diff --git a/src/components/homepage-users.tsx b/src/components/homepage-users.tsx
index 2c3cb656..e23908a6 100644
--- a/src/components/homepage-users.tsx
+++ b/src/components/homepage-users.tsx
@@ -5,11 +5,11 @@ import styles from './homepage-users.module.scss';
import 'react-responsive-carousel/lib/styles/carousel.min.css';
type User = {
- image: string,
- name: string,
- testimonial: string,
- style?: any,
- author?: string,
+ author?: string;
+ image: string;
+ name: string;
+ style?: any;
+ testimonial: string;
};
const users: User[] = [
{
@@ -42,7 +42,7 @@ const users: User[] = [
image: '/img/logo-snaplytics-green.png',
name: 'Snaplytics',
testimonial:
- 'We\'ve been using sequelize since we started in the beginning of 2015. We use it for our graphql servers (in connection with graphql-sequelize), and for all our background workers.',
+ "We've been using sequelize since we started in the beginning of 2015. We use it for our graphql servers (in connection with graphql-sequelize), and for all our background workers.",
style: { paddingTop: '10px' },
},
// {
@@ -59,7 +59,7 @@ const users: User[] = [
},
];
-export default function HomepageUsers(): JSX.Element {
+export function HomepageUsers(): JSX.Element {
const [activeUser, setActiveUser] = useState
(users[0]);
const [width, setWidth] = useState(window.innerWidth);
@@ -100,8 +100,7 @@ export default function HomepageUsers(): JSX.Element {
centerSlidePercentage={isMobile ? 75 : 25}
centerMode
onChange={onChange}
- transitionTime={1000}
- >
+ transitionTime={1000}>
{users.map(user => (
@@ -111,13 +110,9 @@ export default function HomepageUsers(): JSX.Element {
-
- {activeUser.testimonial}
-
+ {activeUser.testimonial}
- {activeUser.author
- ? `${activeUser.author} (${activeUser.name})`
- : activeUser.name}
+ {activeUser.author ? `${activeUser.author} (${activeUser.name})` : activeUser.name}
diff --git a/src/components/support-table.tsx b/src/components/support-table.tsx
index 6572dd85..18906f09 100644
--- a/src/components/support-table.tsx
+++ b/src/components/support-table.tsx
@@ -3,14 +3,15 @@ import { Check, X } from 'react-feather';
import { SUPPORTED_DIALECTS } from '../utils/dialects';
import css from './support-table.module.scss';
-type Props = {
- dialectLinks?: Record
,
-} | {
- features: Record>,
-};
+type Props =
+ | {
+ dialectLinks?: Record;
+ }
+ | {
+ features: Record>;
+ };
export function SupportTable(props: Props) {
-
if ('features' in props) {
const featureNames = Object.keys(props.features);
@@ -20,7 +21,9 @@ export function SupportTable(props: Props) {
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label -- false positive */}
- {Array.from(SUPPORTED_DIALECTS).map(dialect => {dialect} )}
+ {[...SUPPORTED_DIALECTS].map(dialect => (
+ {dialect}
+ ))}
@@ -30,8 +33,8 @@ export function SupportTable(props: Props) {
return (
{featureName}
- {Array.from(SUPPORTED_DIALECTS).map(dialect => {
- const link = featureValue === true ? true : featureValue?.[dialect];
+ {[...SUPPORTED_DIALECTS].map(dialect => {
+ const link = featureValue === true ? true : featureValue[dialect];
return ;
})}
@@ -47,12 +50,14 @@ export function SupportTable(props: Props) {
- {Array.from(SUPPORTED_DIALECTS).map(dialect => {dialect} )}
+ {[...SUPPORTED_DIALECTS].map(dialect => (
+ {dialect}
+ ))}
- {Array.from(SUPPORTED_DIALECTS).map(dialect => {
+ {[...SUPPORTED_DIALECTS].map(dialect => {
const link = props.dialectLinks?.[dialect];
return ;
@@ -65,18 +70,27 @@ export function SupportTable(props: Props) {
function SupportCell(props: { link: string | true | undefined }) {
if (props.link) {
- const supportedIcon = ;
+ const supportedIcon = (
+
+
+
+ );
return (
- {props.link === true ? supportedIcon : (
+ {props.link === true ? (
+ supportedIcon
+ ) : (
{supportedIcon}
[docs]
-
)}
);
@@ -84,7 +98,13 @@ function SupportCell(props: { link: string | true | undefined }) {
return (
-
+
+
+
);
}
diff --git a/src/css/ads.scss b/src/css/ads.scss
index f7efe76c..6fd21238 100644
--- a/src/css/ads.scss
+++ b/src/css/ads.scss
@@ -3,9 +3,8 @@
padding: initial;
}
#carbonads {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
- Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial,
- sans-serif;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
+ 'Helvetica Neue', Helvetica, Arial, sans-serif;
border-radius: var(--ifm-global-radius);
display: flex;
max-width: 300px;
diff --git a/src/hooks/use-ads.tsx b/src/hooks/use-ads.tsx
index 132e7433..9dff827c 100644
--- a/src/hooks/use-ads.tsx
+++ b/src/hooks/use-ads.tsx
@@ -2,15 +2,14 @@ import { useMediaQuery } from '@react-hookz/web'; // cjs
import type { MutableRefObject } from 'react';
import { useEffect } from 'react';
-const SCRIPT_URL
- = '//cdn.carbonads.com/carbon.js?serve=CEAI627Y&placement=sequelizeorg';
+const SCRIPT_URL = '//cdn.carbonads.com/carbon.js?serve=CEAI627Y&placement=sequelizeorg';
type OnEnvironment = 'mobile' | 'desktop' | 'all';
type InitProps = {
- ref?: MutableRefObject,
- selector?: string,
- on?: OnEnvironment,
+ on?: OnEnvironment;
+ ref?: MutableRefObject;
+ selector?: string;
};
function shouldRender(isMobile: boolean, on: OnEnvironment) {
diff --git a/src/models/string.ts b/src/models/string.ts
index cc3d0092..30dcc8d5 100644
--- a/src/models/string.ts
+++ b/src/models/string.ts
@@ -1,13 +1,11 @@
-function getPadding(lines: String[]) {
+function getPadding(lines: string[]) {
const line = lines[0] === '' ? lines[1] : lines[0];
return line.match(/^\s*/)![0].length;
}
-export function trim(_strings: TemplateStringsArray | String) {
- const strings: String[] = (
- Array.isArray(_strings) ? _strings : [_strings]
- ) as String[];
+export function trim(_strings: TemplateStringsArray | string) {
+ const strings: string[] = (Array.isArray(_strings) ? _strings : [_strings]) as string[];
const lines = strings.flatMap(s => s.split('\n'));
const padding = getPadding(lines);
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 3003a3e1..e9505c5b 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -5,9 +5,9 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import clsx from 'clsx';
import React from 'react';
-import HomepageFeatures from '../components/homepage-features';
-import HomepageUsage from '../components/homepage-usage';
-import HomepageUsers from '../components/homepage-users';
+import { HomepageFeatures } from '../components/homepage-features';
+import { HomepageUsage } from '../components/homepage-usage';
+import { HomepageUsers } from '../components/homepage-users';
import { useAds } from '../hooks/use-ads';
import css from './index.module.scss';
@@ -27,17 +27,13 @@ function HomepageHeader() {
{siteConfig.tagline}
-
+
Getting Started
+ to="pathname:///api/v6/identifiers">
API Reference
@@ -45,18 +41,13 @@ function HomepageHeader() {
+ to="/docs/v6/other-topics/upgrade">
Upgrade to v6
+ className={clsx('button button--secondary button--lg', css.supportButton)}
+ to="https://opencollective.com/sequelize">
Support us
@@ -74,9 +65,7 @@ export default function Home(): JSX.Element {
{/* we don't add '| Sequelize' on the homepage title because it already starts with 'Sequelize -' for better search results display */}
-
- Sequelize | Feature-rich ORM for modern TypeScript & JavaScript
-
+ Sequelize | Feature-rich ORM for modern TypeScript & JavaScript
diff --git a/src/pages/releases.mdx b/src/pages/releases.mdx
index 22bf7ca9..104e26df 100644
--- a/src/pages/releases.mdx
+++ b/src/pages/releases.mdx
@@ -10,11 +10,11 @@ This page regroups information related to which engines versions are supported b
## Releases
-| Sequelize | [Node.js][node-releases] | [Typescript][ts-releases] | Release Date | EOL |
-|---------------------------------|----------------------------|---------------------------|--------------|------------|
-| [7 (alpha)][sequelize-core] | >= 18.0.0 | >= 5.0 | ❓ | ❓ |
-| [6 (current)][sequelize-legacy] | >= 10 | >= 4.1 | 2020-06-24 | ❓ |
-| 5 (eol) | >=6 | >= 3.1 | 2019-03-13 | 2022-01-01 |
+| Sequelize | [Node.js][node-releases] | [Typescript][ts-releases] | Release Date | EOL |
+| ------------------------------- | ------------------------ | ------------------------- | ------------ | ---------- |
+| [7 (alpha)][sequelize-core] | >= 18.0.0 | >= 5.0 | ❓ | ❓ |
+| [6 (current)][sequelize-legacy] | >= 10 | >= 4.1 | 2020-06-24 | ❓ |
+| 5 (eol) | >=6 | >= 3.1 | 2019-03-13 | 2022-01-01 |
\* ❓ means the date has not been determined yet.
@@ -36,10 +36,10 @@ If you're trying to use Sequelize 6 in Node 14 or newer, use that version of pg.
:::
-| Sequelize | [PostgreSQL][postgres] | [pg] | [pg-native] |
-|-------------|------------------------|----------------------------------------------|----------------|
-| 7 (alpha) | >= 11 | N/A[^1] | >=3.0.0 |
-| 6 (current) | >= 9.5 | >= 7.8 (node < 14) >= 8.2 (node >= 14) | >=3.0.0 |
+| Sequelize | [PostgreSQL][postgres] | [pg] | [pg-native] |
+| ----------- | ---------------------- | -------------------------------------------- | ----------- |
+| 7 (alpha) | >= 11 | N/A[^1] | >=3.0.0 |
+| 6 (current) | >= 9.5 | >= 7.8 (node < 14) >= 8.2 (node >= 14) | >=3.0.0 |
[postgres]: https://www.postgresql.org/support/versioning/
[pg]: https://www.npmjs.com/package/pg
@@ -51,7 +51,7 @@ In Sequelize 6, MariaDB requires the installation of the [mariadb][mariadb-npm]
[Read more about this here](/docs/v6/other-topics/dialect-specific-things/#mariadb).
| Sequelize | [MariaDB][mariadb] | [mariadb (npm)][mariadb-npm] |
-|-------------|--------------------|------------------------------|
+| ----------- | ------------------ | ---------------------------- |
| 7 (alpha) | >=10.4.30 | N/A[^1] |
| 6 (current) | >=10.3 | ^2.3.3 |
@@ -64,7 +64,7 @@ In Sequelize 6, MySQL requires the installation of the [mysql2] npm package.
[Read more about this here](/docs/v6/other-topics/dialect-specific-things/#mysql).
| Sequelize | [MySQL][mysql] | [mysql2] |
-|-------------|----------------|----------|
+| ----------- | -------------- | -------- |
| 7 (alpha) | >=8.0.19 | N/A[^1] |
| 6 (current) | ^5.7, ^8.0 | >= 2.3.3 |
@@ -77,7 +77,7 @@ In Sequelize 6, MSSQL requires the installation of the [tedious] npm package.
[Read more about this here](/docs/v6/other-topics/dialect-specific-things/#microsoft-sql-server-mssql).
| Sequelize | [SQL Server][mssql] | [tedious] |
-|-------------|---------------------|-----------|
+| ----------- | ------------------- | --------- |
| 7 (alpha) | 2017-2022 | N/A[^1] |
| 6 (current) | 2014-2019 | ^8.3.0 |
@@ -98,7 +98,7 @@ sqlite3@^4 has security vulnerabilities which are fixed by the [@vscode/sqlite3]
:::
| Sequelize | [sqlite3] |
-|-------------|------------------------------------------------|
+| ----------- | ---------------------------------------------- |
| 6 (current) | `@vscode/sqlite3@^4.0.12`, or `sqlite3@^5.0.3` |
[sqlite3]: https://www.npmjs.com/package/sqlite3
@@ -116,8 +116,8 @@ support for Snowflake is limited as it is not handled by the core team.
:::
| Sequelize | [Snowflake](https://www.snowflake.com/pricing/) | [snowflake-sdk] |
-|-------------|-------------------------------------------------|-----------------|
-| 7 (alpha) | all | N/A[^1] |
+| ----------- | ----------------------------------------------- | --------------- |
+| 7 (alpha) | all | N/A[^1] |
| 6 (current) | all | ^1.6.0 |
[snowflake-sdk]: https://www.npmjs.com/package/snowflake-sdk
@@ -127,8 +127,8 @@ support for Snowflake is limited as it is not handled by the core team.
In Sequelize 6, DB2 for Linux, Unix & Windows requires the installation of the [ibm_db] npm package.
| Sequelize | [Db2][db2] | [ibm_db] |
-|-------------|------------|----------|
-| 7 (alpha) | >= 11.5 | N/A[^1] |
+| ----------- | ---------- | -------- |
+| 7 (alpha) | >= 11.5 | N/A[^1] |
| 6 (current) | >= 11.5 | ^2.8.0 |
[db2]: https://www.ibm.com/support/pages/db2-distributed-end-support-eos-dates
@@ -144,7 +144,7 @@ support for DB2 for IBM i is limited as it is not handled by the core team.
:::
| Sequelize | [Db2 for IBM i][ibmi] |
-|-------------|-----------------------|
+| ----------- | --------------------- |
| 7 (alpha) | unknown |
| 6 (current) | not available |
@@ -156,10 +156,10 @@ support for DB2 for IBM i is limited as it is not handled by the core team.
In Sequelize 6, Oracle Database requires the installation of the [node-oracledb] npm package.
[Read more about this here](/docs/v6/other-topics/dialect-specific-things/#oracle-database).
-| Sequelize | [Oracle Database] | [node-oracledb] |
-|-------------|---------------------|-----------------|
-| 7 (alpha) | upcoming | N/A[^1] |
-| 6 (current) | >= 18.4 | ^5.4.0 |
+| Sequelize | [Oracle Database] | [node-oracledb] |
+| ----------- | ----------------- | --------------- |
+| 7 (alpha) | upcoming | N/A[^1] |
+| 6 (current) | >= 18.4 | ^5.4.0 |
[Oracle Database]: https://www.oracle.com/database/technologies/oracle-database-software-downloads.html
[node-oracledb]: https://www.npmjs.com/package/oracledb
diff --git a/src/utils/dialects.ts b/src/utils/dialects.ts
index 11f7ed3d..dc33896a 100644
--- a/src/utils/dialects.ts
+++ b/src/utils/dialects.ts
@@ -1 +1,10 @@
-export const SUPPORTED_DIALECTS = new Set(['PostgreSQL', 'MariaDB', 'MySQL', 'MSSQL', 'SQLite', 'Snowflake', 'db2', 'ibmi']);
+export const SUPPORTED_DIALECTS = new Set([
+ 'PostgreSQL',
+ 'MariaDB',
+ 'MySQL',
+ 'MSSQL',
+ 'SQLite',
+ 'Snowflake',
+ 'db2',
+ 'ibmi',
+]);
diff --git a/src/utils/use-storage.tsx b/src/utils/use-storage.tsx
index c7ac5377..14682f68 100644
--- a/src/utils/use-storage.tsx
+++ b/src/utils/use-storage.tsx
@@ -3,6 +3,7 @@
*/
import { useCallback, useEffect, useRef, useState } from 'react';
+import { isFunction } from '@sequelize/utils';
const eventTargets = new WeakMap();
@@ -25,25 +26,30 @@ function getEventTarget(storage: Storage) {
return eventTarget;
}
-export type TStorageSetValue = (newValue: T | undefined | ((oldValue: T) => T)) => void;
+export type StorageSetValue = (newValue: T | undefined | ((oldValue: T) => T)) => void;
-export type TJsonSerializable = number | boolean | string | null
- | TJsonSerializable[]
- | { [key: string]: TJsonSerializable };
+export type JsonSerializable =
+ | number
+ | boolean
+ | string
+ | null
+ | JsonSerializable[]
+ | { [key: string]: JsonSerializable };
-type TStorageHook = (key: string, defaultValue: T) => [T, TStorageSetValue];
+type StorageHook = (
+ key: string,
+ defaultValue: T,
+) => [T, StorageSetValue];
let lastHookId = 0;
-export function createStorageHook(storage: Storage = new Storage()): TStorageHook {
-
+export function createStorageHook(storage: Storage = new Storage()): StorageHook {
// window.onstorage only triggers cross-realm. This is used to notify other useLocalStorage on the same page that it changed
- return function useStorage(key: string, defaultValue: T): [
- /* get */ T,
- /* set */ TStorageSetValue,
- ] {
-
+ return function useStorage(
+ key: string,
+ defaultValue: T,
+ ): [/* get */ T, /* set */ StorageSetValue] {
const hookIdRef = useRef(null);
if (hookIdRef.current === null) {
hookIdRef.current = lastHookId++;
@@ -72,39 +78,46 @@ export function createStorageHook(storage: Storage = new Storage()): TStorageHoo
const currentValue = useRef(internalValue);
currentValue.current = internalValue;
- const setValue: TStorageSetValue = useCallback((val: T | undefined | ((oldVal: T) => T)) => {
- if (typeof val === 'function') {
- val = val(currentValue.current);
- }
-
- if (currentValue.current === val) {
- return;
- }
-
- // removeItem
- if (val === undefined) {
- currentValue.current = defaultValueRef.current;
- setInternalValue(defaultValueRef.current);
+ const setValue: StorageSetValue = useCallback(
+ (val: T | undefined | ((oldVal: T) => T)) => {
+ if (isFunction(val)) {
+ val = val(currentValue.current);
+ }
- if (storage.getItem(key) == null) {
+ if (currentValue.current === val) {
return;
}
- storage.removeItem(key);
- } else {
- const stringified = JSON.stringify(val);
- currentValue.current = val;
- setInternalValue(val);
+ // removeItem
+ if (val === undefined) {
+ currentValue.current = defaultValueRef.current;
+ setInternalValue(defaultValueRef.current);
- if (stringified === storage.getItem(key)) {
- return;
- }
+ if (storage.getItem(key) == null) {
+ return;
+ }
- storage.setItem(key, stringified);
- }
+ storage.removeItem(key);
+ } else {
+ const stringified = JSON.stringify(val);
+ currentValue.current = val;
+ setInternalValue(val);
- eventTarget.dispatchEvent(new CustomEvent(`uls:storage:${key}`, { detail: { val, sourceHook: hookIdRef.current } }));
- }, [eventTarget, key]);
+ if (stringified === storage.getItem(key)) {
+ return;
+ }
+
+ storage.setItem(key, stringified);
+ }
+
+ eventTarget.dispatchEvent(
+ new CustomEvent(`uls:storage:${key}`, {
+ detail: { val, sourceHook: hookIdRef.current },
+ }),
+ );
+ },
+ [eventTarget, key],
+ );
useEffect(() => {
function crossRealmOnChange(e: StorageEvent) {
@@ -147,16 +160,26 @@ export function createStorageHook(storage: Storage = new Storage()): TStorageHoo
};
}
-function ssrHook(key: string, defaultValue: T): [T, TStorageSetValue] {
- return [defaultValue, () => {
- throw new Error('setState is not supposed to be called server-side.');
- }];
+function ssrHook(
+ key: string,
+ defaultValue: T,
+): [T, StorageSetValue] {
+ return [
+ defaultValue,
+ () => {
+ throw new Error('setState is not supposed to be called server-side.');
+ },
+ ];
}
-export const useLocalStorage: TStorageHook = typeof window !== 'undefined' && typeof localStorage !== 'undefined'
- ? createStorageHook(localStorage)
- : ssrHook;
-
-export const useSessionStorage: TStorageHook = typeof window !== 'undefined' && typeof localStorage !== 'undefined'
-? createStorageHook(sessionStorage)
-: ssrHook;
+export const useLocalStorage: StorageHook =
+ // eslint-disable-next-line no-restricted-syntax -- checking that the variable exists is an acceptable exception
+ typeof window !== 'undefined' && typeof localStorage !== 'undefined'
+ ? createStorageHook(localStorage)
+ : ssrHook;
+
+export const useSessionStorage: StorageHook =
+ // eslint-disable-next-line no-restricted-syntax -- checking that the variable exists is an acceptable exception
+ typeof window !== 'undefined' && typeof localStorage !== 'undefined'
+ ? createStorageHook(sessionStorage)
+ : ssrHook;
diff --git a/tsconfig.json b/tsconfig.json
index 2912937e..402d0cd7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,6 +4,8 @@
"compilerOptions": {
"strict": true,
"jsx": "react",
- "baseUrl": "."
+ "baseUrl": ".",
+ "target": "es2022",
+ "lib": ["ES2023", "DOM.Iterable", "DOM"]
}
}
diff --git a/versioned_docs/version-6.x.x/advanced-association-concepts/advanced-many-to-many.md b/versioned_docs/version-6.x.x/advanced-association-concepts/advanced-many-to-many.md
index f497b2e7..0c583551 100644
--- a/versioned_docs/version-6.x.x/advanced-association-concepts/advanced-many-to-many.md
+++ b/versioned_docs/version-6.x.x/advanced-association-concepts/advanced-many-to-many.md
@@ -1,670 +1,708 @@
----
-sidebar_position: 3
-title: Advanced M:N Associations
----
-
-Make sure you have read the [associations guide](../core-concepts/assocs.md) before reading this guide.
-
-Let's start with an example of a Many-to-Many relationship between `User` and `Profile`.
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- points: DataTypes.INTEGER
-}, { timestamps: false });
-const Profile = sequelize.define('profile', {
- name: DataTypes.STRING
-}, { timestamps: false });
-```
-
-The simplest way to define the Many-to-Many relationship is:
-
-```js
-User.belongsToMany(Profile, { through: 'User_Profiles' });
-Profile.belongsToMany(User, { through: 'User_Profiles' });
-```
-
-By passing a string to `through` above, we are asking Sequelize to automatically generate a model named `User_Profiles` as the *through table* (also known as junction table), with only two columns: `userId` and `profileId`. A composite unique key will be established on these two columns.
-
-We can also define ourselves a model to be used as the through table.
-
-```js
-const User_Profile = sequelize.define('User_Profile', {}, { timestamps: false });
-User.belongsToMany(Profile, { through: User_Profile });
-Profile.belongsToMany(User, { through: User_Profile });
-```
-
-The above has the exact same effect. Note that we didn't define any attributes on the `User_Profile` model. The fact that we passed it into a `belongsToMany` call tells sequelize to create the two attributes `userId` and `profileId` automatically, just like other associations also cause Sequelize to automatically add a column to one of the involved models.
-
-However, defining the model by ourselves has several advantages. We can, for example, define more columns on our through table:
-
-```js
-const User_Profile = sequelize.define('User_Profile', {
- selfGranted: DataTypes.BOOLEAN
-}, { timestamps: false });
-User.belongsToMany(Profile, { through: User_Profile });
-Profile.belongsToMany(User, { through: User_Profile });
-```
-
-With this, we can now track an extra information at the through table, namely the `selfGranted` boolean. For example, when calling the `user.addProfile()` we can pass values for the extra columns using the `through` option.
-
-Example:
-
-```js
-const amidala = await User.create({ username: 'p4dm3', points: 1000 });
-const queen = await Profile.create({ name: 'Queen' });
-await amidala.addProfile(queen, { through: { selfGranted: false } });
-const result = await User.findOne({
- where: { username: 'p4dm3' },
- include: Profile
-});
-console.log(result);
-```
-
-Output:
-
-```json
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen",
- "User_Profile": {
- "userId": 4,
- "profileId": 6,
- "selfGranted": false
- }
- }
- ]
-}
-```
-
-You can create all relationship in single `create` call too.
-
-Example:
-
-```js
-const amidala = await User.create({
- username: 'p4dm3',
- points: 1000,
- profiles: [{
- name: 'Queen',
- User_Profile: {
- selfGranted: true
- }
- }]
-}, {
- include: Profile
-});
-
-const result = await User.findOne({
- where: { username: 'p4dm3' },
- include: Profile
-});
-
-console.log(result);
-```
-
-Output:
-
-```json
-{
- "id": 1,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 1,
- "name": "Queen",
- "User_Profile": {
- "selfGranted": true,
- "userId": 1,
- "profileId": 1
- }
- }
- ]
-}
-```
-
-You probably noticed that the `User_Profiles` table does not have an `id` field. As mentioned above, it has a composite unique key instead. The name of this composite unique key is chosen automatically by Sequelize but can be customized with the `uniqueKey` option:
-
-```js
-User.belongsToMany(Profile, { through: User_Profiles, uniqueKey: 'my_custom_unique' });
-```
-
-Another possibility, if desired, is to force the through table to have a primary key just like other standard tables. To do this, simply define the primary key in the model:
-
-```js
-const User_Profile = sequelize.define('User_Profile', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- },
- selfGranted: DataTypes.BOOLEAN
-}, { timestamps: false });
-User.belongsToMany(Profile, { through: User_Profile });
-Profile.belongsToMany(User, { through: User_Profile });
-```
-
-The above will still create two columns `userId` and `profileId`, of course, but instead of setting up a composite unique key on them, the model will use its `id` column as primary key. Everything else will still work just fine.
-
-## Through tables versus normal tables and the "Super Many-to-Many association"
-
-Now we will compare the usage of the last Many-to-Many setup shown above with the usual One-to-Many relationships, so that in the end we conclude with the concept of a *"Super Many-to-Many relationship"*.
-
-### Models recap (with minor rename)
-
-To make things easier to follow, let's rename our `User_Profile` model to `grant`. Note that everything works in the same way as before. Our models are:
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- points: DataTypes.INTEGER
-}, { timestamps: false });
-
-const Profile = sequelize.define('profile', {
- name: DataTypes.STRING
-}, { timestamps: false });
-
-const Grant = sequelize.define('grant', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- },
- selfGranted: DataTypes.BOOLEAN
-}, { timestamps: false });
-```
-
-We established a Many-to-Many relationship between `User` and `Profile` using the `Grant` model as the through table:
-
-```js
-User.belongsToMany(Profile, { through: Grant });
-Profile.belongsToMany(User, { through: Grant });
-```
-
-This automatically added the columns `userId` and `profileId` to the `Grant` model.
-
-**Note:** As shown above, we have chosen to force the `grant` model to have a single primary key (called `id`, as usual). This is necessary for the *Super Many-to-Many relationship* that will be defined soon.
-
-### Using One-to-Many relationships instead
-
-Instead of setting up the Many-to-Many relationship defined above, what if we did the following instead?
-
-```js
-// Setup a One-to-Many relationship between User and Grant
-User.hasMany(Grant);
-Grant.belongsTo(User);
-
-// Also setup a One-to-Many relationship between Profile and Grant
-Profile.hasMany(Grant);
-Grant.belongsTo(Profile);
-```
-
-The result is essentially the same! This is because `User.hasMany(Grant)` and `Profile.hasMany(Grant)` will automatically add the `userId` and `profileId` columns to `Grant`, respectively.
-
-This shows that one Many-to-Many relationship isn't very different from two One-to-Many relationships. The tables in the database look the same.
-
-The only difference is when you try to perform an eager load with Sequelize.
-
-```js
-// With the Many-to-Many approach, you can do:
-User.findAll({ include: Profile });
-Profile.findAll({ include: User });
-// However, you can't do:
-User.findAll({ include: Grant });
-Profile.findAll({ include: Grant });
-Grant.findAll({ include: User });
-Grant.findAll({ include: Profile });
-
-// On the other hand, with the double One-to-Many approach, you can do:
-User.findAll({ include: Grant });
-Profile.findAll({ include: Grant });
-Grant.findAll({ include: User });
-Grant.findAll({ include: Profile });
-// However, you can't do:
-User.findAll({ include: Profile });
-Profile.findAll({ include: User });
-// Although you can emulate those with nested includes, as follows:
-User.findAll({
- include: {
- model: Grant,
- include: Profile
- }
-}); // This emulates the `User.findAll({ include: Profile })`, however
- // the resulting object structure is a bit different. The original
- // structure has the form `user.profiles[].grant`, while the emulated
- // structure has the form `user.grants[].profiles[]`.
-```
-
-### The best of both worlds: the Super Many-to-Many relationship
-
-We can simply combine both approaches shown above!
-
-```js
-// The Super Many-to-Many relationship
-User.belongsToMany(Profile, { through: Grant });
-Profile.belongsToMany(User, { through: Grant });
-User.hasMany(Grant);
-Grant.belongsTo(User);
-Profile.hasMany(Grant);
-Grant.belongsTo(Profile);
-```
-
-This way, we can do all kinds of eager loading:
-
-```js
-// All these work:
-User.findAll({ include: Profile });
-Profile.findAll({ include: User });
-User.findAll({ include: Grant });
-Profile.findAll({ include: Grant });
-Grant.findAll({ include: User });
-Grant.findAll({ include: Profile });
-```
-
-We can even perform all kinds of deeply nested includes:
-
-```js
-User.findAll({
- include: [
- {
- model: Grant,
- include: [User, Profile]
- },
- {
- model: Profile,
- include: {
- model: User,
- include: {
- model: Grant,
- include: [User, Profile]
- }
- }
- }
- ]
-});
-```
-
-## Aliases and custom key names
-
-Similarly to the other relationships, aliases can be defined for Many-to-Many relationships.
-
-Before proceeding, please recall [the aliasing example for `belongsTo`](../core-concepts/assocs.md#defining-an-alias) on the [associations guide](../core-concepts/assocs.md). Note that, in that case, defining an association impacts both the way includes are done (i.e. passing the association name) and the name Sequelize chooses for the foreign key (in that example, `leaderId` was created on the `Ship` model).
-
-Defining an alias for a `belongsToMany` association also impacts the way includes are performed:
-
-```js
-Product.belongsToMany(Category, { as: 'groups', through: 'product_categories' });
-Category.belongsToMany(Product, { as: 'items', through: 'product_categories' });
-
-// [...]
-
-await Product.findAll({ include: Category }); // This doesn't work
-
-await Product.findAll({ // This works, passing the alias
- include: {
- model: Category,
- as: 'groups'
- }
-});
-
-await Product.findAll({ include: 'groups' }); // This also works
-```
-
-However, defining an alias here has nothing to do with the foreign key names. The names of both foreign keys created in the through table are still constructed by Sequelize based on the name of the models being associated. This can readily be seen by inspecting the generated SQL for the through table in the example above:
-
-```sql
-CREATE TABLE IF NOT EXISTS `product_categories` (
- `createdAt` DATETIME NOT NULL,
- `updatedAt` DATETIME NOT NULL,
- `productId` INTEGER NOT NULL REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- `categoryId` INTEGER NOT NULL REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- PRIMARY KEY (`productId`, `categoryId`)
-);
-```
-
-We can see that the foreign keys are `productId` and `categoryId`. To change these names, Sequelize accepts the options `foreignKey` and `otherKey` respectively (i.e., the `foreignKey` defines the key for the source model in the through relation, and `otherKey` defines it for the target model):
-
-```js
-Product.belongsToMany(Category, {
- through: 'product_categories',
- foreignKey: 'objectId', // replaces `productId`
- otherKey: 'typeId' // replaces `categoryId`
-});
-Category.belongsToMany(Product, {
- through: 'product_categories',
- foreignKey: 'typeId', // replaces `categoryId`
- otherKey: 'objectId' // replaces `productId`
-});
-```
-
-Generated SQL:
-
-```sql
-CREATE TABLE IF NOT EXISTS `product_categories` (
- `createdAt` DATETIME NOT NULL,
- `updatedAt` DATETIME NOT NULL,
- `objectId` INTEGER NOT NULL REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- `typeId` INTEGER NOT NULL REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- PRIMARY KEY (`objectId`, `typeId`)
-);
-```
-
-As shown above, when you define a Many-to-Many relationship with two `belongsToMany` calls (which is the standard way), you should provide the `foreignKey` and `otherKey` options appropriately in both calls. If you pass these options in only one of the calls, the Sequelize behavior will be unreliable.
-
-## Self-references
-
-Sequelize supports self-referential Many-to-Many relationships, intuitively:
-
-```js
-Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' })
-// This will create the table PersonChildren which stores the ids of the objects.
-```
-
-## Specifying attributes from the through table
-
-By default, when eager loading a many-to-many relationship, Sequelize will return data in the following structure (based on the first example in this guide):
-
-```json
-// User.findOne({ include: Profile })
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen",
- "grant": {
- "userId": 4,
- "profileId": 6,
- "selfGranted": false
- }
- }
- ]
-}
-```
-
-Notice that the outer object is an `User`, which has a field called `profiles`, which is a `Profile` array, such that each `Profile` comes with an extra field called `grant` which is a `Grant` instance. This is the default structure created by Sequelize when eager loading from a Many-to-Many relationship.
-
-However, if you want only some of the attributes of the through table, you can provide an array with the attributes you want in the `attributes` option. For example, if you only want the `selfGranted` attribute from the through table:
-
-```js
-User.findOne({
- include: {
- model: Profile,
- through: {
- attributes: ['selfGranted']
- }
- }
-});
-```
-
-Output:
-
-```json
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen",
- "grant": {
- "selfGranted": false
- }
- }
- ]
-}
-```
-
-If you don't want the nested `grant` field at all, use `attributes: []`:
-
-```js
-User.findOne({
- include: {
- model: Profile,
- through: {
- attributes: []
- }
- }
-});
-```
-
-Output:
-
-```json
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen"
- }
- ]
-}
-```
-
-If you are using mixins (such as `user.getProfiles()`) instead of finder methods (such as `User.findAll()`), you have to use the `joinTableAttributes` option instead:
-
-```js
-someUser.getProfiles({ joinTableAttributes: ['selfGranted'] });
-```
-
-Output:
-
-```json
-[
- {
- "id": 6,
- "name": "queen",
- "grant": {
- "selfGranted": false
- }
- }
-]
-```
-
-## Many-to-many-to-many relationships and beyond
-
-Consider you are trying to model a game championship. There are players and teams. Teams play games. However, players can change teams in the middle of the championship (but not in the middle of a game). So, given one specific game, there are certain teams participating in that game, and each of these teams has a set of players (for that game).
-
-So we start by defining the three relevant models:
-
-```js
-const Player = sequelize.define('Player', { username: DataTypes.STRING });
-const Team = sequelize.define('Team', { name: DataTypes.STRING });
-const Game = sequelize.define('Game', { name: DataTypes.STRING });
-```
-
-Now, the question is: how to associate them?
-
-First, we note that:
-
-* One game has many teams associated to it (the ones that are playing that game);
-* One team may have participated in many games.
-
-The above observations show that we need a Many-to-Many relationship between Game and Team. Let's use the Super Many-to-Many relationship as explained earlier in this guide:
-
-```js
-// Super Many-to-Many relationship between Game and Team
-const GameTeam = sequelize.define('GameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Team.belongsToMany(Game, { through: GameTeam });
-Game.belongsToMany(Team, { through: GameTeam });
-GameTeam.belongsTo(Game);
-GameTeam.belongsTo(Team);
-Game.hasMany(GameTeam);
-Team.hasMany(GameTeam);
-```
-
-The part about players is trickier. We note that the set of players that form a team depends not only on the team (obviously), but also on which game is being considered. Therefore, we don't want a Many-to-Many relationship between Player and Team. We also don't want a Many-to-Many relationship between Player and Game. Instead of associating a Player to any of those models, what we need is an association between a Player and something like a *"team-game pair constraint"*, since it is the pair (team plus game) that defines which players belong there. So what we are looking for turns out to be precisely the junction model, GameTeam, itself! And, we note that, since a given *game-team pair* specifies many players, and on the other hand that the same player can participate of many *game-team pairs*, we need a Many-to-Many relationship between Player and GameTeam!
-
-To provide the greatest flexibility, let's use the Super Many-to-Many relationship construction here again:
-
-```js
-// Super Many-to-Many relationship between Player and GameTeam
-const PlayerGameTeam = sequelize.define('PlayerGameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Player.belongsToMany(GameTeam, { through: PlayerGameTeam });
-GameTeam.belongsToMany(Player, { through: PlayerGameTeam });
-PlayerGameTeam.belongsTo(Player);
-PlayerGameTeam.belongsTo(GameTeam);
-Player.hasMany(PlayerGameTeam);
-GameTeam.hasMany(PlayerGameTeam);
-```
-
-The above associations achieve precisely what we want. Here is a full runnable example of this:
-
-```js
-const { Sequelize, Op, Model, DataTypes } = require('sequelize');
-const sequelize = new Sequelize('sqlite::memory:', {
- define: { timestamps: false } // Just for less clutter in this example
-});
-const Player = sequelize.define('Player', { username: DataTypes.STRING });
-const Team = sequelize.define('Team', { name: DataTypes.STRING });
-const Game = sequelize.define('Game', { name: DataTypes.STRING });
-
-// We apply a Super Many-to-Many relationship between Game and Team
-const GameTeam = sequelize.define('GameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Team.belongsToMany(Game, { through: GameTeam });
-Game.belongsToMany(Team, { through: GameTeam });
-GameTeam.belongsTo(Game);
-GameTeam.belongsTo(Team);
-Game.hasMany(GameTeam);
-Team.hasMany(GameTeam);
-
-// We apply a Super Many-to-Many relationship between Player and GameTeam
-const PlayerGameTeam = sequelize.define('PlayerGameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Player.belongsToMany(GameTeam, { through: PlayerGameTeam });
-GameTeam.belongsToMany(Player, { through: PlayerGameTeam });
-PlayerGameTeam.belongsTo(Player);
-PlayerGameTeam.belongsTo(GameTeam);
-Player.hasMany(PlayerGameTeam);
-GameTeam.hasMany(PlayerGameTeam);
-
-(async () => {
-
- await sequelize.sync();
- await Player.bulkCreate([
- { username: 's0me0ne' },
- { username: 'empty' },
- { username: 'greenhead' },
- { username: 'not_spock' },
- { username: 'bowl_of_petunias' }
- ]);
- await Game.bulkCreate([
- { name: 'The Big Clash' },
- { name: 'Winter Showdown' },
- { name: 'Summer Beatdown' }
- ]);
- await Team.bulkCreate([
- { name: 'The Martians' },
- { name: 'The Earthlings' },
- { name: 'The Plutonians' }
- ]);
-
- // Let's start defining which teams were in which games. This can be done
- // in several ways, such as calling `.setTeams` on each game. However, for
- // brevity, we will use direct `create` calls instead, referring directly
- // to the IDs we want. We know that IDs are given in order starting from 1.
- await GameTeam.bulkCreate([
- { GameId: 1, TeamId: 1 }, // this GameTeam will get id 1
- { GameId: 1, TeamId: 2 }, // this GameTeam will get id 2
- { GameId: 2, TeamId: 1 }, // this GameTeam will get id 3
- { GameId: 2, TeamId: 3 }, // this GameTeam will get id 4
- { GameId: 3, TeamId: 2 }, // this GameTeam will get id 5
- { GameId: 3, TeamId: 3 } // this GameTeam will get id 6
- ]);
-
- // Now let's specify players.
- // For brevity, let's do it only for the second game (Winter Showdown).
- // Let's say that that s0me0ne and greenhead played for The Martians, while
- // not_spock and bowl_of_petunias played for The Plutonians:
- await PlayerGameTeam.bulkCreate([
- // In 'Winter Showdown' (i.e. GameTeamIds 3 and 4):
- { PlayerId: 1, GameTeamId: 3 }, // s0me0ne played for The Martians
- { PlayerId: 3, GameTeamId: 3 }, // greenhead played for The Martians
- { PlayerId: 4, GameTeamId: 4 }, // not_spock played for The Plutonians
- { PlayerId: 5, GameTeamId: 4 } // bowl_of_petunias played for The Plutonians
- ]);
-
- // Now we can make queries!
- const game = await Game.findOne({
- where: {
- name: "Winter Showdown"
- },
- include: {
- model: GameTeam,
- include: [
- {
- model: Player,
- through: { attributes: [] } // Hide unwanted `PlayerGameTeam` nested object from results
- },
- Team
- ]
- }
- });
-
- console.log(`Found game: "${game.name}"`);
- for (let i = 0; i < game.GameTeams.length; i++) {
- const team = game.GameTeams[i].Team;
- const players = game.GameTeams[i].Players;
- console.log(`- Team "${team.name}" played game "${game.name}" with the following players:`);
- console.log(players.map(p => `--- ${p.username}`).join('\n'));
- }
-
-})();
-```
-
-Output:
-
-```text
-Found game: "Winter Showdown"
-- Team "The Martians" played game "Winter Showdown" with the following players:
---- s0me0ne
---- greenhead
-- Team "The Plutonians" played game "Winter Showdown" with the following players:
---- not_spock
---- bowl_of_petunias
-```
-
-So this is how we can achieve a *many-to-many-to-many* relationship between three models in Sequelize, by taking advantage of the Super Many-to-Many relationship technique!
-
-This idea can be applied recursively for even more complex, *many-to-many-to-...-to-many* relationships (although at some point queries might become slow).
+---
+sidebar_position: 3
+title: Advanced M:N Associations
+---
+
+Make sure you have read the [associations guide](../core-concepts/assocs.md) before reading this guide.
+
+Let's start with an example of a Many-to-Many relationship between `User` and `Profile`.
+
+```js
+const User = sequelize.define(
+ 'user',
+ {
+ username: DataTypes.STRING,
+ points: DataTypes.INTEGER,
+ },
+ { timestamps: false },
+);
+const Profile = sequelize.define(
+ 'profile',
+ {
+ name: DataTypes.STRING,
+ },
+ { timestamps: false },
+);
+```
+
+The simplest way to define the Many-to-Many relationship is:
+
+```js
+User.belongsToMany(Profile, { through: 'User_Profiles' });
+Profile.belongsToMany(User, { through: 'User_Profiles' });
+```
+
+By passing a string to `through` above, we are asking Sequelize to automatically generate a model named `User_Profiles` as the _through table_ (also known as junction table), with only two columns: `userId` and `profileId`. A composite unique key will be established on these two columns.
+
+We can also define ourselves a model to be used as the through table.
+
+```js
+const User_Profile = sequelize.define('User_Profile', {}, { timestamps: false });
+User.belongsToMany(Profile, { through: User_Profile });
+Profile.belongsToMany(User, { through: User_Profile });
+```
+
+The above has the exact same effect. Note that we didn't define any attributes on the `User_Profile` model. The fact that we passed it into a `belongsToMany` call tells sequelize to create the two attributes `userId` and `profileId` automatically, just like other associations also cause Sequelize to automatically add a column to one of the involved models.
+
+However, defining the model by ourselves has several advantages. We can, for example, define more columns on our through table:
+
+```js
+const User_Profile = sequelize.define(
+ 'User_Profile',
+ {
+ selfGranted: DataTypes.BOOLEAN,
+ },
+ { timestamps: false },
+);
+User.belongsToMany(Profile, { through: User_Profile });
+Profile.belongsToMany(User, { through: User_Profile });
+```
+
+With this, we can now track an extra information at the through table, namely the `selfGranted` boolean. For example, when calling the `user.addProfile()` we can pass values for the extra columns using the `through` option.
+
+Example:
+
+```js
+const amidala = await User.create({ username: 'p4dm3', points: 1000 });
+const queen = await Profile.create({ name: 'Queen' });
+await amidala.addProfile(queen, { through: { selfGranted: false } });
+const result = await User.findOne({
+ where: { username: 'p4dm3' },
+ include: Profile,
+});
+console.log(result);
+```
+
+Output:
+
+```json
+{
+ "id": 4,
+ "username": "p4dm3",
+ "points": 1000,
+ "profiles": [
+ {
+ "id": 6,
+ "name": "queen",
+ "User_Profile": {
+ "userId": 4,
+ "profileId": 6,
+ "selfGranted": false
+ }
+ }
+ ]
+}
+```
+
+You can create all relationship in single `create` call too.
+
+Example:
+
+```js
+const amidala = await User.create(
+ {
+ username: 'p4dm3',
+ points: 1000,
+ profiles: [
+ {
+ name: 'Queen',
+ User_Profile: {
+ selfGranted: true,
+ },
+ },
+ ],
+ },
+ {
+ include: Profile,
+ },
+);
+
+const result = await User.findOne({
+ where: { username: 'p4dm3' },
+ include: Profile,
+});
+
+console.log(result);
+```
+
+Output:
+
+```json
+{
+ "id": 1,
+ "username": "p4dm3",
+ "points": 1000,
+ "profiles": [
+ {
+ "id": 1,
+ "name": "Queen",
+ "User_Profile": {
+ "selfGranted": true,
+ "userId": 1,
+ "profileId": 1
+ }
+ }
+ ]
+}
+```
+
+You probably noticed that the `User_Profiles` table does not have an `id` field. As mentioned above, it has a composite unique key instead. The name of this composite unique key is chosen automatically by Sequelize but can be customized with the `uniqueKey` option:
+
+```js
+User.belongsToMany(Profile, {
+ through: User_Profiles,
+ uniqueKey: 'my_custom_unique',
+});
+```
+
+Another possibility, if desired, is to force the through table to have a primary key just like other standard tables. To do this, simply define the primary key in the model:
+
+```js
+const User_Profile = sequelize.define(
+ 'User_Profile',
+ {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ allowNull: false,
+ },
+ selfGranted: DataTypes.BOOLEAN,
+ },
+ { timestamps: false },
+);
+User.belongsToMany(Profile, { through: User_Profile });
+Profile.belongsToMany(User, { through: User_Profile });
+```
+
+The above will still create two columns `userId` and `profileId`, of course, but instead of setting up a composite unique key on them, the model will use its `id` column as primary key. Everything else will still work just fine.
+
+## Through tables versus normal tables and the "Super Many-to-Many association"
+
+Now we will compare the usage of the last Many-to-Many setup shown above with the usual One-to-Many relationships, so that in the end we conclude with the concept of a _"Super Many-to-Many relationship"_.
+
+### Models recap (with minor rename)
+
+To make things easier to follow, let's rename our `User_Profile` model to `grant`. Note that everything works in the same way as before. Our models are:
+
+```js
+const User = sequelize.define(
+ 'user',
+ {
+ username: DataTypes.STRING,
+ points: DataTypes.INTEGER,
+ },
+ { timestamps: false },
+);
+
+const Profile = sequelize.define(
+ 'profile',
+ {
+ name: DataTypes.STRING,
+ },
+ { timestamps: false },
+);
+
+const Grant = sequelize.define(
+ 'grant',
+ {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ allowNull: false,
+ },
+ selfGranted: DataTypes.BOOLEAN,
+ },
+ { timestamps: false },
+);
+```
+
+We established a Many-to-Many relationship between `User` and `Profile` using the `Grant` model as the through table:
+
+```js
+User.belongsToMany(Profile, { through: Grant });
+Profile.belongsToMany(User, { through: Grant });
+```
+
+This automatically added the columns `userId` and `profileId` to the `Grant` model.
+
+**Note:** As shown above, we have chosen to force the `grant` model to have a single primary key (called `id`, as usual). This is necessary for the _Super Many-to-Many relationship_ that will be defined soon.
+
+### Using One-to-Many relationships instead
+
+Instead of setting up the Many-to-Many relationship defined above, what if we did the following instead?
+
+```js
+// Setup a One-to-Many relationship between User and Grant
+User.hasMany(Grant);
+Grant.belongsTo(User);
+
+// Also setup a One-to-Many relationship between Profile and Grant
+Profile.hasMany(Grant);
+Grant.belongsTo(Profile);
+```
+
+The result is essentially the same! This is because `User.hasMany(Grant)` and `Profile.hasMany(Grant)` will automatically add the `userId` and `profileId` columns to `Grant`, respectively.
+
+This shows that one Many-to-Many relationship isn't very different from two One-to-Many relationships. The tables in the database look the same.
+
+The only difference is when you try to perform an eager load with Sequelize.
+
+```js
+// With the Many-to-Many approach, you can do:
+User.findAll({ include: Profile });
+Profile.findAll({ include: User });
+// However, you can't do:
+User.findAll({ include: Grant });
+Profile.findAll({ include: Grant });
+Grant.findAll({ include: User });
+Grant.findAll({ include: Profile });
+
+// On the other hand, with the double One-to-Many approach, you can do:
+User.findAll({ include: Grant });
+Profile.findAll({ include: Grant });
+Grant.findAll({ include: User });
+Grant.findAll({ include: Profile });
+// However, you can't do:
+User.findAll({ include: Profile });
+Profile.findAll({ include: User });
+// Although you can emulate those with nested includes, as follows:
+User.findAll({
+ include: {
+ model: Grant,
+ include: Profile,
+ },
+}); // This emulates the `User.findAll({ include: Profile })`, however
+// the resulting object structure is a bit different. The original
+// structure has the form `user.profiles[].grant`, while the emulated
+// structure has the form `user.grants[].profiles[]`.
+```
+
+### The best of both worlds: the Super Many-to-Many relationship
+
+We can simply combine both approaches shown above!
+
+```js
+// The Super Many-to-Many relationship
+User.belongsToMany(Profile, { through: Grant });
+Profile.belongsToMany(User, { through: Grant });
+User.hasMany(Grant);
+Grant.belongsTo(User);
+Profile.hasMany(Grant);
+Grant.belongsTo(Profile);
+```
+
+This way, we can do all kinds of eager loading:
+
+```js
+// All these work:
+User.findAll({ include: Profile });
+Profile.findAll({ include: User });
+User.findAll({ include: Grant });
+Profile.findAll({ include: Grant });
+Grant.findAll({ include: User });
+Grant.findAll({ include: Profile });
+```
+
+We can even perform all kinds of deeply nested includes:
+
+```js
+User.findAll({
+ include: [
+ {
+ model: Grant,
+ include: [User, Profile],
+ },
+ {
+ model: Profile,
+ include: {
+ model: User,
+ include: {
+ model: Grant,
+ include: [User, Profile],
+ },
+ },
+ },
+ ],
+});
+```
+
+## Aliases and custom key names
+
+Similarly to the other relationships, aliases can be defined for Many-to-Many relationships.
+
+Before proceeding, please recall [the aliasing example for `belongsTo`](../core-concepts/assocs.md#defining-an-alias) on the [associations guide](../core-concepts/assocs.md). Note that, in that case, defining an association impacts both the way includes are done (i.e. passing the association name) and the name Sequelize chooses for the foreign key (in that example, `leaderId` was created on the `Ship` model).
+
+Defining an alias for a `belongsToMany` association also impacts the way includes are performed:
+
+```js
+Product.belongsToMany(Category, {
+ as: 'groups',
+ through: 'product_categories',
+});
+Category.belongsToMany(Product, { as: 'items', through: 'product_categories' });
+
+// [...]
+
+await Product.findAll({ include: Category }); // This doesn't work
+
+await Product.findAll({
+ // This works, passing the alias
+ include: {
+ model: Category,
+ as: 'groups',
+ },
+});
+
+await Product.findAll({ include: 'groups' }); // This also works
+```
+
+However, defining an alias here has nothing to do with the foreign key names. The names of both foreign keys created in the through table are still constructed by Sequelize based on the name of the models being associated. This can readily be seen by inspecting the generated SQL for the through table in the example above:
+
+```sql
+CREATE TABLE IF NOT EXISTS `product_categories` (
+ `createdAt` DATETIME NOT NULL,
+ `updatedAt` DATETIME NOT NULL,
+ `productId` INTEGER NOT NULL REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ `categoryId` INTEGER NOT NULL REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ PRIMARY KEY (`productId`, `categoryId`)
+);
+```
+
+We can see that the foreign keys are `productId` and `categoryId`. To change these names, Sequelize accepts the options `foreignKey` and `otherKey` respectively (i.e., the `foreignKey` defines the key for the source model in the through relation, and `otherKey` defines it for the target model):
+
+```js
+Product.belongsToMany(Category, {
+ through: 'product_categories',
+ foreignKey: 'objectId', // replaces `productId`
+ otherKey: 'typeId', // replaces `categoryId`
+});
+Category.belongsToMany(Product, {
+ through: 'product_categories',
+ foreignKey: 'typeId', // replaces `categoryId`
+ otherKey: 'objectId', // replaces `productId`
+});
+```
+
+Generated SQL:
+
+```sql
+CREATE TABLE IF NOT EXISTS `product_categories` (
+ `createdAt` DATETIME NOT NULL,
+ `updatedAt` DATETIME NOT NULL,
+ `objectId` INTEGER NOT NULL REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ `typeId` INTEGER NOT NULL REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ PRIMARY KEY (`objectId`, `typeId`)
+);
+```
+
+As shown above, when you define a Many-to-Many relationship with two `belongsToMany` calls (which is the standard way), you should provide the `foreignKey` and `otherKey` options appropriately in both calls. If you pass these options in only one of the calls, the Sequelize behavior will be unreliable.
+
+## Self-references
+
+Sequelize supports self-referential Many-to-Many relationships, intuitively:
+
+```js
+Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' });
+// This will create the table PersonChildren which stores the ids of the objects.
+```
+
+## Specifying attributes from the through table
+
+By default, when eager loading a many-to-many relationship, Sequelize will return data in the following structure (based on the first example in this guide):
+
+```json
+// User.findOne({ include: Profile })
+{
+ "id": 4,
+ "username": "p4dm3",
+ "points": 1000,
+ "profiles": [
+ {
+ "id": 6,
+ "name": "queen",
+ "grant": {
+ "userId": 4,
+ "profileId": 6,
+ "selfGranted": false
+ }
+ }
+ ]
+}
+```
+
+Notice that the outer object is an `User`, which has a field called `profiles`, which is a `Profile` array, such that each `Profile` comes with an extra field called `grant` which is a `Grant` instance. This is the default structure created by Sequelize when eager loading from a Many-to-Many relationship.
+
+However, if you want only some of the attributes of the through table, you can provide an array with the attributes you want in the `attributes` option. For example, if you only want the `selfGranted` attribute from the through table:
+
+```js
+User.findOne({
+ include: {
+ model: Profile,
+ through: {
+ attributes: ['selfGranted'],
+ },
+ },
+});
+```
+
+Output:
+
+```json
+{
+ "id": 4,
+ "username": "p4dm3",
+ "points": 1000,
+ "profiles": [
+ {
+ "id": 6,
+ "name": "queen",
+ "grant": {
+ "selfGranted": false
+ }
+ }
+ ]
+}
+```
+
+If you don't want the nested `grant` field at all, use `attributes: []`:
+
+```js
+User.findOne({
+ include: {
+ model: Profile,
+ through: {
+ attributes: [],
+ },
+ },
+});
+```
+
+Output:
+
+```json
+{
+ "id": 4,
+ "username": "p4dm3",
+ "points": 1000,
+ "profiles": [
+ {
+ "id": 6,
+ "name": "queen"
+ }
+ ]
+}
+```
+
+If you are using mixins (such as `user.getProfiles()`) instead of finder methods (such as `User.findAll()`), you have to use the `joinTableAttributes` option instead:
+
+```js
+someUser.getProfiles({ joinTableAttributes: ['selfGranted'] });
+```
+
+Output:
+
+```json
+[
+ {
+ "id": 6,
+ "name": "queen",
+ "grant": {
+ "selfGranted": false
+ }
+ }
+]
+```
+
+## Many-to-many-to-many relationships and beyond
+
+Consider you are trying to model a game championship. There are players and teams. Teams play games. However, players can change teams in the middle of the championship (but not in the middle of a game). So, given one specific game, there are certain teams participating in that game, and each of these teams has a set of players (for that game).
+
+So we start by defining the three relevant models:
+
+```js
+const Player = sequelize.define('Player', { username: DataTypes.STRING });
+const Team = sequelize.define('Team', { name: DataTypes.STRING });
+const Game = sequelize.define('Game', { name: DataTypes.STRING });
+```
+
+Now, the question is: how to associate them?
+
+First, we note that:
+
+- One game has many teams associated to it (the ones that are playing that game);
+- One team may have participated in many games.
+
+The above observations show that we need a Many-to-Many relationship between Game and Team. Let's use the Super Many-to-Many relationship as explained earlier in this guide:
+
+```js
+// Super Many-to-Many relationship between Game and Team
+const GameTeam = sequelize.define('GameTeam', {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ allowNull: false,
+ },
+});
+Team.belongsToMany(Game, { through: GameTeam });
+Game.belongsToMany(Team, { through: GameTeam });
+GameTeam.belongsTo(Game);
+GameTeam.belongsTo(Team);
+Game.hasMany(GameTeam);
+Team.hasMany(GameTeam);
+```
+
+The part about players is trickier. We note that the set of players that form a team depends not only on the team (obviously), but also on which game is being considered. Therefore, we don't want a Many-to-Many relationship between Player and Team. We also don't want a Many-to-Many relationship between Player and Game. Instead of associating a Player to any of those models, what we need is an association between a Player and something like a _"team-game pair constraint"_, since it is the pair (team plus game) that defines which players belong there. So what we are looking for turns out to be precisely the junction model, GameTeam, itself! And, we note that, since a given _game-team pair_ specifies many players, and on the other hand that the same player can participate of many _game-team pairs_, we need a Many-to-Many relationship between Player and GameTeam!
+
+To provide the greatest flexibility, let's use the Super Many-to-Many relationship construction here again:
+
+```js
+// Super Many-to-Many relationship between Player and GameTeam
+const PlayerGameTeam = sequelize.define('PlayerGameTeam', {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ allowNull: false,
+ },
+});
+Player.belongsToMany(GameTeam, { through: PlayerGameTeam });
+GameTeam.belongsToMany(Player, { through: PlayerGameTeam });
+PlayerGameTeam.belongsTo(Player);
+PlayerGameTeam.belongsTo(GameTeam);
+Player.hasMany(PlayerGameTeam);
+GameTeam.hasMany(PlayerGameTeam);
+```
+
+The above associations achieve precisely what we want. Here is a full runnable example of this:
+
+```js
+const { Sequelize, Op, Model, DataTypes } = require('sequelize');
+const sequelize = new Sequelize('sqlite::memory:', {
+ define: { timestamps: false }, // Just for less clutter in this example
+});
+const Player = sequelize.define('Player', { username: DataTypes.STRING });
+const Team = sequelize.define('Team', { name: DataTypes.STRING });
+const Game = sequelize.define('Game', { name: DataTypes.STRING });
+
+// We apply a Super Many-to-Many relationship between Game and Team
+const GameTeam = sequelize.define('GameTeam', {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ allowNull: false,
+ },
+});
+Team.belongsToMany(Game, { through: GameTeam });
+Game.belongsToMany(Team, { through: GameTeam });
+GameTeam.belongsTo(Game);
+GameTeam.belongsTo(Team);
+Game.hasMany(GameTeam);
+Team.hasMany(GameTeam);
+
+// We apply a Super Many-to-Many relationship between Player and GameTeam
+const PlayerGameTeam = sequelize.define('PlayerGameTeam', {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ allowNull: false,
+ },
+});
+Player.belongsToMany(GameTeam, { through: PlayerGameTeam });
+GameTeam.belongsToMany(Player, { through: PlayerGameTeam });
+PlayerGameTeam.belongsTo(Player);
+PlayerGameTeam.belongsTo(GameTeam);
+Player.hasMany(PlayerGameTeam);
+GameTeam.hasMany(PlayerGameTeam);
+
+(async () => {
+ await sequelize.sync();
+ await Player.bulkCreate([
+ { username: 's0me0ne' },
+ { username: 'empty' },
+ { username: 'greenhead' },
+ { username: 'not_spock' },
+ { username: 'bowl_of_petunias' },
+ ]);
+ await Game.bulkCreate([
+ { name: 'The Big Clash' },
+ { name: 'Winter Showdown' },
+ { name: 'Summer Beatdown' },
+ ]);
+ await Team.bulkCreate([
+ { name: 'The Martians' },
+ { name: 'The Earthlings' },
+ { name: 'The Plutonians' },
+ ]);
+
+ // Let's start defining which teams were in which games. This can be done
+ // in several ways, such as calling `.setTeams` on each game. However, for
+ // brevity, we will use direct `create` calls instead, referring directly
+ // to the IDs we want. We know that IDs are given in order starting from 1.
+ await GameTeam.bulkCreate([
+ { GameId: 1, TeamId: 1 }, // this GameTeam will get id 1
+ { GameId: 1, TeamId: 2 }, // this GameTeam will get id 2
+ { GameId: 2, TeamId: 1 }, // this GameTeam will get id 3
+ { GameId: 2, TeamId: 3 }, // this GameTeam will get id 4
+ { GameId: 3, TeamId: 2 }, // this GameTeam will get id 5
+ { GameId: 3, TeamId: 3 }, // this GameTeam will get id 6
+ ]);
+
+ // Now let's specify players.
+ // For brevity, let's do it only for the second game (Winter Showdown).
+ // Let's say that that s0me0ne and greenhead played for The Martians, while
+ // not_spock and bowl_of_petunias played for The Plutonians:
+ await PlayerGameTeam.bulkCreate([
+ // In 'Winter Showdown' (i.e. GameTeamIds 3 and 4):
+ { PlayerId: 1, GameTeamId: 3 }, // s0me0ne played for The Martians
+ { PlayerId: 3, GameTeamId: 3 }, // greenhead played for The Martians
+ { PlayerId: 4, GameTeamId: 4 }, // not_spock played for The Plutonians
+ { PlayerId: 5, GameTeamId: 4 }, // bowl_of_petunias played for The Plutonians
+ ]);
+
+ // Now we can make queries!
+ const game = await Game.findOne({
+ where: {
+ name: 'Winter Showdown',
+ },
+ include: {
+ model: GameTeam,
+ include: [
+ {
+ model: Player,
+ through: { attributes: [] }, // Hide unwanted `PlayerGameTeam` nested object from results
+ },
+ Team,
+ ],
+ },
+ });
+
+ console.log(`Found game: "${game.name}"`);
+ for (let i = 0; i < game.GameTeams.length; i++) {
+ const team = game.GameTeams[i].Team;
+ const players = game.GameTeams[i].Players;
+ console.log(`- Team "${team.name}" played game "${game.name}" with the following players:`);
+ console.log(players.map(p => `--- ${p.username}`).join('\n'));
+ }
+})();
+```
+
+Output:
+
+```text
+Found game: "Winter Showdown"
+- Team "The Martians" played game "Winter Showdown" with the following players:
+--- s0me0ne
+--- greenhead
+- Team "The Plutonians" played game "Winter Showdown" with the following players:
+--- not_spock
+--- bowl_of_petunias
+```
+
+So this is how we can achieve a _many-to-many-to-many_ relationship between three models in Sequelize, by taking advantage of the Super Many-to-Many relationship technique!
+
+This idea can be applied recursively for even more complex, _many-to-many-to-...-to-many_ relationships (although at some point queries might become slow).
diff --git a/versioned_docs/version-6.x.x/advanced-association-concepts/association-scopes.md b/versioned_docs/version-6.x.x/advanced-association-concepts/association-scopes.md
index 7f7c9a7e..c1d6d0f2 100644
--- a/versioned_docs/version-6.x.x/advanced-association-concepts/association-scopes.md
+++ b/versioned_docs/version-6.x.x/advanced-association-concepts/association-scopes.md
@@ -1,67 +1,67 @@
----
-sidebar_position: 4
-title: Association Scopes
----
-
-This section concerns association scopes, which are similar but not the same as [model scopes](../other-topics/scopes.md).
-
-Association scopes can be placed both on the associated model (the target of the association) and on the through table for Many-to-Many relationships.
-
-## Concept
-
-Similarly to how a [model scope](../other-topics/scopes.md) is automatically applied on the model static calls, such as `Model.scope('foo').findAll()`, an association scope is a rule (more precisely, a set of default attributes and options) that is automatically applied on instance calls from the model. Here, *instance calls* mean method calls that are called from an instance (rather than from the Model itself). Mixins are the main example of instance methods (`instance.getSomething`, `instance.setSomething`, `instance.addSomething` and `instance.createSomething`).
-
-Association scopes behave just like model scopes, in the sense that both cause an automatic application of things like `where` clauses to finder calls; the difference being that instead of applying to static finder calls (which is the case for model scopes), the association scopes automatically apply to instance finder calls (such as mixins).
-
-## Example
-
-A basic example of an association scope for the One-to-Many association between models `Foo` and `Bar` is shown below.
-
-* Setup:
-
- ```js
- const Foo = sequelize.define('foo', { name: DataTypes.STRING });
- const Bar = sequelize.define('bar', { status: DataTypes.STRING });
- Foo.hasMany(Bar, {
- scope: {
- status: 'open'
- },
- as: 'openBars'
- });
- await sequelize.sync();
- const myFoo = await Foo.create({ name: "My Foo" });
- ```
-
-* After this setup, calling `myFoo.getOpenBars()` generates the following SQL:
-
- ```sql
- SELECT
- `id`, `status`, `createdAt`, `updatedAt`, `fooId`
- FROM `bars` AS `bar`
- WHERE `bar`.`status` = 'open' AND `bar`.`fooId` = 1;
- ```
-
-With this we can see that upon calling the `.getOpenBars()` mixin, the association scope `{ status: 'open' }` was automatically applied into the `WHERE` clause of the generated SQL.
-
-## Achieving the same behavior with standard scopes
-
-We could have achieved the same behavior with standard scopes:
-
-```js
-// Foo.hasMany(Bar, {
-// scope: {
-// status: 'open'
-// },
-// as: 'openBars'
-// });
-
-Bar.addScope('open', {
- where: {
- status: 'open'
- }
-});
-Foo.hasMany(Bar);
-Foo.hasMany(Bar.scope('open'), { as: 'openBars' });
-```
-
-With the above code, `myFoo.getOpenBars()` yields the same SQL shown above.
+---
+sidebar_position: 4
+title: Association Scopes
+---
+
+This section concerns association scopes, which are similar but not the same as [model scopes](../other-topics/scopes.md).
+
+Association scopes can be placed both on the associated model (the target of the association) and on the through table for Many-to-Many relationships.
+
+## Concept
+
+Similarly to how a [model scope](../other-topics/scopes.md) is automatically applied on the model static calls, such as `Model.scope('foo').findAll()`, an association scope is a rule (more precisely, a set of default attributes and options) that is automatically applied on instance calls from the model. Here, _instance calls_ mean method calls that are called from an instance (rather than from the Model itself). Mixins are the main example of instance methods (`instance.getSomething`, `instance.setSomething`, `instance.addSomething` and `instance.createSomething`).
+
+Association scopes behave just like model scopes, in the sense that both cause an automatic application of things like `where` clauses to finder calls; the difference being that instead of applying to static finder calls (which is the case for model scopes), the association scopes automatically apply to instance finder calls (such as mixins).
+
+## Example
+
+A basic example of an association scope for the One-to-Many association between models `Foo` and `Bar` is shown below.
+
+- Setup:
+
+ ```js
+ const Foo = sequelize.define('foo', { name: DataTypes.STRING });
+ const Bar = sequelize.define('bar', { status: DataTypes.STRING });
+ Foo.hasMany(Bar, {
+ scope: {
+ status: 'open',
+ },
+ as: 'openBars',
+ });
+ await sequelize.sync();
+ const myFoo = await Foo.create({ name: 'My Foo' });
+ ```
+
+- After this setup, calling `myFoo.getOpenBars()` generates the following SQL:
+
+ ```sql
+ SELECT
+ `id`, `status`, `createdAt`, `updatedAt`, `fooId`
+ FROM `bars` AS `bar`
+ WHERE `bar`.`status` = 'open' AND `bar`.`fooId` = 1;
+ ```
+
+With this we can see that upon calling the `.getOpenBars()` mixin, the association scope `{ status: 'open' }` was automatically applied into the `WHERE` clause of the generated SQL.
+
+## Achieving the same behavior with standard scopes
+
+We could have achieved the same behavior with standard scopes:
+
+```js
+// Foo.hasMany(Bar, {
+// scope: {
+// status: 'open'
+// },
+// as: 'openBars'
+// });
+
+Bar.addScope('open', {
+ where: {
+ status: 'open',
+ },
+});
+Foo.hasMany(Bar);
+Foo.hasMany(Bar.scope('open'), { as: 'openBars' });
+```
+
+With the above code, `myFoo.getOpenBars()` yields the same SQL shown above.
diff --git a/versioned_docs/version-6.x.x/advanced-association-concepts/creating-with-associations.md b/versioned_docs/version-6.x.x/advanced-association-concepts/creating-with-associations.md
index 51af4655..4a2fac80 100644
--- a/versioned_docs/version-6.x.x/advanced-association-concepts/creating-with-associations.md
+++ b/versioned_docs/version-6.x.x/advanced-association-concepts/creating-with-associations.md
@@ -1,133 +1,160 @@
----
-sidebar_position: 2
-title: Creating with Associations
----
-
-An instance can be created with nested association in one step, provided all elements are new.
-
-In contrast, performing updates and deletions involving nested objects is currently not possible. For that, you will have to perform each separate action explicitly.
-
-## BelongsTo / HasMany / HasOne association
-
-Consider the following models:
-
-```js
-class Product extends Model {}
-Product.init({
- title: Sequelize.STRING
-}, { sequelize, modelName: 'product' });
-class User extends Model {}
-User.init({
- firstName: Sequelize.STRING,
- lastName: Sequelize.STRING
-}, { sequelize, modelName: 'user' });
-class Address extends Model {}
-Address.init({
- type: DataTypes.STRING,
- line1: Sequelize.STRING,
- line2: Sequelize.STRING,
- city: Sequelize.STRING,
- state: Sequelize.STRING,
- zip: Sequelize.STRING,
-}, { sequelize, modelName: 'address' });
-
-// We save the return values of the association setup calls to use them later
-Product.User = Product.belongsTo(User);
-User.Addresses = User.hasMany(Address);
-// Also works for `hasOne`
-```
-
-A new `Product`, `User`, and one or more `Address` can be created in one step in the following way:
-
-```js
-return Product.create({
- title: 'Chair',
- user: {
- firstName: 'Mick',
- lastName: 'Broadstone',
- addresses: [{
- type: 'home',
- line1: '100 Main St.',
- city: 'Austin',
- state: 'TX',
- zip: '78704'
- }]
- }
-}, {
- include: [{
- association: Product.User,
- include: [ User.Addresses ]
- }]
-});
-```
-
-Observe the usage of the `include` option in the `Product.create` call. That is necessary for Sequelize to understand what you are trying to create along with the association.
-
-Note: here, our user model is called `user`, with a lowercase `u` - This means that the property in the object should also be `user`. If the name given to `sequelize.define` was `User`, the key in the object should also be `User`. Likewise for `addresses`, except it's pluralized being a `hasMany` association.
-
-## BelongsTo association with an alias
-
-The previous example can be extended to support an association alias.
-
-```js
-const Creator = Product.belongsTo(User, { as: 'creator' });
-
-return Product.create({
- title: 'Chair',
- creator: {
- firstName: 'Matt',
- lastName: 'Hansen'
- }
-}, {
- include: [ Creator ]
-});
-```
-
-## HasMany / BelongsToMany association
-
-Let's introduce the ability to associate a product with many tags. Setting up the models could look like:
-
-```js
-class Tag extends Model {}
-Tag.init({
- name: Sequelize.STRING
-}, { sequelize, modelName: 'tag' });
-
-Product.hasMany(Tag);
-// Also works for `belongsToMany`.
-```
-
-Now we can create a product with multiple tags in the following way:
-
-```js
-Product.create({
- id: 1,
- title: 'Chair',
- tags: [
- { name: 'Alpha'},
- { name: 'Beta'}
- ]
-}, {
- include: [ Tag ]
-})
-```
-
-And, we can modify this example to support an alias as well:
-
-```js
-const Categories = Product.hasMany(Tag, { as: 'categories' });
-
-Product.create({
- id: 1,
- title: 'Chair',
- categories: [
- { id: 1, name: 'Alpha' },
- { id: 2, name: 'Beta' }
- ]
-}, {
- include: [{
- association: Categories,
- as: 'categories'
- }]
-})
-```
+---
+sidebar_position: 2
+title: Creating with Associations
+---
+
+An instance can be created with nested association in one step, provided all elements are new.
+
+In contrast, performing updates and deletions involving nested objects is currently not possible. For that, you will have to perform each separate action explicitly.
+
+## BelongsTo / HasMany / HasOne association
+
+Consider the following models:
+
+```js
+class Product extends Model {}
+Product.init(
+ {
+ title: Sequelize.STRING,
+ },
+ { sequelize, modelName: 'product' },
+);
+class User extends Model {}
+User.init(
+ {
+ firstName: Sequelize.STRING,
+ lastName: Sequelize.STRING,
+ },
+ { sequelize, modelName: 'user' },
+);
+class Address extends Model {}
+Address.init(
+ {
+ type: DataTypes.STRING,
+ line1: Sequelize.STRING,
+ line2: Sequelize.STRING,
+ city: Sequelize.STRING,
+ state: Sequelize.STRING,
+ zip: Sequelize.STRING,
+ },
+ { sequelize, modelName: 'address' },
+);
+
+// We save the return values of the association setup calls to use them later
+Product.User = Product.belongsTo(User);
+User.Addresses = User.hasMany(Address);
+// Also works for `hasOne`
+```
+
+A new `Product`, `User`, and one or more `Address` can be created in one step in the following way:
+
+```js
+return Product.create(
+ {
+ title: 'Chair',
+ user: {
+ firstName: 'Mick',
+ lastName: 'Broadstone',
+ addresses: [
+ {
+ type: 'home',
+ line1: '100 Main St.',
+ city: 'Austin',
+ state: 'TX',
+ zip: '78704',
+ },
+ ],
+ },
+ },
+ {
+ include: [
+ {
+ association: Product.User,
+ include: [User.Addresses],
+ },
+ ],
+ },
+);
+```
+
+Observe the usage of the `include` option in the `Product.create` call. That is necessary for Sequelize to understand what you are trying to create along with the association.
+
+Note: here, our user model is called `user`, with a lowercase `u` - This means that the property in the object should also be `user`. If the name given to `sequelize.define` was `User`, the key in the object should also be `User`. Likewise for `addresses`, except it's pluralized being a `hasMany` association.
+
+## BelongsTo association with an alias
+
+The previous example can be extended to support an association alias.
+
+```js
+const Creator = Product.belongsTo(User, { as: 'creator' });
+
+return Product.create(
+ {
+ title: 'Chair',
+ creator: {
+ firstName: 'Matt',
+ lastName: 'Hansen',
+ },
+ },
+ {
+ include: [Creator],
+ },
+);
+```
+
+## HasMany / BelongsToMany association
+
+Let's introduce the ability to associate a product with many tags. Setting up the models could look like:
+
+```js
+class Tag extends Model {}
+Tag.init(
+ {
+ name: Sequelize.STRING,
+ },
+ { sequelize, modelName: 'tag' },
+);
+
+Product.hasMany(Tag);
+// Also works for `belongsToMany`.
+```
+
+Now we can create a product with multiple tags in the following way:
+
+```js
+Product.create(
+ {
+ id: 1,
+ title: 'Chair',
+ tags: [{ name: 'Alpha' }, { name: 'Beta' }],
+ },
+ {
+ include: [Tag],
+ },
+);
+```
+
+And, we can modify this example to support an alias as well:
+
+```js
+const Categories = Product.hasMany(Tag, { as: 'categories' });
+
+Product.create(
+ {
+ id: 1,
+ title: 'Chair',
+ categories: [
+ { id: 1, name: 'Alpha' },
+ { id: 2, name: 'Beta' },
+ ],
+ },
+ {
+ include: [
+ {
+ association: Categories,
+ as: 'categories',
+ },
+ ],
+ },
+);
+```
diff --git a/versioned_docs/version-6.x.x/advanced-association-concepts/eager-loading.md b/versioned_docs/version-6.x.x/advanced-association-concepts/eager-loading.md
index 475a0017..9d584a77 100644
--- a/versioned_docs/version-6.x.x/advanced-association-concepts/eager-loading.md
+++ b/versioned_docs/version-6.x.x/advanced-association-concepts/eager-loading.md
@@ -1,669 +1,695 @@
----
-sidebar_position: 1
-title: Eager Loading
----
-
-As briefly mentioned in [the associations guide](../core-concepts/assocs.md), eager Loading is the act of querying data of several models at once (one 'main' model and one or more associated models). At the SQL level, this is a query with one or more [joins](https://en.wikipedia.org/wiki/Join_\(SQL\)).
-
-When this is done, the associated models will be added by Sequelize in appropriately named, automatically created field(s) in the returned objects.
-
-In Sequelize, eager loading is mainly done by using the `include` option on a model finder query (such as `findOne`, `findAll`, etc).
-
-## Basic example
-
-Let's assume the following setup:
-
-```js
-const User = sequelize.define('user', { name: DataTypes.STRING }, { timestamps: false });
-const Task = sequelize.define('task', { name: DataTypes.STRING }, { timestamps: false });
-const Tool = sequelize.define('tool', {
- name: DataTypes.STRING,
- size: DataTypes.STRING
-}, { timestamps: false });
-User.hasMany(Task);
-Task.belongsTo(User);
-User.hasMany(Tool, { as: 'Instruments' });
-```
-
-### Fetching a single associated element
-
-OK. So, first of all, let's load all tasks with their associated user:
-
-```js
-const tasks = await Task.findAll({ include: User });
-console.log(JSON.stringify(tasks, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "A Task",
- "id": 1,
- "userId": 1,
- "user": {
- "name": "John Doe",
- "id": 1
- }
-}]
-```
-
-Here, `tasks[0].user instanceof User` is `true`. This shows that when Sequelize fetches associated models, they are added to the output object as model instances.
-
-Above, the associated model was added to a new field called `user` in the fetched task. The name of this field was automatically chosen by Sequelize based on the name of the associated model, where its pluralized form is used when applicable (i.e., when the association is `hasMany` or `belongsToMany`). In other words, since `Task.belongsTo(User)`, a task is associated to one user, therefore the logical choice is the singular form (which Sequelize follows automatically).
-
-### Fetching all associated elements
-
-Now, instead of loading the user that is associated to a given task, we will do the opposite - we will find all tasks associated to a given user.
-
-The method call is essentially the same. The only difference is that now the extra field created in the query result uses the pluralized form (`tasks` in this case), and its value is an array of task instances (instead of a single instance, as above).
-
-```js
-const users = await User.findAll({ include: Task });
-console.log(JSON.stringify(users, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "John Doe",
- "id": 1,
- "tasks": [{
- "name": "A Task",
- "id": 1,
- "userId": 1
- }]
-}]
-```
-
-Notice that the accessor (the `tasks` property in the resulting instance) is pluralized since the association is one-to-many.
-
-### Fetching an Aliased association
-
-If an association is aliased (using the `as` option), you must specify this alias when including the model. Instead of passing the model directly to the `include` option, you should instead provide an object with two options: `model` and `as`.
-
-Notice how the user's `Tool`s are aliased as `Instruments` above. In order to get that right you have to specify the model you want to load, as well as the alias:
-
-```js
-const users = await User.findAll({
- include: { model: Tool, as: 'Instruments' }
-});
-console.log(JSON.stringify(users, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "John Doe",
- "id": 1,
- "Instruments": [{
- "name": "Scissor",
- "id": 1,
- "userId": 1
- }]
-}]
-```
-
-You can also include by alias name by specifying a string that matches the association alias:
-
-```js
-User.findAll({ include: 'Instruments' }); // Also works
-User.findAll({ include: { association: 'Instruments' } }); // Also works
-```
-
-### Required eager loading
-
-When eager loading, we can force the query to return only records which have an associated model, effectively converting the query from the default `OUTER JOIN` to an `INNER JOIN`. This is done with the `required: true` option, as follows:
-
-```js
-User.findAll({
- include: {
- model: Task,
- required: true
- }
-});
-```
-
-This option also works on nested includes.
-
-### Eager loading filtered at the associated model level
-
-When eager loading, we can also filter the associated model using the `where` option, as in the following example:
-
-```js
-User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- where: {
- size: {
- [Op.ne]: 'small'
- }
- }
- }
-});
-```
-
-Generated SQL:
-
-```sql
-SELECT
- `user`.`id`,
- `user`.`name`,
- `Instruments`.`id` AS `Instruments.id`,
- `Instruments`.`name` AS `Instruments.name`,
- `Instruments`.`size` AS `Instruments.size`,
- `Instruments`.`userId` AS `Instruments.userId`
-FROM `users` AS `user`
-INNER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId` AND
- `Instruments`.`size` != 'small';
-```
-
-Note that the SQL query generated above will only fetch users that have at least one tool that matches the condition (of not being `small`, in this case). This is the case because, when the `where` option is used inside an `include`, Sequelize automatically sets the `required` option to `true`. This means that, instead of an `OUTER JOIN`, an `INNER JOIN` is done, returning only the parent models with at least one matching children.
-
-Note also that the `where` option used was converted into a condition for the `ON` clause of the `INNER JOIN`. In order to obtain a *top-level* `WHERE` clause, instead of an `ON` clause, something different must be done. This will be shown next.
-
-#### Referring to other columns
-
-If you want to apply a `WHERE` clause in an included model referring to a value from an associated model, you can simply use the `Sequelize.col` function, as show in the example below:
-
-```js
-// Find all projects with a least one task where task.state === project.state
-Project.findAll({
- include: {
- model: Task,
- where: {
- state: Sequelize.col('project.state')
- }
- }
-})
-```
-
-### Complex where clauses at the top-level
-
-To obtain top-level `WHERE` clauses that involve nested columns, Sequelize provides a way to reference nested columns: the `'$nested.column$'` syntax.
-
-It can be used, for example, to move the where conditions from an included model from the `ON` condition to a top-level `WHERE` clause.
-
-```js
-User.findAll({
- where: {
- '$Instruments.size$': { [Op.ne]: 'small' }
- },
- include: [{
- model: Tool,
- as: 'Instruments'
- }]
-});
-```
-
-Generated SQL:
-
-```sql
-SELECT
- `user`.`id`,
- `user`.`name`,
- `Instruments`.`id` AS `Instruments.id`,
- `Instruments`.`name` AS `Instruments.name`,
- `Instruments`.`size` AS `Instruments.size`,
- `Instruments`.`userId` AS `Instruments.userId`
-FROM `users` AS `user`
-LEFT OUTER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
-WHERE `Instruments`.`size` != 'small';
-```
-
-The `$nested.column$` syntax also works for columns that are nested several levels deep, such as `$some.super.deeply.nested.column$`. Therefore, you can use this to make complex filters on deeply nested columns.
-
-For a better understanding of all differences between the inner `where` option (used inside an `include`), with and without the `required` option, and a top-level `where` using the `$nested.column$` syntax, below we have four examples for you:
-
-```js
-// Inner where, with default `required: true`
-await User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- where: {
- size: { [Op.ne]: 'small' }
- }
- }
-});
-
-// Inner where, `required: false`
-await User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- where: {
- size: { [Op.ne]: 'small' }
- },
- required: false
- }
-});
-
-// Top-level where, with default `required: false`
-await User.findAll({
- where: {
- '$Instruments.size$': { [Op.ne]: 'small' }
- },
- include: {
- model: Tool,
- as: 'Instruments'
- }
-});
-
-// Top-level where, `required: true`
-await User.findAll({
- where: {
- '$Instruments.size$': { [Op.ne]: 'small' }
- },
- include: {
- model: Tool,
- as: 'Instruments',
- required: true
- }
-});
-```
-
-Generated SQLs, in order:
-
-```sql
--- Inner where, with default `required: true`
-SELECT [...] FROM `users` AS `user`
-INNER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
- AND `Instruments`.`size` != 'small';
-
--- Inner where, `required: false`
-SELECT [...] FROM `users` AS `user`
-LEFT OUTER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
- AND `Instruments`.`size` != 'small';
-
--- Top-level where, with default `required: false`
-SELECT [...] FROM `users` AS `user`
-LEFT OUTER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
-WHERE `Instruments`.`size` != 'small';
-
--- Top-level where, `required: true`
-SELECT [...] FROM `users` AS `user`
-INNER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
-WHERE `Instruments`.`size` != 'small';
-```
-
-### Fetching with `RIGHT OUTER JOIN` (MySQL, MariaDB, PostgreSQL and MSSQL only)
-
-By default, associations are loaded using a `LEFT OUTER JOIN` - that is to say it only includes records from the parent table. You can change this behavior to a `RIGHT OUTER JOIN` by passing the `right` option, if the dialect you are using supports it.
-
-Currently, SQLite does not support [right joins](https://www.sqlite.org/omitted.html).
-
-*Note:* `right` is only respected if `required` is false.
-
-```js
-User.findAll({
- include: [{
- model: Task // will create a left join
- }]
-});
-User.findAll({
- include: [{
- model: Task,
- right: true // will create a right join
- }]
-});
-User.findAll({
- include: [{
- model: Task,
- required: true,
- right: true // has no effect, will create an inner join
- }]
-});
-User.findAll({
- include: [{
- model: Task,
- where: { name: { [Op.ne]: 'empty trash' } },
- right: true // has no effect, will create an inner join
- }]
-});
-User.findAll({
- include: [{
- model: Tool,
- where: { name: { [Op.ne]: 'empty trash' } },
- required: false // will create a left join
- }]
-});
-User.findAll({
- include: [{
- model: Tool,
- where: { name: { [Op.ne]: 'empty trash' } },
- required: false
- right: true // will create a right join
- }]
-});
-```
-
-## Multiple eager loading
-
-The `include` option can receive an array in order to fetch multiple associated models at once:
-
-```js
-Foo.findAll({
- include: [
- {
- model: Bar,
- required: true
- },
- {
- model: Baz,
- where: /* ... */
- },
- Qux // Shorthand syntax for { model: Qux } also works here
- ]
-})
-```
-
-## Eager loading with Many-to-Many relationships
-
-When you perform eager loading on a model with a Belongs-to-Many relationship, Sequelize will fetch the junction table data as well, by default. For example:
-
-```js
-const Foo = sequelize.define('Foo', { name: DataTypes.TEXT });
-const Bar = sequelize.define('Bar', { name: DataTypes.TEXT });
-Foo.belongsToMany(Bar, { through: 'Foo_Bar' });
-Bar.belongsToMany(Foo, { through: 'Foo_Bar' });
-
-await sequelize.sync();
-const foo = await Foo.create({ name: 'foo' });
-const bar = await Bar.create({ name: 'bar' });
-await foo.addBar(bar);
-const fetchedFoo = await Foo.findOne({ include: Bar });
-console.log(JSON.stringify(fetchedFoo, null, 2));
-```
-
-Output:
-
-```json
-{
- "id": 1,
- "name": "foo",
- "Bars": [
- {
- "id": 1,
- "name": "bar",
- "Foo_Bar": {
- "FooId": 1,
- "BarId": 1
- }
- }
- ]
-}
-```
-
-Note that every bar instance eager loaded into the `"Bars"` property has an extra property called `Foo_Bar` which is the relevant Sequelize instance of the junction model. By default, Sequelize fetches all attributes from the junction table in order to build this extra property.
-
-However, you can specify which attributes you want fetched. This is done with the `attributes` option applied inside the `through` option of the include. For example:
-
-```js
-Foo.findAll({
- include: [{
- model: Bar,
- through: {
- attributes: [/* list the wanted attributes here */]
- }
- }]
-});
-```
-
-If you don't want anything from the junction table, you can explicitly provide an empty array to the `attributes` option inside the `through` option of the `include` option, and in this case nothing will be fetched and the extra property will not even be created:
-
-```js
-Foo.findOne({
- include: {
- model: Bar,
- through: {
- attributes: []
- }
- }
-});
-```
-
-Output:
-
-```json
-{
- "id": 1,
- "name": "foo",
- "Bars": [
- {
- "id": 1,
- "name": "bar"
- }
- ]
-}
-```
-
-Whenever including a model from a Many-to-Many relationship, you can also apply a filter on the junction table. This is done with the `where` option applied inside the `through` option of the include. For example:
-
-```js
-User.findAll({
- include: [{
- model: Project,
- through: {
- where: {
- // Here, `completed` is a column present at the junction table
- completed: true
- }
- }
- }]
-});
-```
-
-Generated SQL (using SQLite):
-
-```sql
-SELECT
- `User`.`id`,
- `User`.`name`,
- `Projects`.`id` AS `Projects.id`,
- `Projects`.`name` AS `Projects.name`,
- `Projects->User_Project`.`completed` AS `Projects.User_Project.completed`,
- `Projects->User_Project`.`UserId` AS `Projects.User_Project.UserId`,
- `Projects->User_Project`.`ProjectId` AS `Projects.User_Project.ProjectId`
-FROM `Users` AS `User`
-LEFT OUTER JOIN `User_Projects` AS `Projects->User_Project` ON
- `User`.`id` = `Projects->User_Project`.`UserId`
-LEFT OUTER JOIN `Projects` AS `Projects` ON
- `Projects`.`id` = `Projects->User_Project`.`ProjectId` AND
- `Projects->User_Project`.`completed` = 1;
-```
-
-## Including everything
-
-To include all associated models, you can use the `all` and `nested` options:
-
-```js
-// Fetch all models associated with User
-User.findAll({ include: { all: true }});
-
-// Fetch all models associated with User and their nested associations (recursively)
-User.findAll({ include: { all: true, nested: true }});
-```
-
-## Including soft deleted records
-
-In case you want to eager load soft deleted records you can do that by setting `include.paranoid` to `false`:
-
-```js
-User.findAll({
- include: [{
- model: Tool,
- as: 'Instruments',
- where: { size: { [Op.ne]: 'small' } },
- paranoid: false
- }]
-});
-```
-
-## Ordering eager loaded associations
-
-When you want to apply `ORDER` clauses to eager loaded models, you must use the top-level `order` option with augmented arrays, starting with the specification of the nested model you want to sort.
-
-This is better understood with examples.
-
-```js
-Company.findAll({
- include: Division,
- order: [
- // We start the order array with the model we want to sort
- [Division, 'name', 'ASC']
- ]
-});
-Company.findAll({
- include: Division,
- order: [
- [Division, 'name', 'DESC']
- ]
-});
-Company.findAll({
- // If the include uses an alias...
- include: { model: Division, as: 'Div' },
- order: [
- // ...we use the same syntax from the include
- // in the beginning of the order array
- [{ model: Division, as: 'Div' }, 'name', 'DESC']
- ]
-});
-
-Company.findAll({
- // If we have includes nested in several levels...
- include: {
- model: Division,
- include: Department
- },
- order: [
- // ... we replicate the include chain of interest
- // at the beginning of the order array
- [Division, Department, 'name', 'DESC']
- ]
-});
-```
-
-In the case of many-to-many relationships, you are also able to sort by attributes in the through table. For example, assuming we have a Many-to-Many relationship between `Division` and `Department` whose junction model is `DepartmentDivision`, you can do:
-
-```js
-Company.findAll({
- include: {
- model: Division,
- include: Department
- },
- order: [
- [Division, DepartmentDivision, 'name', 'ASC']
- ]
-});
-```
-
-In all the above examples, you have noticed that the `order` option is used at the top-level. The only situation in which `order` also works inside the include option is when `separate: true` is used. In that case, the usage is as follows:
-
-```js
-// This only works for `separate: true` (which in turn
-// only works for has-many relationships).
-User.findAll({
- include: {
- model: Post,
- separate: true,
- order: [
- ['createdAt', 'DESC']
- ]
- }
-});
-```
-
-### Complex ordering involving sub-queries
-
-Take a look at the [guide on sub-queries](../other-topics/sub-queries.md) for an example of how to use a sub-query to assist a more complex ordering.
-
-## Nested eager loading
-
-You can use nested eager loading to load all related models of a related model:
-
-```js
-const users = await User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- include: {
- model: Teacher,
- include: [ /* etc */ ]
- }
- }
-});
-console.log(JSON.stringify(users, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "John Doe",
- "id": 1,
- "Instruments": [{ // 1:M and N:M association
- "name": "Scissor",
- "id": 1,
- "userId": 1,
- "Teacher": { // 1:1 association
- "name": "Jimi Hendrix"
- }
- }]
-}]
-```
-
-This will produce an outer join. However, a `where` clause on a related model will create an inner join and return only the instances that have matching sub-models. To return all parent instances, you should add `required: false`.
-
-```js
-User.findAll({
- include: [{
- model: Tool,
- as: 'Instruments',
- include: [{
- model: Teacher,
- where: {
- school: "Woodstock Music School"
- },
- required: false
- }]
- }]
-});
-```
-
-The query above will return all users, and all their instruments, but only those teachers associated with `Woodstock Music School`.
-
-## Using `findAndCountAll` with includes
-
-The `findAndCountAll` utility function supports includes. Only the includes that are marked as `required` will be considered in `count`. For example, if you want to find and count all users who have a profile:
-
-```js
-User.findAndCountAll({
- include: [
- { model: Profile, required: true }
- ],
- limit: 3
-});
-```
-
-Because the include for `Profile` has `required` set it will result in an inner join, and only the users who have a profile will be counted. If we remove `required` from the include, both users with and without profiles will be counted. Adding a `where` clause to the include automatically makes it required:
-
-```js
-User.findAndCountAll({
- include: [
- { model: Profile, where: { active: true } }
- ],
- limit: 3
-});
-```
-
-The query above will only count users who have an active profile, because `required` is implicitly set to true when you add a where clause to the include.
+---
+sidebar_position: 1
+title: Eager Loading
+---
+
+As briefly mentioned in [the associations guide](../core-concepts/assocs.md), eager Loading is the act of querying data of several models at once (one 'main' model and one or more associated models). At the SQL level, this is a query with one or more [joins]().
+
+When this is done, the associated models will be added by Sequelize in appropriately named, automatically created field(s) in the returned objects.
+
+In Sequelize, eager loading is mainly done by using the `include` option on a model finder query (such as `findOne`, `findAll`, etc).
+
+## Basic example
+
+Let's assume the following setup:
+
+```js
+const User = sequelize.define('user', { name: DataTypes.STRING }, { timestamps: false });
+const Task = sequelize.define('task', { name: DataTypes.STRING }, { timestamps: false });
+const Tool = sequelize.define(
+ 'tool',
+ {
+ name: DataTypes.STRING,
+ size: DataTypes.STRING,
+ },
+ { timestamps: false },
+);
+User.hasMany(Task);
+Task.belongsTo(User);
+User.hasMany(Tool, { as: 'Instruments' });
+```
+
+### Fetching a single associated element
+
+OK. So, first of all, let's load all tasks with their associated user:
+
+```js
+const tasks = await Task.findAll({ include: User });
+console.log(JSON.stringify(tasks, null, 2));
+```
+
+Output:
+
+```json
+[
+ {
+ "name": "A Task",
+ "id": 1,
+ "userId": 1,
+ "user": {
+ "name": "John Doe",
+ "id": 1
+ }
+ }
+]
+```
+
+Here, `tasks[0].user instanceof User` is `true`. This shows that when Sequelize fetches associated models, they are added to the output object as model instances.
+
+Above, the associated model was added to a new field called `user` in the fetched task. The name of this field was automatically chosen by Sequelize based on the name of the associated model, where its pluralized form is used when applicable (i.e., when the association is `hasMany` or `belongsToMany`). In other words, since `Task.belongsTo(User)`, a task is associated to one user, therefore the logical choice is the singular form (which Sequelize follows automatically).
+
+### Fetching all associated elements
+
+Now, instead of loading the user that is associated to a given task, we will do the opposite - we will find all tasks associated to a given user.
+
+The method call is essentially the same. The only difference is that now the extra field created in the query result uses the pluralized form (`tasks` in this case), and its value is an array of task instances (instead of a single instance, as above).
+
+```js
+const users = await User.findAll({ include: Task });
+console.log(JSON.stringify(users, null, 2));
+```
+
+Output:
+
+```json
+[
+ {
+ "name": "John Doe",
+ "id": 1,
+ "tasks": [
+ {
+ "name": "A Task",
+ "id": 1,
+ "userId": 1
+ }
+ ]
+ }
+]
+```
+
+Notice that the accessor (the `tasks` property in the resulting instance) is pluralized since the association is one-to-many.
+
+### Fetching an Aliased association
+
+If an association is aliased (using the `as` option), you must specify this alias when including the model. Instead of passing the model directly to the `include` option, you should instead provide an object with two options: `model` and `as`.
+
+Notice how the user's `Tool`s are aliased as `Instruments` above. In order to get that right you have to specify the model you want to load, as well as the alias:
+
+```js
+const users = await User.findAll({
+ include: { model: Tool, as: 'Instruments' },
+});
+console.log(JSON.stringify(users, null, 2));
+```
+
+Output:
+
+```json
+[
+ {
+ "name": "John Doe",
+ "id": 1,
+ "Instruments": [
+ {
+ "name": "Scissor",
+ "id": 1,
+ "userId": 1
+ }
+ ]
+ }
+]
+```
+
+You can also include by alias name by specifying a string that matches the association alias:
+
+```js
+User.findAll({ include: 'Instruments' }); // Also works
+User.findAll({ include: { association: 'Instruments' } }); // Also works
+```
+
+### Required eager loading
+
+When eager loading, we can force the query to return only records which have an associated model, effectively converting the query from the default `OUTER JOIN` to an `INNER JOIN`. This is done with the `required: true` option, as follows:
+
+```js
+User.findAll({
+ include: {
+ model: Task,
+ required: true,
+ },
+});
+```
+
+This option also works on nested includes.
+
+### Eager loading filtered at the associated model level
+
+When eager loading, we can also filter the associated model using the `where` option, as in the following example:
+
+```js
+User.findAll({
+ include: {
+ model: Tool,
+ as: 'Instruments',
+ where: {
+ size: {
+ [Op.ne]: 'small',
+ },
+ },
+ },
+});
+```
+
+Generated SQL:
+
+```sql
+SELECT
+ `user`.`id`,
+ `user`.`name`,
+ `Instruments`.`id` AS `Instruments.id`,
+ `Instruments`.`name` AS `Instruments.name`,
+ `Instruments`.`size` AS `Instruments.size`,
+ `Instruments`.`userId` AS `Instruments.userId`
+FROM `users` AS `user`
+INNER JOIN `tools` AS `Instruments` ON
+ `user`.`id` = `Instruments`.`userId` AND
+ `Instruments`.`size` != 'small';
+```
+
+Note that the SQL query generated above will only fetch users that have at least one tool that matches the condition (of not being `small`, in this case). This is the case because, when the `where` option is used inside an `include`, Sequelize automatically sets the `required` option to `true`. This means that, instead of an `OUTER JOIN`, an `INNER JOIN` is done, returning only the parent models with at least one matching children.
+
+Note also that the `where` option used was converted into a condition for the `ON` clause of the `INNER JOIN`. In order to obtain a _top-level_ `WHERE` clause, instead of an `ON` clause, something different must be done. This will be shown next.
+
+#### Referring to other columns
+
+If you want to apply a `WHERE` clause in an included model referring to a value from an associated model, you can simply use the `Sequelize.col` function, as show in the example below:
+
+```js
+// Find all projects with a least one task where task.state === project.state
+Project.findAll({
+ include: {
+ model: Task,
+ where: {
+ state: Sequelize.col('project.state'),
+ },
+ },
+});
+```
+
+### Complex where clauses at the top-level
+
+To obtain top-level `WHERE` clauses that involve nested columns, Sequelize provides a way to reference nested columns: the `'$nested.column$'` syntax.
+
+It can be used, for example, to move the where conditions from an included model from the `ON` condition to a top-level `WHERE` clause.
+
+```js
+User.findAll({
+ where: {
+ '$Instruments.size$': { [Op.ne]: 'small' },
+ },
+ include: [
+ {
+ model: Tool,
+ as: 'Instruments',
+ },
+ ],
+});
+```
+
+Generated SQL:
+
+```sql
+SELECT
+ `user`.`id`,
+ `user`.`name`,
+ `Instruments`.`id` AS `Instruments.id`,
+ `Instruments`.`name` AS `Instruments.name`,
+ `Instruments`.`size` AS `Instruments.size`,
+ `Instruments`.`userId` AS `Instruments.userId`
+FROM `users` AS `user`
+LEFT OUTER JOIN `tools` AS `Instruments` ON
+ `user`.`id` = `Instruments`.`userId`
+WHERE `Instruments`.`size` != 'small';
+```
+
+The `$nested.column$` syntax also works for columns that are nested several levels deep, such as `$some.super.deeply.nested.column$`. Therefore, you can use this to make complex filters on deeply nested columns.
+
+For a better understanding of all differences between the inner `where` option (used inside an `include`), with and without the `required` option, and a top-level `where` using the `$nested.column$` syntax, below we have four examples for you:
+
+```js
+// Inner where, with default `required: true`
+await User.findAll({
+ include: {
+ model: Tool,
+ as: 'Instruments',
+ where: {
+ size: { [Op.ne]: 'small' },
+ },
+ },
+});
+
+// Inner where, `required: false`
+await User.findAll({
+ include: {
+ model: Tool,
+ as: 'Instruments',
+ where: {
+ size: { [Op.ne]: 'small' },
+ },
+ required: false,
+ },
+});
+
+// Top-level where, with default `required: false`
+await User.findAll({
+ where: {
+ '$Instruments.size$': { [Op.ne]: 'small' },
+ },
+ include: {
+ model: Tool,
+ as: 'Instruments',
+ },
+});
+
+// Top-level where, `required: true`
+await User.findAll({
+ where: {
+ '$Instruments.size$': { [Op.ne]: 'small' },
+ },
+ include: {
+ model: Tool,
+ as: 'Instruments',
+ required: true,
+ },
+});
+```
+
+Generated SQLs, in order:
+
+```sql
+-- Inner where, with default `required: true`
+SELECT [...] FROM `users` AS `user`
+INNER JOIN `tools` AS `Instruments` ON
+ `user`.`id` = `Instruments`.`userId`
+ AND `Instruments`.`size` != 'small';
+
+-- Inner where, `required: false`
+SELECT [...] FROM `users` AS `user`
+LEFT OUTER JOIN `tools` AS `Instruments` ON
+ `user`.`id` = `Instruments`.`userId`
+ AND `Instruments`.`size` != 'small';
+
+-- Top-level where, with default `required: false`
+SELECT [...] FROM `users` AS `user`
+LEFT OUTER JOIN `tools` AS `Instruments` ON
+ `user`.`id` = `Instruments`.`userId`
+WHERE `Instruments`.`size` != 'small';
+
+-- Top-level where, `required: true`
+SELECT [...] FROM `users` AS `user`
+INNER JOIN `tools` AS `Instruments` ON
+ `user`.`id` = `Instruments`.`userId`
+WHERE `Instruments`.`size` != 'small';
+```
+
+### Fetching with `RIGHT OUTER JOIN` (MySQL, MariaDB, PostgreSQL and MSSQL only)
+
+By default, associations are loaded using a `LEFT OUTER JOIN` - that is to say it only includes records from the parent table. You can change this behavior to a `RIGHT OUTER JOIN` by passing the `right` option, if the dialect you are using supports it.
+
+Currently, SQLite does not support [right joins](https://www.sqlite.org/omitted.html).
+
+_Note:_ `right` is only respected if `required` is false.
+
+```js
+User.findAll({
+ include: [{
+ model: Task // will create a left join
+ }]
+});
+User.findAll({
+ include: [{
+ model: Task,
+ right: true // will create a right join
+ }]
+});
+User.findAll({
+ include: [{
+ model: Task,
+ required: true,
+ right: true // has no effect, will create an inner join
+ }]
+});
+User.findAll({
+ include: [{
+ model: Task,
+ where: { name: { [Op.ne]: 'empty trash' } },
+ right: true // has no effect, will create an inner join
+ }]
+});
+User.findAll({
+ include: [{
+ model: Tool,
+ where: { name: { [Op.ne]: 'empty trash' } },
+ required: false // will create a left join
+ }]
+});
+User.findAll({
+ include: [{
+ model: Tool,
+ where: { name: { [Op.ne]: 'empty trash' } },
+ required: false
+ right: true // will create a right join
+ }]
+});
+```
+
+## Multiple eager loading
+
+The `include` option can receive an array in order to fetch multiple associated models at once:
+
+```js
+Foo.findAll({
+ include: [
+ {
+ model: Bar,
+ required: true
+ },
+ {
+ model: Baz,
+ where: /* ... */
+ },
+ Qux // Shorthand syntax for { model: Qux } also works here
+ ]
+})
+```
+
+## Eager loading with Many-to-Many relationships
+
+When you perform eager loading on a model with a Belongs-to-Many relationship, Sequelize will fetch the junction table data as well, by default. For example:
+
+```js
+const Foo = sequelize.define('Foo', { name: DataTypes.TEXT });
+const Bar = sequelize.define('Bar', { name: DataTypes.TEXT });
+Foo.belongsToMany(Bar, { through: 'Foo_Bar' });
+Bar.belongsToMany(Foo, { through: 'Foo_Bar' });
+
+await sequelize.sync();
+const foo = await Foo.create({ name: 'foo' });
+const bar = await Bar.create({ name: 'bar' });
+await foo.addBar(bar);
+const fetchedFoo = await Foo.findOne({ include: Bar });
+console.log(JSON.stringify(fetchedFoo, null, 2));
+```
+
+Output:
+
+```json
+{
+ "id": 1,
+ "name": "foo",
+ "Bars": [
+ {
+ "id": 1,
+ "name": "bar",
+ "Foo_Bar": {
+ "FooId": 1,
+ "BarId": 1
+ }
+ }
+ ]
+}
+```
+
+Note that every bar instance eager loaded into the `"Bars"` property has an extra property called `Foo_Bar` which is the relevant Sequelize instance of the junction model. By default, Sequelize fetches all attributes from the junction table in order to build this extra property.
+
+However, you can specify which attributes you want fetched. This is done with the `attributes` option applied inside the `through` option of the include. For example:
+
+```js
+Foo.findAll({
+ include: [
+ {
+ model: Bar,
+ through: {
+ attributes: [
+ /* list the wanted attributes here */
+ ],
+ },
+ },
+ ],
+});
+```
+
+If you don't want anything from the junction table, you can explicitly provide an empty array to the `attributes` option inside the `through` option of the `include` option, and in this case nothing will be fetched and the extra property will not even be created:
+
+```js
+Foo.findOne({
+ include: {
+ model: Bar,
+ through: {
+ attributes: [],
+ },
+ },
+});
+```
+
+Output:
+
+```json
+{
+ "id": 1,
+ "name": "foo",
+ "Bars": [
+ {
+ "id": 1,
+ "name": "bar"
+ }
+ ]
+}
+```
+
+Whenever including a model from a Many-to-Many relationship, you can also apply a filter on the junction table. This is done with the `where` option applied inside the `through` option of the include. For example:
+
+```js
+User.findAll({
+ include: [
+ {
+ model: Project,
+ through: {
+ where: {
+ // Here, `completed` is a column present at the junction table
+ completed: true,
+ },
+ },
+ },
+ ],
+});
+```
+
+Generated SQL (using SQLite):
+
+```sql
+SELECT
+ `User`.`id`,
+ `User`.`name`,
+ `Projects`.`id` AS `Projects.id`,
+ `Projects`.`name` AS `Projects.name`,
+ `Projects->User_Project`.`completed` AS `Projects.User_Project.completed`,
+ `Projects->User_Project`.`UserId` AS `Projects.User_Project.UserId`,
+ `Projects->User_Project`.`ProjectId` AS `Projects.User_Project.ProjectId`
+FROM `Users` AS `User`
+LEFT OUTER JOIN `User_Projects` AS `Projects->User_Project` ON
+ `User`.`id` = `Projects->User_Project`.`UserId`
+LEFT OUTER JOIN `Projects` AS `Projects` ON
+ `Projects`.`id` = `Projects->User_Project`.`ProjectId` AND
+ `Projects->User_Project`.`completed` = 1;
+```
+
+## Including everything
+
+To include all associated models, you can use the `all` and `nested` options:
+
+```js
+// Fetch all models associated with User
+User.findAll({ include: { all: true } });
+
+// Fetch all models associated with User and their nested associations (recursively)
+User.findAll({ include: { all: true, nested: true } });
+```
+
+## Including soft deleted records
+
+In case you want to eager load soft deleted records you can do that by setting `include.paranoid` to `false`:
+
+```js
+User.findAll({
+ include: [
+ {
+ model: Tool,
+ as: 'Instruments',
+ where: { size: { [Op.ne]: 'small' } },
+ paranoid: false,
+ },
+ ],
+});
+```
+
+## Ordering eager loaded associations
+
+When you want to apply `ORDER` clauses to eager loaded models, you must use the top-level `order` option with augmented arrays, starting with the specification of the nested model you want to sort.
+
+This is better understood with examples.
+
+```js
+Company.findAll({
+ include: Division,
+ order: [
+ // We start the order array with the model we want to sort
+ [Division, 'name', 'ASC'],
+ ],
+});
+Company.findAll({
+ include: Division,
+ order: [[Division, 'name', 'DESC']],
+});
+Company.findAll({
+ // If the include uses an alias...
+ include: { model: Division, as: 'Div' },
+ order: [
+ // ...we use the same syntax from the include
+ // in the beginning of the order array
+ [{ model: Division, as: 'Div' }, 'name', 'DESC'],
+ ],
+});
+
+Company.findAll({
+ // If we have includes nested in several levels...
+ include: {
+ model: Division,
+ include: Department,
+ },
+ order: [
+ // ... we replicate the include chain of interest
+ // at the beginning of the order array
+ [Division, Department, 'name', 'DESC'],
+ ],
+});
+```
+
+In the case of many-to-many relationships, you are also able to sort by attributes in the through table. For example, assuming we have a Many-to-Many relationship between `Division` and `Department` whose junction model is `DepartmentDivision`, you can do:
+
+```js
+Company.findAll({
+ include: {
+ model: Division,
+ include: Department,
+ },
+ order: [[Division, DepartmentDivision, 'name', 'ASC']],
+});
+```
+
+In all the above examples, you have noticed that the `order` option is used at the top-level. The only situation in which `order` also works inside the include option is when `separate: true` is used. In that case, the usage is as follows:
+
+```js
+// This only works for `separate: true` (which in turn
+// only works for has-many relationships).
+User.findAll({
+ include: {
+ model: Post,
+ separate: true,
+ order: [['createdAt', 'DESC']],
+ },
+});
+```
+
+### Complex ordering involving sub-queries
+
+Take a look at the [guide on sub-queries](../other-topics/sub-queries.md) for an example of how to use a sub-query to assist a more complex ordering.
+
+## Nested eager loading
+
+You can use nested eager loading to load all related models of a related model:
+
+```js
+const users = await User.findAll({
+ include: {
+ model: Tool,
+ as: 'Instruments',
+ include: {
+ model: Teacher,
+ include: [
+ /* etc */
+ ],
+ },
+ },
+});
+console.log(JSON.stringify(users, null, 2));
+```
+
+Output:
+
+```json
+[
+ {
+ "name": "John Doe",
+ "id": 1,
+ "Instruments": [
+ {
+ // 1:M and N:M association
+ "name": "Scissor",
+ "id": 1,
+ "userId": 1,
+ "Teacher": {
+ // 1:1 association
+ "name": "Jimi Hendrix"
+ }
+ }
+ ]
+ }
+]
+```
+
+This will produce an outer join. However, a `where` clause on a related model will create an inner join and return only the instances that have matching sub-models. To return all parent instances, you should add `required: false`.
+
+```js
+User.findAll({
+ include: [
+ {
+ model: Tool,
+ as: 'Instruments',
+ include: [
+ {
+ model: Teacher,
+ where: {
+ school: 'Woodstock Music School',
+ },
+ required: false,
+ },
+ ],
+ },
+ ],
+});
+```
+
+The query above will return all users, and all their instruments, but only those teachers associated with `Woodstock Music School`.
+
+## Using `findAndCountAll` with includes
+
+The `findAndCountAll` utility function supports includes. Only the includes that are marked as `required` will be considered in `count`. For example, if you want to find and count all users who have a profile:
+
+```js
+User.findAndCountAll({
+ include: [{ model: Profile, required: true }],
+ limit: 3,
+});
+```
+
+Because the include for `Profile` has `required` set it will result in an inner join, and only the users who have a profile will be counted. If we remove `required` from the include, both users with and without profiles will be counted. Adding a `where` clause to the include automatically makes it required:
+
+```js
+User.findAndCountAll({
+ include: [{ model: Profile, where: { active: true } }],
+ limit: 3,
+});
+```
+
+The query above will only count users who have an active profile, because `required` is implicitly set to true when you add a where clause to the include.
diff --git a/versioned_docs/version-6.x.x/advanced-association-concepts/polymorphic-associations.md b/versioned_docs/version-6.x.x/advanced-association-concepts/polymorphic-associations.md
index da2c6d79..919d2052 100644
--- a/versioned_docs/version-6.x.x/advanced-association-concepts/polymorphic-associations.md
+++ b/versioned_docs/version-6.x.x/advanced-association-concepts/polymorphic-associations.md
@@ -1,430 +1,447 @@
----
-sidebar_position: 5
-title: Polymorphic Associations
----
-
-_**Note:** the usage of polymorphic associations in Sequelize, as outlined in this guide, should be done with caution. Don't just copy-paste code from here, otherwise you might easily make mistakes and introduce bugs in your code. Make sure you understand what is going on._
-
-## Concept
-
-A **polymorphic association** consists on two (or more) associations happening with the same foreign key.
-
-For example, consider the models `Image`, `Video` and `Comment`. The first two represent something that a user might post. We want to allow comments to be placed in both of them. This way, we immediately think of establishing the following associations:
-
-* A One-to-Many association between `Image` and `Comment`:
-
- ```js
- Image.hasMany(Comment);
- Comment.belongsTo(Image);
- ```
-
-* A One-to-Many association between `Video` and `Comment`:
-
- ```js
- Video.hasMany(Comment);
- Comment.belongsTo(Video);
- ```
-
-However, the above would cause Sequelize to create two foreign keys on the `Comment` table: `ImageId` and `VideoId`. This is not ideal because this structure makes it look like a comment can be attached at the same time to one image and one video, which isn't true. Instead, what we really want here is precisely a polymorphic association, in which a `Comment` points to a single **Commentable**, an abstract polymorphic entity that represents one of `Image` or `Video`.
-
-Before proceeding to how to configure such an association, let's see how using it looks like:
-
-```js
-const image = await Image.create({ url: "https://placekitten.com/408/287" });
-const comment = await image.createComment({ content: "Awesome!" });
-
-console.log(comment.commentableId === image.id); // true
-
-// We can also retrieve which type of commentable a comment is associated to.
-// The following prints the model name of the associated commentable instance.
-console.log(comment.commentableType); // "Image"
-
-// We can use a polymorphic method to retrieve the associated commentable, without
-// having to worry whether it's an Image or a Video.
-const associatedCommentable = await comment.getCommentable();
-
-// In this example, `associatedCommentable` is the same thing as `image`:
-const isDeepEqual = require('deep-equal');
-console.log(isDeepEqual(image, commentable)); // true
-```
-
-## Configuring a One-to-Many polymorphic association
-
-To setup the polymorphic association for the example above (which is an example of One-to-Many polymorphic association), we have the following steps:
-
-* Define a string field called `commentableType` in the `Comment` model;
-* Define the `hasMany` and `belongsTo` association between `Image`/`Video` and `Comment`:
- * Disabling constraints (i.e. using `{ constraints: false }`), since the same foreign key is referencing multiple tables;
- * Specifying the appropriate [association scopes](./association-scopes.md);
-* To properly support lazy loading, define a new instance method on the `Comment` model called `getCommentable` which calls, under the hood, the correct mixin to fetch the appropriate commentable;
-* To properly support eager loading, define an `afterFind` hook on the `Comment` model that automatically populates the `commentable` field in every instance;
-* To prevent bugs/mistakes in eager loading, you can also delete the concrete fields `image` and `video` from Comment instances in the same `afterFind` hook, leaving only the abstract `commentable` field available.
-
-Here is an example:
-
-```js
-// Helper function
-const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`;
-
-class Image extends Model {}
-Image.init({
- title: DataTypes.STRING,
- url: DataTypes.STRING
-}, { sequelize, modelName: 'image' });
-
-class Video extends Model {}
-Video.init({
- title: DataTypes.STRING,
- text: DataTypes.STRING
-}, { sequelize, modelName: 'video' });
-
-class Comment extends Model {
- getCommentable(options) {
- if (!this.commentableType) return Promise.resolve(null);
- const mixinMethodName = `get${uppercaseFirst(this.commentableType)}`;
- return this[mixinMethodName](options);
- }
-}
-Comment.init({
- title: DataTypes.STRING,
- commentableId: DataTypes.INTEGER,
- commentableType: DataTypes.STRING
-}, { sequelize, modelName: 'comment' });
-
-Image.hasMany(Comment, {
- foreignKey: 'commentableId',
- constraints: false,
- scope: {
- commentableType: 'image'
- }
-});
-Comment.belongsTo(Image, { foreignKey: 'commentableId', constraints: false });
-
-Video.hasMany(Comment, {
- foreignKey: 'commentableId',
- constraints: false,
- scope: {
- commentableType: 'video'
- }
-});
-Comment.belongsTo(Video, { foreignKey: 'commentableId', constraints: false });
-
-Comment.addHook("afterFind", findResult => {
- if (!Array.isArray(findResult)) findResult = [findResult];
- for (const instance of findResult) {
- if (instance.commentableType === "image" && instance.image !== undefined) {
- instance.commentable = instance.image;
- } else if (instance.commentableType === "video" && instance.video !== undefined) {
- instance.commentable = instance.video;
- }
- // To prevent mistakes:
- delete instance.image;
- delete instance.dataValues.image;
- delete instance.video;
- delete instance.dataValues.video;
- }
-});
-```
-
-Since the `commentableId` column references several tables (two in this case), we cannot add a `REFERENCES` constraint to it. This is why the `constraints: false` option was used.
-
-Note that, in the code above:
-
-* The *Image -> Comment* association defined an association scope: `{ commentableType: 'image' }`
-* The *Video -> Comment* association defined an association scope: `{ commentableType: 'video' }`
-
-These scopes are automatically applied when using the association functions (as explained in the [Association Scopes](./association-scopes.md) guide). Some examples are below, with their generated SQL statements:
-
-* `image.getComments()`:
-
- ```sql
- SELECT "id", "title", "commentableType", "commentableId", "createdAt", "updatedAt"
- FROM "comments" AS "comment"
- WHERE "comment"."commentableType" = 'image' AND "comment"."commentableId" = 1;
- ```
-
- Here we can see that `` `comment`.`commentableType` = 'image'`` was automatically added to the `WHERE` clause of the generated SQL. This is exactly the behavior we want.
-
-* `image.createComment({ title: 'Awesome!' })`:
-
- ```sql
- INSERT INTO "comments" (
- "id", "title", "commentableType", "commentableId", "createdAt", "updatedAt"
- ) VALUES (
- DEFAULT, 'Awesome!', 'image', 1,
- '2018-04-17 05:36:40.454 +00:00', '2018-04-17 05:36:40.454 +00:00'
- ) RETURNING *;
- ```
-
-* `image.addComment(comment)`:
-
- ```sql
- UPDATE "comments"
- SET "commentableId"=1, "commentableType"='image', "updatedAt"='2018-04-17 05:38:43.948 +00:00'
- WHERE "id" IN (1)
- ```
-
-### Polymorphic lazy loading
-
-The `getCommentable` instance method on `Comment` provides an abstraction for lazy loading the associated commentable - working whether the comment belongs to an Image or a Video.
-
-It works by simply converting the `commentableType` string into a call to the correct mixin (either `getImage` or `getVideo`).
-
-Note that the `getCommentable` implementation above:
-
-* Returns `null` when no association is present (which is good);
-* Allows you to pass an options object to `getCommentable(options)`, just like any other standard Sequelize method. This is useful to specify where-conditions or includes, for example.
-
-### Polymorphic eager loading
-
-Now, we want to perform a polymorphic eager loading of the associated commentables for one (or more) comments. We want to achieve something similar to the following idea:
-
-```js
-const comment = await Comment.findOne({
- include: [ /* What to put here? */ ]
-});
-console.log(comment.commentable); // This is our goal
-```
-
-The solution is to tell Sequelize to include both Images and Videos, so that our `afterFind` hook defined above will do the work, automatically adding the `commentable` field to the instance object, providing the abstraction we want.
-
-For example:
-
-```js
-const comments = await Comment.findAll({
- include: [Image, Video]
-});
-for (const comment of comments) {
- const message = `Found comment #${comment.id} with ${comment.commentableType} commentable:`;
- console.log(message, comment.commentable.toJSON());
-}
-```
-
-Output example:
-
-```text
-Found comment #1 with image commentable: { id: 1,
- title: 'Meow',
- url: 'https://placekitten.com/408/287',
- createdAt: 2019-12-26T15:04:53.047Z,
- updatedAt: 2019-12-26T15:04:53.047Z }
-```
-
-### Caution - possibly invalid eager/lazy loading!
-
-Consider a comment `Foo` whose `commentableId` is 2 and `commentableType` is `image`. Consider also that `Image A` and `Video X` both happen to have an id equal to 2. Conceptually, it is clear that `Video X` is not associated to `Foo`, because even though its id is 2, the `commentableType` of `Foo` is `image`, not `video`. However, this distinction is made by Sequelize only at the level of the abstractions performed by `getCommentable` and the hook we created above.
-
-This means that if you call `Comment.findAll({ include: Video })` in the situation above, `Video X` will be eager loaded into `Foo`. Thankfully, our `afterFind` hook will delete it automatically, to help prevent bugs, but regardless it is important that you understand what is going on.
-
-The best way to prevent this kind of mistake is to **avoid using the concrete accessors and mixins directly at all costs** (such as `.image`, `.getVideo()`, `.setImage()`, etc), always preferring the abstractions we created, such as `.getCommentable()` and `.commentable`. If you really need to access eager-loaded `.image` and `.video` for some reason, make sure you wrap that in a type check such as `comment.commentableType === 'image'`.
-
-## Configuring a Many-to-Many polymorphic association
-
-In the above example, we had the models `Image` and `Video` being abstractly called *commentables*, with one *commentable* having many comments. However, one given comment would belong to a single *commentable* - this is why the whole situation is a One-to-Many polymorphic association.
-
-Now, to consider a Many-to-Many polymorphic association, instead of considering comments, we will consider tags. For convenience, instead of calling Image and Video as *commentables*, we will now call them *taggables*. One *taggable* may have several tags, and at the same time one tag can be placed in several *taggables*.
-
-The setup for this goes as follows:
-
-* Define the junction model explicitly, specifying the two foreign keys as `tagId` and `taggableId` (this way it is a junction model for a Many-to-Many relationship between `Tag` and the abstract concept of *taggable*);
-* Define a string field called `taggableType` in the junction model;
-* Define the `belongsToMany` associations between the two models and `Tag`:
- * Disabling constraints (i.e. using `{ constraints: false }`), since the same foreign key is referencing multiple tables;
- * Specifying the appropriate [association scopes](./association-scopes.md);
-* Define a new instance method on the `Tag` model called `getTaggables` which calls, under the hood, the correct mixin to fetch the appropriate taggables.
-
-Implementation:
-
-```js
-class Tag extends Model {
- async getTaggables(options) {
- const images = await this.getImages(options);
- const videos = await this.getVideos(options);
- // Concat images and videos in a single array of taggables
- return images.concat(videos);
- }
-}
-Tag.init({
- name: DataTypes.STRING
-}, { sequelize, modelName: 'tag' });
-
-// Here we define the junction model explicitly
-class Tag_Taggable extends Model {}
-Tag_Taggable.init({
- tagId: {
- type: DataTypes.INTEGER,
- unique: 'tt_unique_constraint'
- },
- taggableId: {
- type: DataTypes.INTEGER,
- unique: 'tt_unique_constraint',
- references: null
- },
- taggableType: {
- type: DataTypes.STRING,
- unique: 'tt_unique_constraint'
- }
-}, { sequelize, modelName: 'tag_taggable' });
-
-Image.belongsToMany(Tag, {
- through: {
- model: Tag_Taggable,
- unique: false,
- scope: {
- taggableType: 'image'
- }
- },
- foreignKey: 'taggableId',
- constraints: false
-});
-Tag.belongsToMany(Image, {
- through: {
- model: Tag_Taggable,
- unique: false
- },
- foreignKey: 'tagId',
- constraints: false
-});
-
-Video.belongsToMany(Tag, {
- through: {
- model: Tag_Taggable,
- unique: false,
- scope: {
- taggableType: 'video'
- }
- },
- foreignKey: 'taggableId',
- constraints: false
-});
-Tag.belongsToMany(Video, {
- through: {
- model: Tag_Taggable,
- unique: false
- },
- foreignKey: 'tagId',
- constraints: false
-});
-```
-
-The `constraints: false` option disables references constraints, as the `taggableId` column references several tables, we cannot add a `REFERENCES` constraint to it.
-
-Note that:
-
-* The *Image -> Tag* association defined an association scope: `{ taggableType: 'image' }`
-* The *Video -> Tag* association defined an association scope: `{ taggableType: 'video' }`
-
-These scopes are automatically applied when using the association functions. Some examples are below, with their generated SQL statements:
-
-* `image.getTags()`:
-
- ```sql
- SELECT
- `tag`.`id`,
- `tag`.`name`,
- `tag`.`createdAt`,
- `tag`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
- FROM `tags` AS `tag`
- INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `tag`.`id` = `tag_taggable`.`tagId` AND
- `tag_taggable`.`taggableId` = 1 AND
- `tag_taggable`.`taggableType` = 'image';
- ```
-
- Here we can see that `` `tag_taggable`.`taggableType` = 'image'`` was automatically added to the `WHERE` clause of the generated SQL. This is exactly the behavior we want.
-
-* `tag.getTaggables()`:
-
- ```sql
- SELECT
- `image`.`id`,
- `image`.`url`,
- `image`.`createdAt`,
- `image`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
- FROM `images` AS `image`
- INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `image`.`id` = `tag_taggable`.`taggableId` AND
- `tag_taggable`.`tagId` = 1;
-
- SELECT
- `video`.`id`,
- `video`.`url`,
- `video`.`createdAt`,
- `video`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
- FROM `videos` AS `video`
- INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `video`.`id` = `tag_taggable`.`taggableId` AND
- `tag_taggable`.`tagId` = 1;
- ```
-
-Note that the above implementation of `getTaggables()` allows you to pass an options object to `getCommentable(options)`, just like any other standard Sequelize method. This is useful to specify where-conditions or includes, for example.
-
-### Applying scopes on the target model
-
-In the example above, the `scope` options (such as `scope: { taggableType: 'image' }`) were applied to the *through* model, not the *target* model, since it was used under the `through` option.
-
-We can also apply an association scope on the target model. We can even do both at the same time.
-
-To illustrate this, consider an extension of the above example between tags and taggables, where each tag has a status. This way, to get all pending tags of an image, we could establish another `belongsToMany` relationship between `Image` and `Tag`, this time applying a scope on the through model and another scope on the target model:
-
-```js
-Image.belongsToMany(Tag, {
- through: {
- model: Tag_Taggable,
- unique: false,
- scope: {
- taggableType: 'image'
- }
- },
- scope: {
- status: 'pending'
- },
- as: 'pendingTags',
- foreignKey: 'taggableId',
- constraints: false
-});
-```
-
-This way, when calling `image.getPendingTags()`, the following SQL query will be generated:
-
-```sql
-SELECT
- `tag`.`id`,
- `tag`.`name`,
- `tag`.`status`,
- `tag`.`createdAt`,
- `tag`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
-FROM `tags` AS `tag`
-INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `tag`.`id` = `tag_taggable`.`tagId` AND
- `tag_taggable`.`taggableId` = 1 AND
- `tag_taggable`.`taggableType` = 'image'
-WHERE (
- `tag`.`status` = 'pending'
-);
-```
-
-We can see that both scopes were applied automatically:
-
-* `` `tag_taggable`.`taggableType` = 'image'`` was added automatically to the `INNER JOIN`;
-* `` `tag`.`status` = 'pending'`` was added automatically to an outer where clause.
+---
+sidebar_position: 5
+title: Polymorphic Associations
+---
+
+_**Note:** the usage of polymorphic associations in Sequelize, as outlined in this guide, should be done with caution. Don't just copy-paste code from here, otherwise you might easily make mistakes and introduce bugs in your code. Make sure you understand what is going on._
+
+## Concept
+
+A **polymorphic association** consists on two (or more) associations happening with the same foreign key.
+
+For example, consider the models `Image`, `Video` and `Comment`. The first two represent something that a user might post. We want to allow comments to be placed in both of them. This way, we immediately think of establishing the following associations:
+
+- A One-to-Many association between `Image` and `Comment`:
+
+ ```js
+ Image.hasMany(Comment);
+ Comment.belongsTo(Image);
+ ```
+
+- A One-to-Many association between `Video` and `Comment`:
+
+ ```js
+ Video.hasMany(Comment);
+ Comment.belongsTo(Video);
+ ```
+
+However, the above would cause Sequelize to create two foreign keys on the `Comment` table: `ImageId` and `VideoId`. This is not ideal because this structure makes it look like a comment can be attached at the same time to one image and one video, which isn't true. Instead, what we really want here is precisely a polymorphic association, in which a `Comment` points to a single **Commentable**, an abstract polymorphic entity that represents one of `Image` or `Video`.
+
+Before proceeding to how to configure such an association, let's see how using it looks like:
+
+```js
+const image = await Image.create({ url: 'https://placekitten.com/408/287' });
+const comment = await image.createComment({ content: 'Awesome!' });
+
+console.log(comment.commentableId === image.id); // true
+
+// We can also retrieve which type of commentable a comment is associated to.
+// The following prints the model name of the associated commentable instance.
+console.log(comment.commentableType); // "Image"
+
+// We can use a polymorphic method to retrieve the associated commentable, without
+// having to worry whether it's an Image or a Video.
+const associatedCommentable = await comment.getCommentable();
+
+// In this example, `associatedCommentable` is the same thing as `image`:
+const isDeepEqual = require('deep-equal');
+console.log(isDeepEqual(image, commentable)); // true
+```
+
+## Configuring a One-to-Many polymorphic association
+
+To setup the polymorphic association for the example above (which is an example of One-to-Many polymorphic association), we have the following steps:
+
+- Define a string field called `commentableType` in the `Comment` model;
+- Define the `hasMany` and `belongsTo` association between `Image`/`Video` and `Comment`:
+ - Disabling constraints (i.e. using `{ constraints: false }`), since the same foreign key is referencing multiple tables;
+ - Specifying the appropriate [association scopes](./association-scopes.md);
+- To properly support lazy loading, define a new instance method on the `Comment` model called `getCommentable` which calls, under the hood, the correct mixin to fetch the appropriate commentable;
+- To properly support eager loading, define an `afterFind` hook on the `Comment` model that automatically populates the `commentable` field in every instance;
+- To prevent bugs/mistakes in eager loading, you can also delete the concrete fields `image` and `video` from Comment instances in the same `afterFind` hook, leaving only the abstract `commentable` field available.
+
+Here is an example:
+
+```js
+// Helper function
+const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`;
+
+class Image extends Model {}
+Image.init(
+ {
+ title: DataTypes.STRING,
+ url: DataTypes.STRING,
+ },
+ { sequelize, modelName: 'image' },
+);
+
+class Video extends Model {}
+Video.init(
+ {
+ title: DataTypes.STRING,
+ text: DataTypes.STRING,
+ },
+ { sequelize, modelName: 'video' },
+);
+
+class Comment extends Model {
+ getCommentable(options) {
+ if (!this.commentableType) return Promise.resolve(null);
+ const mixinMethodName = `get${uppercaseFirst(this.commentableType)}`;
+ return this[mixinMethodName](options);
+ }
+}
+Comment.init(
+ {
+ title: DataTypes.STRING,
+ commentableId: DataTypes.INTEGER,
+ commentableType: DataTypes.STRING,
+ },
+ { sequelize, modelName: 'comment' },
+);
+
+Image.hasMany(Comment, {
+ foreignKey: 'commentableId',
+ constraints: false,
+ scope: {
+ commentableType: 'image',
+ },
+});
+Comment.belongsTo(Image, { foreignKey: 'commentableId', constraints: false });
+
+Video.hasMany(Comment, {
+ foreignKey: 'commentableId',
+ constraints: false,
+ scope: {
+ commentableType: 'video',
+ },
+});
+Comment.belongsTo(Video, { foreignKey: 'commentableId', constraints: false });
+
+Comment.addHook('afterFind', findResult => {
+ if (!Array.isArray(findResult)) findResult = [findResult];
+ for (const instance of findResult) {
+ if (instance.commentableType === 'image' && instance.image !== undefined) {
+ instance.commentable = instance.image;
+ } else if (instance.commentableType === 'video' && instance.video !== undefined) {
+ instance.commentable = instance.video;
+ }
+ // To prevent mistakes:
+ delete instance.image;
+ delete instance.dataValues.image;
+ delete instance.video;
+ delete instance.dataValues.video;
+ }
+});
+```
+
+Since the `commentableId` column references several tables (two in this case), we cannot add a `REFERENCES` constraint to it. This is why the `constraints: false` option was used.
+
+Note that, in the code above:
+
+- The _Image -> Comment_ association defined an association scope: `{ commentableType: 'image' }`
+- The _Video -> Comment_ association defined an association scope: `{ commentableType: 'video' }`
+
+These scopes are automatically applied when using the association functions (as explained in the [Association Scopes](./association-scopes.md) guide). Some examples are below, with their generated SQL statements:
+
+- `image.getComments()`:
+
+ ```sql
+ SELECT "id", "title", "commentableType", "commentableId", "createdAt", "updatedAt"
+ FROM "comments" AS "comment"
+ WHERE "comment"."commentableType" = 'image' AND "comment"."commentableId" = 1;
+ ```
+
+ Here we can see that `` `comment`.`commentableType` = 'image'`` was automatically added to the `WHERE` clause of the generated SQL. This is exactly the behavior we want.
+
+- `image.createComment({ title: 'Awesome!' })`:
+
+ ```sql
+ INSERT INTO "comments" (
+ "id", "title", "commentableType", "commentableId", "createdAt", "updatedAt"
+ ) VALUES (
+ DEFAULT, 'Awesome!', 'image', 1,
+ '2018-04-17 05:36:40.454 +00:00', '2018-04-17 05:36:40.454 +00:00'
+ ) RETURNING *;
+ ```
+
+- `image.addComment(comment)`:
+
+ ```sql
+ UPDATE "comments"
+ SET "commentableId"=1, "commentableType"='image', "updatedAt"='2018-04-17 05:38:43.948 +00:00'
+ WHERE "id" IN (1)
+ ```
+
+### Polymorphic lazy loading
+
+The `getCommentable` instance method on `Comment` provides an abstraction for lazy loading the associated commentable - working whether the comment belongs to an Image or a Video.
+
+It works by simply converting the `commentableType` string into a call to the correct mixin (either `getImage` or `getVideo`).
+
+Note that the `getCommentable` implementation above:
+
+- Returns `null` when no association is present (which is good);
+- Allows you to pass an options object to `getCommentable(options)`, just like any other standard Sequelize method. This is useful to specify where-conditions or includes, for example.
+
+### Polymorphic eager loading
+
+Now, we want to perform a polymorphic eager loading of the associated commentables for one (or more) comments. We want to achieve something similar to the following idea:
+
+```js
+const comment = await Comment.findOne({
+ include: [
+ /* What to put here? */
+ ],
+});
+console.log(comment.commentable); // This is our goal
+```
+
+The solution is to tell Sequelize to include both Images and Videos, so that our `afterFind` hook defined above will do the work, automatically adding the `commentable` field to the instance object, providing the abstraction we want.
+
+For example:
+
+```js
+const comments = await Comment.findAll({
+ include: [Image, Video],
+});
+for (const comment of comments) {
+ const message = `Found comment #${comment.id} with ${comment.commentableType} commentable:`;
+ console.log(message, comment.commentable.toJSON());
+}
+```
+
+Output example:
+
+```text
+Found comment #1 with image commentable: { id: 1,
+ title: 'Meow',
+ url: 'https://placekitten.com/408/287',
+ createdAt: 2019-12-26T15:04:53.047Z,
+ updatedAt: 2019-12-26T15:04:53.047Z }
+```
+
+### Caution - possibly invalid eager/lazy loading!
+
+Consider a comment `Foo` whose `commentableId` is 2 and `commentableType` is `image`. Consider also that `Image A` and `Video X` both happen to have an id equal to 2. Conceptually, it is clear that `Video X` is not associated to `Foo`, because even though its id is 2, the `commentableType` of `Foo` is `image`, not `video`. However, this distinction is made by Sequelize only at the level of the abstractions performed by `getCommentable` and the hook we created above.
+
+This means that if you call `Comment.findAll({ include: Video })` in the situation above, `Video X` will be eager loaded into `Foo`. Thankfully, our `afterFind` hook will delete it automatically, to help prevent bugs, but regardless it is important that you understand what is going on.
+
+The best way to prevent this kind of mistake is to **avoid using the concrete accessors and mixins directly at all costs** (such as `.image`, `.getVideo()`, `.setImage()`, etc), always preferring the abstractions we created, such as `.getCommentable()` and `.commentable`. If you really need to access eager-loaded `.image` and `.video` for some reason, make sure you wrap that in a type check such as `comment.commentableType === 'image'`.
+
+## Configuring a Many-to-Many polymorphic association
+
+In the above example, we had the models `Image` and `Video` being abstractly called _commentables_, with one _commentable_ having many comments. However, one given comment would belong to a single _commentable_ - this is why the whole situation is a One-to-Many polymorphic association.
+
+Now, to consider a Many-to-Many polymorphic association, instead of considering comments, we will consider tags. For convenience, instead of calling Image and Video as _commentables_, we will now call them _taggables_. One _taggable_ may have several tags, and at the same time one tag can be placed in several _taggables_.
+
+The setup for this goes as follows:
+
+- Define the junction model explicitly, specifying the two foreign keys as `tagId` and `taggableId` (this way it is a junction model for a Many-to-Many relationship between `Tag` and the abstract concept of _taggable_);
+- Define a string field called `taggableType` in the junction model;
+- Define the `belongsToMany` associations between the two models and `Tag`:
+ - Disabling constraints (i.e. using `{ constraints: false }`), since the same foreign key is referencing multiple tables;
+ - Specifying the appropriate [association scopes](./association-scopes.md);
+- Define a new instance method on the `Tag` model called `getTaggables` which calls, under the hood, the correct mixin to fetch the appropriate taggables.
+
+Implementation:
+
+```js
+class Tag extends Model {
+ async getTaggables(options) {
+ const images = await this.getImages(options);
+ const videos = await this.getVideos(options);
+ // Concat images and videos in a single array of taggables
+ return images.concat(videos);
+ }
+}
+Tag.init(
+ {
+ name: DataTypes.STRING,
+ },
+ { sequelize, modelName: 'tag' },
+);
+
+// Here we define the junction model explicitly
+class Tag_Taggable extends Model {}
+Tag_Taggable.init(
+ {
+ tagId: {
+ type: DataTypes.INTEGER,
+ unique: 'tt_unique_constraint',
+ },
+ taggableId: {
+ type: DataTypes.INTEGER,
+ unique: 'tt_unique_constraint',
+ references: null,
+ },
+ taggableType: {
+ type: DataTypes.STRING,
+ unique: 'tt_unique_constraint',
+ },
+ },
+ { sequelize, modelName: 'tag_taggable' },
+);
+
+Image.belongsToMany(Tag, {
+ through: {
+ model: Tag_Taggable,
+ unique: false,
+ scope: {
+ taggableType: 'image',
+ },
+ },
+ foreignKey: 'taggableId',
+ constraints: false,
+});
+Tag.belongsToMany(Image, {
+ through: {
+ model: Tag_Taggable,
+ unique: false,
+ },
+ foreignKey: 'tagId',
+ constraints: false,
+});
+
+Video.belongsToMany(Tag, {
+ through: {
+ model: Tag_Taggable,
+ unique: false,
+ scope: {
+ taggableType: 'video',
+ },
+ },
+ foreignKey: 'taggableId',
+ constraints: false,
+});
+Tag.belongsToMany(Video, {
+ through: {
+ model: Tag_Taggable,
+ unique: false,
+ },
+ foreignKey: 'tagId',
+ constraints: false,
+});
+```
+
+The `constraints: false` option disables references constraints, as the `taggableId` column references several tables, we cannot add a `REFERENCES` constraint to it.
+
+Note that:
+
+- The _Image -> Tag_ association defined an association scope: `{ taggableType: 'image' }`
+- The _Video -> Tag_ association defined an association scope: `{ taggableType: 'video' }`
+
+These scopes are automatically applied when using the association functions. Some examples are below, with their generated SQL statements:
+
+- `image.getTags()`:
+
+ ```sql
+ SELECT
+ `tag`.`id`,
+ `tag`.`name`,
+ `tag`.`createdAt`,
+ `tag`.`updatedAt`,
+ `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
+ `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
+ `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
+ `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
+ `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
+ FROM `tags` AS `tag`
+ INNER JOIN `tag_taggables` AS `tag_taggable` ON
+ `tag`.`id` = `tag_taggable`.`tagId` AND
+ `tag_taggable`.`taggableId` = 1 AND
+ `tag_taggable`.`taggableType` = 'image';
+ ```
+
+ Here we can see that `` `tag_taggable`.`taggableType` = 'image'`` was automatically added to the `WHERE` clause of the generated SQL. This is exactly the behavior we want.
+
+- `tag.getTaggables()`:
+
+ ```sql
+ SELECT
+ `image`.`id`,
+ `image`.`url`,
+ `image`.`createdAt`,
+ `image`.`updatedAt`,
+ `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
+ `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
+ `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
+ `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
+ `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
+ FROM `images` AS `image`
+ INNER JOIN `tag_taggables` AS `tag_taggable` ON
+ `image`.`id` = `tag_taggable`.`taggableId` AND
+ `tag_taggable`.`tagId` = 1;
+
+ SELECT
+ `video`.`id`,
+ `video`.`url`,
+ `video`.`createdAt`,
+ `video`.`updatedAt`,
+ `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
+ `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
+ `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
+ `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
+ `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
+ FROM `videos` AS `video`
+ INNER JOIN `tag_taggables` AS `tag_taggable` ON
+ `video`.`id` = `tag_taggable`.`taggableId` AND
+ `tag_taggable`.`tagId` = 1;
+ ```
+
+Note that the above implementation of `getTaggables()` allows you to pass an options object to `getCommentable(options)`, just like any other standard Sequelize method. This is useful to specify where-conditions or includes, for example.
+
+### Applying scopes on the target model
+
+In the example above, the `scope` options (such as `scope: { taggableType: 'image' }`) were applied to the _through_ model, not the _target_ model, since it was used under the `through` option.
+
+We can also apply an association scope on the target model. We can even do both at the same time.
+
+To illustrate this, consider an extension of the above example between tags and taggables, where each tag has a status. This way, to get all pending tags of an image, we could establish another `belongsToMany` relationship between `Image` and `Tag`, this time applying a scope on the through model and another scope on the target model:
+
+```js
+Image.belongsToMany(Tag, {
+ through: {
+ model: Tag_Taggable,
+ unique: false,
+ scope: {
+ taggableType: 'image',
+ },
+ },
+ scope: {
+ status: 'pending',
+ },
+ as: 'pendingTags',
+ foreignKey: 'taggableId',
+ constraints: false,
+});
+```
+
+This way, when calling `image.getPendingTags()`, the following SQL query will be generated:
+
+```sql
+SELECT
+ `tag`.`id`,
+ `tag`.`name`,
+ `tag`.`status`,
+ `tag`.`createdAt`,
+ `tag`.`updatedAt`,
+ `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
+ `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
+ `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
+ `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
+ `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
+FROM `tags` AS `tag`
+INNER JOIN `tag_taggables` AS `tag_taggable` ON
+ `tag`.`id` = `tag_taggable`.`tagId` AND
+ `tag_taggable`.`taggableId` = 1 AND
+ `tag_taggable`.`taggableType` = 'image'
+WHERE (
+ `tag`.`status` = 'pending'
+);
+```
+
+We can see that both scopes were applied automatically:
+
+- `` `tag_taggable`.`taggableType` = 'image'`` was added automatically to the `INNER JOIN`;
+- `` `tag`.`status` = 'pending'`` was added automatically to an outer where clause.
diff --git a/versioned_docs/version-6.x.x/core-concepts/assocs.md b/versioned_docs/version-6.x.x/core-concepts/assocs.md
index b2035e31..dd49a975 100644
--- a/versioned_docs/version-6.x.x/core-concepts/assocs.md
+++ b/versioned_docs/version-6.x.x/core-concepts/assocs.md
@@ -7,10 +7,10 @@ Sequelize supports the standard associations: [One-To-One](https://en.wikipedia.
To do this, Sequelize provides **four** types of associations that should be combined to create them:
-* The `HasOne` association
-* The `BelongsTo` association
-* The `HasMany` association
-* The `BelongsToMany` association
+- The `HasOne` association
+- The `BelongsTo` association
+- The `HasMany` association
+- The `BelongsToMany` association
The guide will start explaining how to define these four types of associations, and then will follow up to explain how to combine those to define the three standard association types ([One-To-One](https://en.wikipedia.org/wiki/One-to-one_%28data_model%29), [One-To-Many](https://en.wikipedia.org/wiki/One-to-many_%28data_model%29) and [Many-To-Many](https://en.wikipedia.org/wiki/Many-to-many_%28data_model%29)).
@@ -19,8 +19,8 @@ The guide will start explaining how to define these four types of associations,
The four association types are defined in a very similar way. Let's say we have two models, `A` and `B`. Telling Sequelize that you want an association between the two needs just a function call:
```js
-const A = sequelize.define('A', /* ... */);
-const B = sequelize.define('B', /* ... */);
+const A = sequelize.define('A' /* ... */);
+const B = sequelize.define('B' /* ... */);
A.hasOne(B); // A HasOne B
A.belongsTo(B); // A BelongsTo B
@@ -31,10 +31,16 @@ A.belongsToMany(B, { through: 'C' }); // A BelongsToMany B through the junction
They all accept an options object as a second parameter (optional for the first three, mandatory for `belongsToMany` containing at least the `through` property):
```js
-A.hasOne(B, { /* options */ });
-A.belongsTo(B, { /* options */ });
-A.hasMany(B, { /* options */ });
-A.belongsToMany(B, { through: 'C', /* options */ });
+A.hasOne(B, {
+ /* options */
+});
+A.belongsTo(B, {
+ /* options */
+});
+A.hasMany(B, {
+ /* options */
+});
+A.belongsToMany(B, { through: 'C' /* options */ });
```
The order in which the association is defined is relevant. In other words, the order matters, for the four cases. In all examples above, `A` is called the **source** model and `B` is called the **target** model. This terminology is important.
@@ -49,7 +55,7 @@ These three calls will cause Sequelize to automatically add foreign keys to the
The `A.belongsToMany(B, { through: 'C' })` association means that a Many-To-Many relationship exists between `A` and `B`, using table `C` as [junction table](https://en.wikipedia.org/wiki/Associative_entity), which will have the foreign keys (`aId` and `bId`, for example). Sequelize will automatically create this model `C` (unless it already exists) and define the appropriate foreign keys on it.
-*Note: In the examples above for `belongsToMany`, a string (`'C'`) was passed to the through option. In this case, Sequelize automatically generates a model with this name. However, you can also pass a model directly, if you have already defined it.*
+_Note: In the examples above for `belongsToMany`, a string (`'C'`) was passed to the through option. In this case, Sequelize automatically generates a model with this name. However, you can also pass a model directly, if you have already defined it._
These are the main ideas involved in each type of association. However, these relationships are often used in pairs, in order to enable better usage with Sequelize. This will be seen later on.
@@ -57,10 +63,10 @@ These are the main ideas involved in each type of association. However, these re
As mentioned, usually the Sequelize associations are defined in pairs. In summary:
-* To create a **One-To-One** relationship, the `hasOne` and `belongsTo` associations are used together;
-* To create a **One-To-Many** relationship, the `hasMany` and `belongsTo` associations are used together;
-* To create a **Many-To-Many** relationship, two `belongsToMany` calls are used together.
- * Note: there is also a *Super Many-To-Many* relationship, which uses six associations at once, and will be discussed in the [Advanced Many-to-Many relationships guide](../advanced-association-concepts/advanced-many-to-many.md).
+- To create a **One-To-One** relationship, the `hasOne` and `belongsTo` associations are used together;
+- To create a **One-To-Many** relationship, the `hasMany` and `belongsTo` associations are used together;
+- To create a **Many-To-Many** relationship, two `belongsToMany` calls are used together.
+ - Note: there is also a _Super Many-To-Many_ relationship, which uses six associations at once, and will be discussed in the [Advanced Many-to-Many relationships guide](../advanced-association-concepts/advanced-many-to-many.md).
This will all be seen in detail next. The advantages of using these pairs instead of one single association will be discussed in the end of this chapter.
@@ -72,7 +78,7 @@ Before digging into the aspects of using Sequelize, it is useful to take a step
Let's say we have two models, `Foo` and `Bar`. We want to establish a One-To-One relationship between Foo and Bar. We know that in a relational database, this will be done by establishing a foreign key in one of the tables. So in this case, a very relevant question is: in which table do we want this foreign key to be? In other words, do we want `Foo` to have a `barId` column, or should `Bar` have a `fooId` column instead?
-In principle, both options are a valid way to establish a One-To-One relationship between Foo and Bar. However, when we say something like *"there is a One-To-One relationship between Foo and Bar"*, it is unclear whether or not the relationship is *mandatory* or optional. In other words, can a Foo exist without a Bar? Can a Bar exist without a Foo? The answers to these questions help figuring out where we want the foreign key column to be.
+In principle, both options are a valid way to establish a One-To-One relationship between Foo and Bar. However, when we say something like _"there is a One-To-One relationship between Foo and Bar"_, it is unclear whether or not the relationship is _mandatory_ or optional. In other words, can a Foo exist without a Bar? Can a Bar exist without a Foo? The answers to these questions help figuring out where we want the foreign key column to be.
### Goal
@@ -113,7 +119,7 @@ For example, to configure the `ON DELETE` and `ON UPDATE` behaviors, you can do:
```js
Foo.hasOne(Bar, {
onDelete: 'RESTRICT',
- onUpdate: 'RESTRICT'
+ onUpdate: 'RESTRICT',
});
Bar.belongsTo(Foo);
```
@@ -129,45 +135,45 @@ Both the `hasOne` and `belongsTo` calls shown above will infer that the foreign
```js
// Option 1
Foo.hasOne(Bar, {
- foreignKey: 'myFooId'
+ foreignKey: 'myFooId',
});
Bar.belongsTo(Foo);
// Option 2
Foo.hasOne(Bar, {
foreignKey: {
- name: 'myFooId'
- }
+ name: 'myFooId',
+ },
});
Bar.belongsTo(Foo);
// Option 3
Foo.hasOne(Bar);
Bar.belongsTo(Foo, {
- foreignKey: 'myFooId'
+ foreignKey: 'myFooId',
});
// Option 4
Foo.hasOne(Bar);
Bar.belongsTo(Foo, {
foreignKey: {
- name: 'myFooId'
- }
+ name: 'myFooId',
+ },
});
```
-As shown above, the `foreignKey` option accepts a string or an object. When receiving an object, this object will be used as the definition for the column just like it would do in a standard `sequelize.define` call. Therefore, specifying options such as `type`, `allowNull`, `defaultValue`, etc, just work.
+As shown above, the `foreignKey` option accepts a string or an object. When receiving an object, this object will be used as the definition for the column just like it would do in a standard `sequelize.define` call. Therefore, specifying options such as `type`, `allowNull`, `defaultValue`, etc, just work.
For example, to use `UUID` as the foreign key data type instead of the default (`INTEGER`), you can simply do:
```js
-const { DataTypes } = require("Sequelize");
+const { DataTypes } = require('Sequelize');
Foo.hasOne(Bar, {
foreignKey: {
// name: 'myFooId'
- type: DataTypes.UUID
- }
+ type: DataTypes.UUID,
+ },
});
Bar.belongsTo(Foo);
```
@@ -179,8 +185,8 @@ By default, the association is considered optional. In other words, in our examp
```js
Foo.hasOne(Bar, {
foreignKey: {
- allowNull: false
- }
+ allowNull: false,
+ },
});
// "fooId" INTEGER NOT NULL REFERENCES "foos" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT
```
@@ -227,7 +233,7 @@ The options to be applied in this case are the same from the One-To-One case. Fo
```js
Team.hasMany(Player, {
- foreignKey: 'clubId'
+ foreignKey: 'clubId',
});
Player.belongsTo(Team);
```
@@ -240,7 +246,7 @@ Like One-To-One relationships, `ON DELETE` defaults to `SET NULL` and `ON UPDATE
Many-To-Many associations connect one source with multiple targets, while all these targets can in turn be connected to other sources beyond the first.
-This cannot be represented by adding one foreign key to one of the tables, like the other relationships did. Instead, the concept of a [Junction Model](https://en.wikipedia.org/wiki/Associative_entity) is used. This will be an extra model (and extra table in the database) which will have two foreign key columns and will keep track of the associations. The junction table is also sometimes called *join table* or *through table*.
+This cannot be represented by adding one foreign key to one of the tables, like the other relationships did. Instead, the concept of a [Junction Model](https://en.wikipedia.org/wiki/Associative_entity) is used. This will be an extra model (and extra table in the database) which will have two foreign key columns and will keep track of the associations. The junction table is also sometimes called _join table_ or _through table_.
### Goal
@@ -279,16 +285,16 @@ const ActorMovies = sequelize.define('ActorMovies', {
type: DataTypes.INTEGER,
references: {
model: Movie, // 'Movies' would also work
- key: 'id'
- }
+ key: 'id',
+ },
},
ActorId: {
type: DataTypes.INTEGER,
references: {
model: Actor, // 'Actors' would also work
- key: 'id'
- }
- }
+ key: 'id',
+ },
+ },
});
Movie.belongsToMany(Actor, { through: ActorMovies });
Actor.belongsToMany(Movie, { through: ActorMovies });
@@ -311,32 +317,43 @@ CREATE TABLE IF NOT EXISTS "ActorMovies" (
Unlike One-To-One and One-To-Many relationships, the defaults for both `ON UPDATE` and `ON DELETE` are `CASCADE` for Many-To-Many relationships.
-Belongs-To-Many creates a unique key on through model. This unique key name can be overridden using **uniqueKey** option. To prevent creating this unique key, use the ***unique: false*** option.
+Belongs-To-Many creates a unique key on through model. This unique key name can be overridden using **uniqueKey** option. To prevent creating this unique key, use the **_unique: false_** option.
```js
-Project.belongsToMany(User, { through: UserProjects, uniqueKey: 'my_custom_unique' })
+Project.belongsToMany(User, {
+ through: UserProjects,
+ uniqueKey: 'my_custom_unique',
+});
```
## Basics of queries involving associations
-With the basics of defining associations covered, we can look at queries involving associations. The most common queries on this matter are the *read* queries (i.e. SELECTs). Later on, other types of queries will be shown.
+With the basics of defining associations covered, we can look at queries involving associations. The most common queries on this matter are the _read_ queries (i.e. SELECTs). Later on, other types of queries will be shown.
In order to study this, we will consider an example in which we have Ships and Captains, and a one-to-one relationship between them. We will allow null on foreign keys (the default), meaning that a Ship can exist without a Captain and vice-versa.
```js
// This is the setup of our models for the examples below
-const Ship = sequelize.define('ship', {
- name: DataTypes.TEXT,
- crewCapacity: DataTypes.INTEGER,
- amountOfSails: DataTypes.INTEGER
-}, { timestamps: false });
-const Captain = sequelize.define('captain', {
- name: DataTypes.TEXT,
- skillLevel: {
- type: DataTypes.INTEGER,
- validate: { min: 1, max: 10 }
- }
-}, { timestamps: false });
+const Ship = sequelize.define(
+ 'ship',
+ {
+ name: DataTypes.TEXT,
+ crewCapacity: DataTypes.INTEGER,
+ amountOfSails: DataTypes.INTEGER,
+ },
+ { timestamps: false },
+);
+const Captain = sequelize.define(
+ 'captain',
+ {
+ name: DataTypes.TEXT,
+ skillLevel: {
+ type: DataTypes.INTEGER,
+ validate: { min: 1, max: 10 },
+ },
+ },
+ { timestamps: false },
+);
Captain.hasOne(Ship);
Ship.belongsTo(Captain);
```
@@ -350,8 +367,8 @@ The concepts of Eager Loading and Lazy Loading are fundamental to understand how
```js
const awesomeCaptain = await Captain.findOne({
where: {
- name: "Jack Sparrow"
- }
+ name: 'Jack Sparrow',
+ },
});
// Do stuff with the fetched captain
console.log('Name:', awesomeCaptain.name);
@@ -372,9 +389,9 @@ Note: the `getShip()` instance method used above is one of the methods Sequelize
```js
const awesomeCaptain = await Captain.findOne({
where: {
- name: "Jack Sparrow"
+ name: 'Jack Sparrow',
},
- include: Ship
+ include: Ship,
});
// Now the ship comes with it
console.log('Name:', awesomeCaptain.name);
@@ -391,21 +408,21 @@ This was just a quick introduction to Eager Loading in Sequelize. There is a lot
The above showed the basics on queries for fetching data involving associations. For creating, updating and deleting, you can either:
-* Use the standard model queries directly:
+- Use the standard model queries directly:
```js
// Example: creating an associated model using the standard methods
Bar.create({
name: 'My Bar',
- fooId: 5
+ fooId: 5,
});
// This creates a Bar belonging to the Foo of ID 5 (since fooId is
// a regular column, after all). Nothing very clever going on here.
```
-* Or use the *[special methods/mixins](#special-methodsmixins-added-to-instances)* available for associated models, which are explained later on this page.
+- Or use the _[special methods/mixins](#special-methodsmixins-added-to-instances)_ available for associated models, which are explained later on this page.
-**Note:** The [`save()` instance method](pathname:///api/v6/class/src/model.js~Model.html#instance-method-save) is not aware of associations. In other words, if you change a value from a *child* object that was eager loaded along a *parent* object, calling `save()` on the parent will completely ignore the change that happened on the child.
+**Note:** The [`save()` instance method](pathname:///api/v6/class/src/model.js~Model.html#instance-method-save) is not aware of associations. In other words, if you change a value from a _child_ object that was eager loaded along a _parent_ object, calling `save()` on the parent will completely ignore the change that happened on the child.
## Association Aliases & Custom Foreign Keys
@@ -420,9 +437,9 @@ const Captain = sequelize.define('captain', { name: DataTypes.TEXT }, { timestam
There are three ways to specify a different name for the foreign key:
-* By providing the foreign key name directly
-* By defining an Alias
-* By doing both things
+- By providing the foreign key name directly
+- By defining an Alias
+- By doing both things
### Recap: the default setup
@@ -472,12 +489,16 @@ console.log((await Ship.findAll({ include: Captain })).toJSON()); // Throws an e
// Instead, you have to pass the alias:
console.log((await Ship.findAll({ include: 'leader' })).toJSON());
// Or you can pass an object specifying the model and alias:
-console.log((await Ship.findAll({
- include: {
- model: Captain,
- as: 'leader'
- }
-})).toJSON());
+console.log(
+ (
+ await Ship.findAll({
+ include: {
+ model: Captain,
+ as: 'leader',
+ },
+ })
+ ).toJSON(),
+);
// Also, instances obtain a `getLeader()` method for Lazy Loading:
const ship = await Ship.findOne();
@@ -500,12 +521,16 @@ console.log((await Ship.findAll({ include: Captain })).toJSON()); // Throws an e
// Instead, you have to pass the alias:
console.log((await Ship.findAll({ include: 'leader' })).toJSON());
// Or you can pass an object specifying the model and alias:
-console.log((await Ship.findAll({
- include: {
- model: Captain,
- as: 'leader'
- }
-})).toJSON());
+console.log(
+ (
+ await Ship.findAll({
+ include: {
+ model: Captain,
+ as: 'leader',
+ },
+ })
+ ).toJSON(),
+);
// Also, instances obtain a `getLeader()` method for Lazy Loading:
const ship = await Ship.findOne();
@@ -520,9 +545,9 @@ For example, if we have two models, `Foo` and `Bar`, and they are associated, th
### `Foo.hasOne(Bar)`
-* `fooInstance.getBar()`
-* `fooInstance.setBar()`
-* `fooInstance.createBar()`
+- `fooInstance.getBar()`
+- `fooInstance.setBar()`
+- `fooInstance.createBar()`
Example:
@@ -544,22 +569,22 @@ console.log(await foo.getBar()); // null
The same ones from `Foo.hasOne(Bar)`:
-* `fooInstance.getBar()`
-* `fooInstance.setBar()`
-* `fooInstance.createBar()`
+- `fooInstance.getBar()`
+- `fooInstance.setBar()`
+- `fooInstance.createBar()`
### `Foo.hasMany(Bar)`
-* `fooInstance.getBars()`
-* `fooInstance.countBars()`
-* `fooInstance.hasBar()`
-* `fooInstance.hasBars()`
-* `fooInstance.setBars()`
-* `fooInstance.addBar()`
-* `fooInstance.addBars()`
-* `fooInstance.removeBar()`
-* `fooInstance.removeBars()`
-* `fooInstance.createBar()`
+- `fooInstance.getBars()`
+- `fooInstance.countBars()`
+- `fooInstance.hasBar()`
+- `fooInstance.hasBars()`
+- `fooInstance.setBars()`
+- `fooInstance.addBar()`
+- `fooInstance.addBars()`
+- `fooInstance.removeBar()`
+- `fooInstance.removeBars()`
+- `fooInstance.createBar()`
Example:
@@ -589,44 +614,48 @@ The getter method accepts options just like the usual finder methods (such as `f
const easyTasks = await project.getTasks({
where: {
difficulty: {
- [Op.lte]: 5
- }
- }
+ [Op.lte]: 5,
+ },
+ },
});
-const taskTitles = (await project.getTasks({
- attributes: ['title'],
- raw: true
-})).map(task => task.title);
+const taskTitles = (
+ await project.getTasks({
+ attributes: ['title'],
+ raw: true,
+ })
+).map(task => task.title);
```
### `Foo.belongsToMany(Bar, { through: Baz })`
The same ones from `Foo.hasMany(Bar)`:
-* `fooInstance.getBars()`
-* `fooInstance.countBars()`
-* `fooInstance.hasBar()`
-* `fooInstance.hasBars()`
-* `fooInstance.setBars()`
-* `fooInstance.addBar()`
-* `fooInstance.addBars()`
-* `fooInstance.removeBar()`
-* `fooInstance.removeBars()`
-* `fooInstance.createBar()`
+- `fooInstance.getBars()`
+- `fooInstance.countBars()`
+- `fooInstance.hasBar()`
+- `fooInstance.hasBars()`
+- `fooInstance.setBars()`
+- `fooInstance.addBar()`
+- `fooInstance.addBars()`
+- `fooInstance.removeBar()`
+- `fooInstance.removeBars()`
+- `fooInstance.createBar()`
For belongsToMany relationships, by default `getBars()` will return all fields from the join table. Note that any `include` options will apply to the target `Bar` object, so trying to set options for the join table as you would when eager loading with `find` methods is not possible. To choose what attributes of the join table to include, `getBars()` supports a `joinTableAttributes` option that can be used similarly to setting `through.attributes` in an `include`. As an example, given Foo belongsToMany Bar, the following will both output results without join table fields:
```js
const foo = Foo.findByPk(id, {
- include: [{
- model: Bar,
- through: { attributes: [] }
- }]
-})
-console.log(foo.bars)
+ include: [
+ {
+ model: Bar,
+ through: { attributes: [] },
+ },
+ ],
+});
+console.log(foo.bars);
-const foo = Foo.findByPk(id)
-console.log(foo.getBars({ joinTableAttributes: [] }))
+const foo = Foo.findByPk(id);
+console.log(foo.getBars({ joinTableAttributes: [] }));
```
### Note: Method names
@@ -639,27 +668,27 @@ If an alias was defined, it will be used instead of the model name to form the m
Task.hasOne(User, { as: 'Author' });
```
-* `taskInstance.getAuthor()`
-* `taskInstance.setAuthor()`
-* `taskInstance.createAuthor()`
+- `taskInstance.getAuthor()`
+- `taskInstance.setAuthor()`
+- `taskInstance.createAuthor()`
## Why associations are defined in pairs?
As mentioned earlier and shown in most examples above, usually associations in Sequelize are defined in pairs:
-* To create a **One-To-One** relationship, the `hasOne` and `belongsTo` associations are used together;
-* To create a **One-To-Many** relationship, the `hasMany` and `belongsTo` associations are used together;
-* To create a **Many-To-Many** relationship, two `belongsToMany` calls are used together.
+- To create a **One-To-One** relationship, the `hasOne` and `belongsTo` associations are used together;
+- To create a **One-To-Many** relationship, the `hasMany` and `belongsTo` associations are used together;
+- To create a **Many-To-Many** relationship, two `belongsToMany` calls are used together.
-When a Sequelize association is defined between two models, only the *source* model *knows about it*. So, for example, when using `Foo.hasOne(Bar)` (so `Foo` is the source model and `Bar` is the target model), only `Foo` knows about the existence of this association. This is why in this case, as shown above, `Foo` instances gain the methods `getBar()`, `setBar()` and `createBar()`, while on the other hand `Bar` instances get nothing.
+When a Sequelize association is defined between two models, only the _source_ model _knows about it_. So, for example, when using `Foo.hasOne(Bar)` (so `Foo` is the source model and `Bar` is the target model), only `Foo` knows about the existence of this association. This is why in this case, as shown above, `Foo` instances gain the methods `getBar()`, `setBar()` and `createBar()`, while on the other hand `Bar` instances get nothing.
Similarly, for `Foo.hasOne(Bar)`, since `Foo` knows about the relationship, we can perform eager loading as in `Foo.findOne({ include: Bar })`, but we can't do `Bar.findOne({ include: Foo })`.
-Therefore, to bring full power to Sequelize usage, we usually setup the relationship in pairs, so that both models get to *know about it*.
+Therefore, to bring full power to Sequelize usage, we usually setup the relationship in pairs, so that both models get to _know about it_.
Practical demonstration:
-* If we do not define the pair of associations, calling for example just `Foo.hasOne(Bar)`:
+- If we do not define the pair of associations, calling for example just `Foo.hasOne(Bar)`:
```js
// This works...
@@ -670,7 +699,7 @@ Practical demonstration:
// SequelizeEagerLoadingError: foo is not associated to bar!
```
-* If we define the pair as recommended, i.e., both `Foo.hasOne(Bar)` and `Bar.belongsTo(Foo)`:
+- If we define the pair as recommended, i.e., both `Foo.hasOne(Bar)` and `Bar.belongsTo(Foo)`:
```js
// This works!
@@ -698,18 +727,22 @@ This other field must have a unique constraint on it (otherwise, it wouldn't mak
### For `belongsTo` relationships
-First, recall that the `A.belongsTo(B)` association places the foreign key in the *source model* (i.e., in `A`).
+First, recall that the `A.belongsTo(B)` association places the foreign key in the _source model_ (i.e., in `A`).
Let's again use the example of Ships and Captains. Additionally, we will assume that Captain names are unique:
```js
const Ship = sequelize.define('ship', { name: DataTypes.TEXT }, { timestamps: false });
-const Captain = sequelize.define('captain', {
- name: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
+const Captain = sequelize.define(
+ 'captain',
+ {
+ name: { type: DataTypes.TEXT, unique: true },
+ },
+ { timestamps: false },
+);
```
-This way, instead of keeping the `captainId` on our Ships, we could keep a `captainName` instead and use it as our association tracker. In other words, instead of referencing the `id` from the target model (Captain), our relationship will reference another column on the target model: the `name` column. To specify this, we have to define a *target key*. We will also have to specify a name for the foreign key itself:
+This way, instead of keeping the `captainId` on our Ships, we could keep a `captainName` instead and use it as our association tracker. In other words, instead of referencing the `id` from the target model (Captain), our relationship will reference another column on the target model: the `name` column. To specify this, we have to define a _target key_. We will also have to specify a name for the foreign key itself:
```js
Ship.belongsTo(Captain, { targetKey: 'name', foreignKey: 'captainName' });
@@ -720,8 +753,11 @@ Ship.belongsTo(Captain, { targetKey: 'name', foreignKey: 'captainName' });
Now we can do things like:
```js
-await Captain.create({ name: "Jack Sparrow" });
-const ship = await Ship.create({ name: "Black Pearl", captainName: "Jack Sparrow" });
+await Captain.create({ name: 'Jack Sparrow' });
+const ship = await Ship.create({
+ name: 'Black Pearl',
+ captainName: 'Jack Sparrow',
+});
console.log((await ship.getCaptain()).name); // "Jack Sparrow"
```
@@ -730,12 +766,20 @@ console.log((await ship.getCaptain()).name); // "Jack Sparrow"
The exact same idea can be applied to the `hasOne` and `hasMany` associations, but instead of providing a `targetKey`, we provide a `sourceKey` when defining the association. This is because unlike `belongsTo`, the `hasOne` and `hasMany` associations keep the foreign key on the target model:
```js
-const Foo = sequelize.define('foo', {
- name: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
-const Bar = sequelize.define('bar', {
- title: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
+const Foo = sequelize.define(
+ 'foo',
+ {
+ name: { type: DataTypes.TEXT, unique: true },
+ },
+ { timestamps: false },
+);
+const Bar = sequelize.define(
+ 'bar',
+ {
+ title: { type: DataTypes.TEXT, unique: true },
+ },
+ { timestamps: false },
+);
const Baz = sequelize.define('baz', { summary: DataTypes.TEXT }, { timestamps: false });
Foo.hasOne(Bar, { sourceKey: 'name', foreignKey: 'fooName' });
Bar.hasMany(Baz, { sourceKey: 'title', foreignKey: 'barTitle' });
@@ -751,41 +795,53 @@ The same idea can also be applied to `belongsToMany` relationships. However, unl
Consider the following setup:
```js
-const Foo = sequelize.define('foo', {
- name: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
-const Bar = sequelize.define('bar', {
- title: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
+const Foo = sequelize.define(
+ 'foo',
+ {
+ name: { type: DataTypes.TEXT, unique: true },
+ },
+ { timestamps: false },
+);
+const Bar = sequelize.define(
+ 'bar',
+ {
+ title: { type: DataTypes.TEXT, unique: true },
+ },
+ { timestamps: false },
+);
```
There are four cases to consider:
-* We might want a many-to-many relationship using the default primary keys for both `Foo` and `Bar`:
+- We might want a many-to-many relationship using the default primary keys for both `Foo` and `Bar`:
```js
Foo.belongsToMany(Bar, { through: 'foo_bar' });
// This creates a junction table `foo_bar` with fields `fooId` and `barId`
```
-* We might want a many-to-many relationship using the default primary key for `Foo` but a different field for `Bar`:
+- We might want a many-to-many relationship using the default primary key for `Foo` but a different field for `Bar`:
```js
Foo.belongsToMany(Bar, { through: 'foo_bar', targetKey: 'title' });
// This creates a junction table `foo_bar` with fields `fooId` and `barTitle`
```
-* We might want a many-to-many relationship using the a different field for `Foo` and the default primary key for `Bar`:
+- We might want a many-to-many relationship using the a different field for `Foo` and the default primary key for `Bar`:
```js
Foo.belongsToMany(Bar, { through: 'foo_bar', sourceKey: 'name' });
// This creates a junction table `foo_bar` with fields `fooName` and `barId`
```
-* We might want a many-to-many relationship using different fields for both `Foo` and `Bar`:
+- We might want a many-to-many relationship using different fields for both `Foo` and `Bar`:
```js
-Foo.belongsToMany(Bar, { through: 'foo_bar', sourceKey: 'name', targetKey: 'title' });
+Foo.belongsToMany(Bar, {
+ through: 'foo_bar',
+ sourceKey: 'name',
+ targetKey: 'title',
+});
// This creates a junction table `foo_bar` with fields `fooName` and `barTitle`
```
@@ -795,8 +851,8 @@ Don't forget that the field referenced in the association must have a unique con
The trick to deciding between `sourceKey` and `targetKey` is just to remember where each relationship places its foreign key. As mentioned in the beginning of this guide:
-* `A.belongsTo(B)` keeps the foreign key in the source model (`A`), therefore the referenced key is in the target model, hence the usage of `targetKey`.
+- `A.belongsTo(B)` keeps the foreign key in the source model (`A`), therefore the referenced key is in the target model, hence the usage of `targetKey`.
-* `A.hasOne(B)` and `A.hasMany(B)` keep the foreign key in the target model (`B`), therefore the referenced key is in the source model, hence the usage of `sourceKey`.
+- `A.hasOne(B)` and `A.hasMany(B)` keep the foreign key in the target model (`B`), therefore the referenced key is in the source model, hence the usage of `sourceKey`.
-* `A.belongsToMany(B)` involves an extra table (the junction table), therefore both `sourceKey` and `targetKey` are usable, with `sourceKey` corresponding to some field in `A` (the source) and `targetKey` corresponding to some field in `B` (the target).
+- `A.belongsToMany(B)` involves an extra table (the junction table), therefore both `sourceKey` and `targetKey` are usable, with `sourceKey` corresponding to some field in `A` (the source) and `targetKey` corresponding to some field in `B` (the target).
diff --git a/versioned_docs/version-6.x.x/core-concepts/getters-setters-virtuals.md b/versioned_docs/version-6.x.x/core-concepts/getters-setters-virtuals.md
index 4efe7193..21aee0aa 100644
--- a/versioned_docs/version-6.x.x/core-concepts/getters-setters-virtuals.md
+++ b/versioned_docs/version-6.x.x/core-concepts/getters-setters-virtuals.md
@@ -1,210 +1,217 @@
----
-sidebar_position: 5
-title: Getters, Setters & Virtuals
----
-
-Sequelize allows you to define custom getters and setters for the attributes of your models.
-
-Sequelize also allows you to specify the so-called *virtual attributes*, which are attributes on the Sequelize Model that doesn't really exist in the underlying SQL table, but instead are populated automatically by Sequelize. They are very useful to create custom attributes which also could simplify your code, for example.
-
-## Getters
-
-A getter is a `get()` function defined for one column in the model definition:
-
-```js
-const User = sequelize.define('user', {
- // Let's say we wanted to see every username in uppercase, even
- // though they are not necessarily uppercase in the database itself
- username: {
- type: DataTypes.STRING,
- get() {
- const rawValue = this.getDataValue('username');
- return rawValue ? rawValue.toUpperCase() : null;
- }
- }
-});
-```
-
-This getter, just like a standard JavaScript getter, is called automatically when the field value is read:
-
-```js
-const user = User.build({ username: 'SuperUser123' });
-console.log(user.username); // 'SUPERUSER123'
-console.log(user.getDataValue('username')); // 'SuperUser123'
-```
-
-Note that, although `SUPERUSER123` was logged above, the value truly stored in the database is still `SuperUser123`. We used `this.getDataValue('username')` to obtain this value, and converted it to uppercase.
-
-Had we tried to use `this.username` in the getter instead, we would have gotten an infinite loop! This is why Sequelize provides the `getDataValue` method.
-
-## Setters
-
-A setter is a `set()` function defined for one column in the model definition. It receives the value being set:
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- password: {
- type: DataTypes.STRING,
- set(value) {
- // Storing passwords in plaintext in the database is terrible.
- // Hashing the value with an appropriate cryptographic hash function is better.
- this.setDataValue('password', hash(value));
- }
- }
-});
-```
-
-```js
-const user = User.build({ username: 'someone', password: 'NotSo§tr0ngP4$SW0RD!' });
-console.log(user.password); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
-console.log(user.getDataValue('password')); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
-```
-
-Observe that Sequelize called the setter automatically, before even sending data to the database. The only data the database ever saw was the already hashed value.
-
-If we wanted to involve another field from our model instance in the computation, that is possible and very easy!
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- password: {
- type: DataTypes.STRING,
- set(value) {
- // Storing passwords in plaintext in the database is terrible.
- // Hashing the value with an appropriate cryptographic hash function is better.
- // Using the username as a salt is better.
- this.setDataValue('password', hash(this.username + value));
- }
- }
-});
-```
-
-**Note:** The above examples involving password handling, although much better than simply storing the password in plaintext, are far from perfect security. Handling passwords properly is hard, everything here is just for the sake of an example to show Sequelize functionality. We suggest involving a cybersecurity expert and/or reading [OWASP](https://www.owasp.org/) documents and/or visiting the [InfoSec StackExchange](https://security.stackexchange.com/).
-
-## Combining getters and setters
-
-Getters and setters can be both defined in the same field.
-
-For the sake of an example, let's say we are modeling a `Post`, whose `content` is a text of unlimited length. To improve memory usage, let's say we want to store a gzipped version of the content.
-
-*Note: modern databases should do some compression automatically in these cases. Please note that this is just for the sake of an example.*
-
-```js
-const { gzipSync, gunzipSync } = require('zlib');
-
-const Post = sequelize.define('post', {
- content: {
- type: DataTypes.TEXT,
- get() {
- const storedValue = this.getDataValue('content');
- const gzippedBuffer = Buffer.from(storedValue, 'base64');
- const unzippedBuffer = gunzipSync(gzippedBuffer);
- return unzippedBuffer.toString();
- },
- set(value) {
- const gzippedBuffer = gzipSync(value);
- this.setDataValue('content', gzippedBuffer.toString('base64'));
- }
- }
-});
-```
-
-With the above setup, whenever we try to interact with the `content` field of our `Post` model, Sequelize will automatically handle the custom getter and setter. For example:
-
-```js
-const post = await Post.create({ content: 'Hello everyone!' });
-
-console.log(post.content); // 'Hello everyone!'
-// Everything is happening under the hood, so we can even forget that the
-// content is actually being stored as a gzipped base64 string!
-
-// However, if we are really curious, we can get the 'raw' data...
-console.log(post.getDataValue('content'));
-// Output: 'H4sIAAAAAAAACvNIzcnJV0gtSy2qzM9LVQQAUuk9jQ8AAAA='
-```
-
-## Virtual fields
-
-Virtual fields are fields that Sequelize populates under the hood, but in reality they don't even exist in the database.
-
-For example, let's say we have the `firstName` and `lastName` attributes for a User.
-
-*Again, this is [only for the sake of an example](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/).*
-
-It would be nice to have a simple way to obtain the *full name* directly! We can combine the idea of `getters` with the special data type Sequelize provides for this kind of situation: `DataTypes.VIRTUAL`:
-
-```js
-const { DataTypes } = require("sequelize");
-
-const User = sequelize.define('user', {
- firstName: DataTypes.TEXT,
- lastName: DataTypes.TEXT,
- fullName: {
- type: DataTypes.VIRTUAL,
- get() {
- return `${this.firstName} ${this.lastName}`;
- },
- set(value) {
- throw new Error('Do not try to set the `fullName` value!');
- }
- }
-});
-```
-
-The `VIRTUAL` field does not cause a column in the table to exist. In other words, the model above will not have a `fullName` column. However, it will appear to have it!
-
-```js
-const user = await User.create({ firstName: 'John', lastName: 'Doe' });
-console.log(user.fullName); // 'John Doe'
-```
-
-## Deprecated: `getterMethods` and `setterMethods`
-
-:::warning
-
-This feature has been removed in Sequelize 7. You should consider using either VIRTUAL attributes or native class getter & setters instead.
-
-:::
-
-Sequelize also provides the `getterMethods` and `setterMethods` options in the model definition to specify things that look like, but aren't exactly the same as, virtual attributes.
-
-Example:
-
-```js
-const { Sequelize, DataTypes } = require('sequelize');
-const sequelize = new Sequelize('sqlite::memory:');
-
-const User = sequelize.define('user', {
- firstName: DataTypes.STRING,
- lastName: DataTypes.STRING
-}, {
- getterMethods: {
- fullName() {
- return this.firstName + ' ' + this.lastName;
- }
- },
- setterMethods: {
- fullName(value) {
- // Note: this is just for demonstration.
- // See: https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
- const names = value.split(' ');
- const firstName = names[0];
- const lastName = names.slice(1).join(' ');
- this.setDataValue('firstName', firstName);
- this.setDataValue('lastName', lastName);
- }
- }
-});
-
-(async () => {
- await sequelize.sync();
- let user = await User.create({ firstName: 'John', lastName: 'Doe' });
- console.log(user.fullName); // 'John Doe'
- user.fullName = 'Someone Else';
- await user.save();
- user = await User.findOne();
- console.log(user.firstName); // 'Someone'
- console.log(user.lastName); // 'Else'
-})();
-```
+---
+sidebar_position: 5
+title: Getters, Setters & Virtuals
+---
+
+Sequelize allows you to define custom getters and setters for the attributes of your models.
+
+Sequelize also allows you to specify the so-called _virtual attributes_, which are attributes on the Sequelize Model that doesn't really exist in the underlying SQL table, but instead are populated automatically by Sequelize. They are very useful to create custom attributes which also could simplify your code, for example.
+
+## Getters
+
+A getter is a `get()` function defined for one column in the model definition:
+
+```js
+const User = sequelize.define('user', {
+ // Let's say we wanted to see every username in uppercase, even
+ // though they are not necessarily uppercase in the database itself
+ username: {
+ type: DataTypes.STRING,
+ get() {
+ const rawValue = this.getDataValue('username');
+ return rawValue ? rawValue.toUpperCase() : null;
+ },
+ },
+});
+```
+
+This getter, just like a standard JavaScript getter, is called automatically when the field value is read:
+
+```js
+const user = User.build({ username: 'SuperUser123' });
+console.log(user.username); // 'SUPERUSER123'
+console.log(user.getDataValue('username')); // 'SuperUser123'
+```
+
+Note that, although `SUPERUSER123` was logged above, the value truly stored in the database is still `SuperUser123`. We used `this.getDataValue('username')` to obtain this value, and converted it to uppercase.
+
+Had we tried to use `this.username` in the getter instead, we would have gotten an infinite loop! This is why Sequelize provides the `getDataValue` method.
+
+## Setters
+
+A setter is a `set()` function defined for one column in the model definition. It receives the value being set:
+
+```js
+const User = sequelize.define('user', {
+ username: DataTypes.STRING,
+ password: {
+ type: DataTypes.STRING,
+ set(value) {
+ // Storing passwords in plaintext in the database is terrible.
+ // Hashing the value with an appropriate cryptographic hash function is better.
+ this.setDataValue('password', hash(value));
+ },
+ },
+});
+```
+
+```js
+const user = User.build({
+ username: 'someone',
+ password: 'NotSo§tr0ngP4$SW0RD!',
+});
+console.log(user.password); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
+console.log(user.getDataValue('password')); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
+```
+
+Observe that Sequelize called the setter automatically, before even sending data to the database. The only data the database ever saw was the already hashed value.
+
+If we wanted to involve another field from our model instance in the computation, that is possible and very easy!
+
+```js
+const User = sequelize.define('user', {
+ username: DataTypes.STRING,
+ password: {
+ type: DataTypes.STRING,
+ set(value) {
+ // Storing passwords in plaintext in the database is terrible.
+ // Hashing the value with an appropriate cryptographic hash function is better.
+ // Using the username as a salt is better.
+ this.setDataValue('password', hash(this.username + value));
+ },
+ },
+});
+```
+
+**Note:** The above examples involving password handling, although much better than simply storing the password in plaintext, are far from perfect security. Handling passwords properly is hard, everything here is just for the sake of an example to show Sequelize functionality. We suggest involving a cybersecurity expert and/or reading [OWASP](https://www.owasp.org/) documents and/or visiting the [InfoSec StackExchange](https://security.stackexchange.com/).
+
+## Combining getters and setters
+
+Getters and setters can be both defined in the same field.
+
+For the sake of an example, let's say we are modeling a `Post`, whose `content` is a text of unlimited length. To improve memory usage, let's say we want to store a gzipped version of the content.
+
+_Note: modern databases should do some compression automatically in these cases. Please note that this is just for the sake of an example._
+
+```js
+const { gzipSync, gunzipSync } = require('zlib');
+
+const Post = sequelize.define('post', {
+ content: {
+ type: DataTypes.TEXT,
+ get() {
+ const storedValue = this.getDataValue('content');
+ const gzippedBuffer = Buffer.from(storedValue, 'base64');
+ const unzippedBuffer = gunzipSync(gzippedBuffer);
+ return unzippedBuffer.toString();
+ },
+ set(value) {
+ const gzippedBuffer = gzipSync(value);
+ this.setDataValue('content', gzippedBuffer.toString('base64'));
+ },
+ },
+});
+```
+
+With the above setup, whenever we try to interact with the `content` field of our `Post` model, Sequelize will automatically handle the custom getter and setter. For example:
+
+```js
+const post = await Post.create({ content: 'Hello everyone!' });
+
+console.log(post.content); // 'Hello everyone!'
+// Everything is happening under the hood, so we can even forget that the
+// content is actually being stored as a gzipped base64 string!
+
+// However, if we are really curious, we can get the 'raw' data...
+console.log(post.getDataValue('content'));
+// Output: 'H4sIAAAAAAAACvNIzcnJV0gtSy2qzM9LVQQAUuk9jQ8AAAA='
+```
+
+## Virtual fields
+
+Virtual fields are fields that Sequelize populates under the hood, but in reality they don't even exist in the database.
+
+For example, let's say we have the `firstName` and `lastName` attributes for a User.
+
+_Again, this is [only for the sake of an example](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/)._
+
+It would be nice to have a simple way to obtain the _full name_ directly! We can combine the idea of `getters` with the special data type Sequelize provides for this kind of situation: `DataTypes.VIRTUAL`:
+
+```js
+const { DataTypes } = require('sequelize');
+
+const User = sequelize.define('user', {
+ firstName: DataTypes.TEXT,
+ lastName: DataTypes.TEXT,
+ fullName: {
+ type: DataTypes.VIRTUAL,
+ get() {
+ return `${this.firstName} ${this.lastName}`;
+ },
+ set(value) {
+ throw new Error('Do not try to set the `fullName` value!');
+ },
+ },
+});
+```
+
+The `VIRTUAL` field does not cause a column in the table to exist. In other words, the model above will not have a `fullName` column. However, it will appear to have it!
+
+```js
+const user = await User.create({ firstName: 'John', lastName: 'Doe' });
+console.log(user.fullName); // 'John Doe'
+```
+
+## Deprecated: `getterMethods` and `setterMethods`
+
+:::warning
+
+This feature has been removed in Sequelize 7. You should consider using either VIRTUAL attributes or native class getter & setters instead.
+
+:::
+
+Sequelize also provides the `getterMethods` and `setterMethods` options in the model definition to specify things that look like, but aren't exactly the same as, virtual attributes.
+
+Example:
+
+```js
+const { Sequelize, DataTypes } = require('sequelize');
+const sequelize = new Sequelize('sqlite::memory:');
+
+const User = sequelize.define(
+ 'user',
+ {
+ firstName: DataTypes.STRING,
+ lastName: DataTypes.STRING,
+ },
+ {
+ getterMethods: {
+ fullName() {
+ return this.firstName + ' ' + this.lastName;
+ },
+ },
+ setterMethods: {
+ fullName(value) {
+ // Note: this is just for demonstration.
+ // See: https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
+ const names = value.split(' ');
+ const firstName = names[0];
+ const lastName = names.slice(1).join(' ');
+ this.setDataValue('firstName', firstName);
+ this.setDataValue('lastName', lastName);
+ },
+ },
+ },
+);
+
+(async () => {
+ await sequelize.sync();
+ let user = await User.create({ firstName: 'John', lastName: 'Doe' });
+ console.log(user.fullName); // 'John Doe'
+ user.fullName = 'Someone Else';
+ await user.save();
+ user = await User.findOne();
+ console.log(user.firstName); // 'Someone'
+ console.log(user.lastName); // 'Else'
+})();
+```
diff --git a/versioned_docs/version-6.x.x/core-concepts/model-basics.md b/versioned_docs/version-6.x.x/core-concepts/model-basics.md
index 5a7f49d4..9a32cfff 100644
--- a/versioned_docs/version-6.x.x/core-concepts/model-basics.md
+++ b/versioned_docs/version-6.x.x/core-concepts/model-basics.md
@@ -17,8 +17,8 @@ A model in Sequelize has a name. This name does not have to be the same name of
Models can be defined in two equivalent ways in Sequelize:
-* Calling [`sequelize.define(modelName, attributes, options)`](pathname:///api/v6/class/src/sequelize.js~Sequelize.html#instance-method-define)
-* Extending [Model](pathname:///api/v6/class/src/model.js~Model.html) and calling [`init(attributes, options)`](pathname:///api/v6/class/src/model.js~Model.html#static-method-init)
+- Calling [`sequelize.define(modelName, attributes, options)`](pathname:///api/v6/class/src/sequelize.js~Sequelize.html#instance-method-define)
+- Extending [Model](pathname:///api/v6/class/src/model.js~Model.html) and calling [`init(attributes, options)`](pathname:///api/v6/class/src/model.js~Model.html#static-method-init)
After a model is defined, it is available within `sequelize.models` by its model name.
@@ -32,19 +32,23 @@ Both ways to define this model are shown below. After being defined, we can acce
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
-const User = sequelize.define('User', {
- // Model attributes are defined here
- firstName: {
- type: DataTypes.STRING,
- allowNull: false
+const User = sequelize.define(
+ 'User',
+ {
+ // Model attributes are defined here
+ firstName: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ lastName: {
+ type: DataTypes.STRING,
+ // allowNull defaults to true
+ },
},
- lastName: {
- type: DataTypes.STRING
- // allowNull defaults to true
- }
-}, {
- // Other model options go here
-});
+ {
+ // Other model options go here
+ },
+);
// `sequelize.define` also returns the model
console.log(User === sequelize.models.User); // true
@@ -58,21 +62,24 @@ const sequelize = new Sequelize('sqlite::memory:');
class User extends Model {}
-User.init({
- // Model attributes are defined here
- firstName: {
- type: DataTypes.STRING,
- allowNull: false
+User.init(
+ {
+ // Model attributes are defined here
+ firstName: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ lastName: {
+ type: DataTypes.STRING,
+ // allowNull defaults to true
+ },
},
- lastName: {
- type: DataTypes.STRING
- // allowNull defaults to true
- }
-}, {
- // Other model options go here
- sequelize, // We need to pass the connection instance
- modelName: 'User' // We need to choose the model name
-});
+ {
+ // Other model options go here
+ sequelize, // We need to pass the connection instance
+ modelName: 'User', // We need to choose the model name
+ },
+);
// the defined model is the class itself
console.log(User === sequelize.models.User); // true
@@ -93,13 +100,16 @@ class User extends Model {
otherPublicField; // this field does not shadow anything. It is fine.
}
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- }
-}, { sequelize });
+User.init(
+ {
+ id: {
+ type: DataTypes.INTEGER,
+ autoIncrement: true,
+ primaryKey: true,
+ },
+ },
+ { sequelize },
+);
const user = new User({ id: 1 });
user.id; // undefined
@@ -111,13 +121,16 @@ class User extends Model {
otherPublicField;
}
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- }
-}, { sequelize });
+User.init(
+ {
+ id: {
+ type: DataTypes.INTEGER,
+ autoIncrement: true,
+ primaryKey: true,
+ },
+ },
+ { sequelize },
+);
const user = new User({ id: 1 });
user.id; // 1
@@ -131,13 +144,16 @@ class User extends Model {
declare id: number; // this is ok! The 'declare' keyword ensures this field will not be emitted by TypeScript.
}
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- }
-}, { sequelize });
+User.init(
+ {
+ id: {
+ type: DataTypes.INTEGER,
+ autoIncrement: true,
+ primaryKey: true,
+ },
+ },
+ { sequelize },
+);
const user = new User({ id: 1 });
user.id; // 1
@@ -156,11 +172,15 @@ Of course, this behavior is easily configurable.
You can stop the auto-pluralization performed by Sequelize using the `freezeTableName: true` option. This way, Sequelize will infer the table name to be equal to the model name, without any modifications:
```js
-sequelize.define('User', {
- // ... (attributes)
-}, {
- freezeTableName: true
-});
+sequelize.define(
+ 'User',
+ {
+ // ... (attributes)
+ },
+ {
+ freezeTableName: true,
+ },
+);
```
The example above will create a model named `User` pointing to a table also named `User`.
@@ -170,8 +190,8 @@ This behavior can also be defined globally for the sequelize instance, when it i
```js
const sequelize = new Sequelize('sqlite::memory:', {
define: {
- freezeTableName: true
- }
+ freezeTableName: true,
+ },
});
```
@@ -182,11 +202,15 @@ This way, all tables will use the same name as the model name.
You can simply tell Sequelize the name of the table directly as well:
```js
-sequelize.define('User', {
- // ... (attributes)
-}, {
- tableName: 'Employees'
-});
+sequelize.define(
+ 'User',
+ {
+ // ... (attributes)
+ },
+ {
+ tableName: 'Employees',
+ },
+);
```
## Model synchronization
@@ -195,15 +219,15 @@ When you define a model, you're telling Sequelize a few things about its table i
This is where model synchronization comes in. A model can be synchronized with the database by calling [`model.sync(options)`](https://sequelize.org/master/class/src/model.js~Model.html#static-method-sync), an asynchronous function (that returns a Promise). With this call, Sequelize will automatically perform an SQL query to the database. Note that this changes only the table in the database, not the model in the JavaScript side.
-* `User.sync()` - This creates the table if it doesn't exist (and does nothing if it already exists)
-* `User.sync({ force: true })` - This creates the table, dropping it first if it already existed
-* `User.sync({ alter: true })` - This checks what is the current state of the table in the database (which columns it has, what are their data types, etc), and then performs the necessary changes in the table to make it match the model.
+- `User.sync()` - This creates the table if it doesn't exist (and does nothing if it already exists)
+- `User.sync({ force: true })` - This creates the table, dropping it first if it already existed
+- `User.sync({ alter: true })` - This checks what is the current state of the table in the database (which columns it has, what are their data types, etc), and then performs the necessary changes in the table to make it match the model.
Example:
```js
await User.sync({ force: true });
-console.log("The table for the User model was just (re)created!");
+console.log('The table for the User model was just (re)created!');
```
### Synchronizing all models at once
@@ -212,7 +236,7 @@ You can use [`sequelize.sync()`](pathname:///api/v6/class/src/sequelize.js~Seque
```js
await sequelize.sync({ force: true });
-console.log("All models were synchronized successfully.");
+console.log('All models were synchronized successfully.');
```
### Dropping tables
@@ -221,14 +245,14 @@ To drop the table related to a model:
```js
await User.drop();
-console.log("User table dropped!");
+console.log('User table dropped!');
```
To drop all tables:
```js
await sequelize.drop();
-console.log("All tables dropped!");
+console.log('All tables dropped!');
```
### Database safety check
@@ -248,34 +272,43 @@ As shown above, `sync({ force: true })` and `sync({ alter: true })` can be destr
By default, Sequelize automatically adds the fields `createdAt` and `updatedAt` to every model, using the data type `DataTypes.DATE`. Those fields are automatically managed as well - whenever you use Sequelize to create or update something, those fields will be set correctly. The `createdAt` field will contain the timestamp representing the moment of creation, and the `updatedAt` will contain the timestamp of the latest update.
-**Note:** This is done in the Sequelize level (i.e. not done with *SQL triggers*). This means that direct SQL queries (for example queries performed without Sequelize by any other means) will not cause these fields to be updated automatically.
+**Note:** This is done in the Sequelize level (i.e. not done with _SQL triggers_). This means that direct SQL queries (for example queries performed without Sequelize by any other means) will not cause these fields to be updated automatically.
This behavior can be disabled for a model with the `timestamps: false` option:
```js
-sequelize.define('User', {
- // ... (attributes)
-}, {
- timestamps: false
-});
+sequelize.define(
+ 'User',
+ {
+ // ... (attributes)
+ },
+ {
+ timestamps: false,
+ },
+);
```
It is also possible to enable only one of `createdAt`/`updatedAt`, and to provide a custom name for these columns:
```js
class Foo extends Model {}
-Foo.init({ /* attributes */ }, {
- sequelize,
+Foo.init(
+ {
+ /* attributes */
+ },
+ {
+ sequelize,
- // don't forget to enable timestamps!
- timestamps: true,
+ // don't forget to enable timestamps!
+ timestamps: true,
- // I don't want createdAt
- createdAt: false,
+ // I don't want createdAt
+ createdAt: false,
- // I want updatedAt to actually be called updateTimestamp
- updatedAt: 'updateTimestamp'
-});
+ // I want updatedAt to actually be called updateTimestamp
+ updatedAt: 'updateTimestamp',
+ },
+);
```
## Column declaration shorthand syntax
@@ -286,8 +319,8 @@ If the only thing being specified about a column is its data type, the syntax ca
// This:
sequelize.define('User', {
name: {
- type: DataTypes.STRING
- }
+ type: DataTypes.STRING,
+ },
});
// Can be simplified to:
@@ -302,8 +335,8 @@ By default, Sequelize assumes that the default value of a column is `NULL`. This
sequelize.define('User', {
name: {
type: DataTypes.STRING,
- defaultValue: "John Doe"
- }
+ defaultValue: 'John Doe',
+ },
});
```
@@ -313,9 +346,9 @@ Some special values, such as `DataTypes.NOW`, are also accepted:
sequelize.define('Foo', {
bar: {
type: DataTypes.DATETIME,
- defaultValue: DataTypes.NOW
+ defaultValue: DataTypes.NOW,
// This way, the current date/time will be used to populate this column (at the moment of insertion)
- }
+ },
});
```
@@ -324,48 +357,48 @@ sequelize.define('Foo', {
Every column you define in your model must have a data type. Sequelize provides [a lot of built-in data types](https://github.com/sequelize/sequelize/blob/v6/src/data-types.js). To access a built-in data type, you must import `DataTypes`:
```js
-const { DataTypes } = require("sequelize"); // Import the built-in data types
+const { DataTypes } = require('sequelize'); // Import the built-in data types
```
### Strings
```js
-DataTypes.STRING // VARCHAR(255)
-DataTypes.STRING(1234) // VARCHAR(1234)
-DataTypes.STRING.BINARY // VARCHAR BINARY
-DataTypes.TEXT // TEXT
-DataTypes.TEXT('tiny') // TINYTEXT
-DataTypes.CITEXT // CITEXT PostgreSQL and SQLite only.
-DataTypes.TSVECTOR // TSVECTOR PostgreSQL only.
+DataTypes.STRING; // VARCHAR(255)
+DataTypes.STRING(1234); // VARCHAR(1234)
+DataTypes.STRING.BINARY; // VARCHAR BINARY
+DataTypes.TEXT; // TEXT
+DataTypes.TEXT('tiny'); // TINYTEXT
+DataTypes.CITEXT; // CITEXT PostgreSQL and SQLite only.
+DataTypes.TSVECTOR; // TSVECTOR PostgreSQL only.
```
### Boolean
```js
-DataTypes.BOOLEAN // TINYINT(1)
+DataTypes.BOOLEAN; // TINYINT(1)
```
### Numbers
```js
-DataTypes.INTEGER // INTEGER
-DataTypes.BIGINT // BIGINT
-DataTypes.BIGINT(11) // BIGINT(11)
+DataTypes.INTEGER; // INTEGER
+DataTypes.BIGINT; // BIGINT
+DataTypes.BIGINT(11); // BIGINT(11)
-DataTypes.FLOAT // FLOAT
-DataTypes.FLOAT(11) // FLOAT(11)
-DataTypes.FLOAT(11, 10) // FLOAT(11,10)
+DataTypes.FLOAT; // FLOAT
+DataTypes.FLOAT(11); // FLOAT(11)
+DataTypes.FLOAT(11, 10); // FLOAT(11,10)
-DataTypes.REAL // REAL PostgreSQL only.
-DataTypes.REAL(11) // REAL(11) PostgreSQL only.
-DataTypes.REAL(11, 12) // REAL(11,12) PostgreSQL only.
+DataTypes.REAL; // REAL PostgreSQL only.
+DataTypes.REAL(11); // REAL(11) PostgreSQL only.
+DataTypes.REAL(11, 12); // REAL(11,12) PostgreSQL only.
-DataTypes.DOUBLE // DOUBLE
-DataTypes.DOUBLE(11) // DOUBLE(11)
-DataTypes.DOUBLE(11, 10) // DOUBLE(11,10)
+DataTypes.DOUBLE; // DOUBLE
+DataTypes.DOUBLE(11); // DOUBLE(11)
+DataTypes.DOUBLE(11, 10); // DOUBLE(11,10)
-DataTypes.DECIMAL // DECIMAL
-DataTypes.DECIMAL(10, 2) // DECIMAL(10,2)
+DataTypes.DECIMAL; // DECIMAL
+DataTypes.DECIMAL(10, 2); // DECIMAL(10,2)
```
#### Unsigned & Zerofill integers - MySQL/MariaDB only
@@ -373,9 +406,9 @@ DataTypes.DECIMAL(10, 2) // DECIMAL(10,2)
In MySQL and MariaDB, the data types `INTEGER`, `BIGINT`, `FLOAT` and `DOUBLE` can be set as unsigned or zerofill (or both), as follows:
```js
-DataTypes.INTEGER.UNSIGNED
-DataTypes.INTEGER.ZEROFILL
-DataTypes.INTEGER.UNSIGNED.ZEROFILL
+DataTypes.INTEGER.UNSIGNED;
+DataTypes.INTEGER.ZEROFILL;
+DataTypes.INTEGER.UNSIGNED.ZEROFILL;
// You can also specify the size i.e. INTEGER(10) instead of simply INTEGER
// Same for BIGINT, FLOAT and DOUBLE
```
@@ -383,9 +416,9 @@ DataTypes.INTEGER.UNSIGNED.ZEROFILL
### Dates
```js
-DataTypes.DATE // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
-DataTypes.DATE(6) // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision
-DataTypes.DATEONLY // DATE without time
+DataTypes.DATE; // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
+DataTypes.DATE(6); // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision
+DataTypes.DATEONLY; // DATE without time
```
### UUIDs
@@ -408,71 +441,77 @@ There are other data types, covered in a [separate guide](../other-topics/other-
When defining a column, apart from specifying the `type` of the column, and the `allowNull` and `defaultValue` options mentioned above, there are a lot more options that can be used. Some examples are below.
```js
-const { Model, DataTypes, Deferrable } = require("sequelize");
+const { Model, DataTypes, Deferrable } = require('sequelize');
class Foo extends Model {}
-Foo.init({
- // instantiating will automatically set the flag to true if not set
- flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
-
- // default values for dates => current time
- myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
-
- // setting allowNull to false will add NOT NULL to the column, which means an error will be
- // thrown from the DB when the query is executed if the column is null. If you want to check that a value
- // is not null before querying the DB, look at the validations section below.
- title: { type: DataTypes.STRING, allowNull: false },
-
- // Creating two objects with the same value will throw an error. The unique property can be either a
- // boolean, or a string. If you provide the same string for multiple columns, they will form a
- // composite unique key.
- uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
- uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },
-
- // The unique property is simply a shorthand to create a unique constraint.
- someUnique: { type: DataTypes.STRING, unique: true },
-
- // Go on reading for further information about primary keys
- identifier: { type: DataTypes.STRING, primaryKey: true },
-
- // autoIncrement can be used to create auto_incrementing integer columns
- incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },
-
- // You can specify a custom column name via the 'field' attribute:
- fieldWithUnderscores: { type: DataTypes.STRING, field: 'field_with_underscores' },
-
- // It is possible to create foreign keys:
- bar_id: {
- type: DataTypes.INTEGER,
-
- references: {
- // This is a reference to another model
- model: Bar,
-
- // This is the column name of the referenced model
- key: 'id',
-
- // With PostgreSQL, it is optionally possible to declare when to check the foreign key constraint, passing the Deferrable type.
- deferrable: Deferrable.INITIALLY_IMMEDIATE
- // Options:
- // - `Deferrable.INITIALLY_IMMEDIATE` - Immediately check the foreign key constraints
- // - `Deferrable.INITIALLY_DEFERRED` - Defer all foreign key constraint check to the end of a transaction
- // - `Deferrable.NOT` - Don't defer the checks at all (default) - This won't allow you to dynamically change the rule in a transaction
- }
+Foo.init(
+ {
+ // instantiating will automatically set the flag to true if not set
+ flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
+
+ // default values for dates => current time
+ myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
+
+ // setting allowNull to false will add NOT NULL to the column, which means an error will be
+ // thrown from the DB when the query is executed if the column is null. If you want to check that a value
+ // is not null before querying the DB, look at the validations section below.
+ title: { type: DataTypes.STRING, allowNull: false },
+
+ // Creating two objects with the same value will throw an error. The unique property can be either a
+ // boolean, or a string. If you provide the same string for multiple columns, they will form a
+ // composite unique key.
+ uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
+ uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },
+
+ // The unique property is simply a shorthand to create a unique constraint.
+ someUnique: { type: DataTypes.STRING, unique: true },
+
+ // Go on reading for further information about primary keys
+ identifier: { type: DataTypes.STRING, primaryKey: true },
+
+ // autoIncrement can be used to create auto_incrementing integer columns
+ incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },
+
+ // You can specify a custom column name via the 'field' attribute:
+ fieldWithUnderscores: {
+ type: DataTypes.STRING,
+ field: 'field_with_underscores',
+ },
+
+ // It is possible to create foreign keys:
+ bar_id: {
+ type: DataTypes.INTEGER,
+
+ references: {
+ // This is a reference to another model
+ model: Bar,
+
+ // This is the column name of the referenced model
+ key: 'id',
+
+ // With PostgreSQL, it is optionally possible to declare when to check the foreign key constraint, passing the Deferrable type.
+ deferrable: Deferrable.INITIALLY_IMMEDIATE,
+ // Options:
+ // - `Deferrable.INITIALLY_IMMEDIATE` - Immediately check the foreign key constraints
+ // - `Deferrable.INITIALLY_DEFERRED` - Defer all foreign key constraint check to the end of a transaction
+ // - `Deferrable.NOT` - Don't defer the checks at all (default) - This won't allow you to dynamically change the rule in a transaction
+ },
+ },
+
+ // Comments can only be added to columns in MySQL, MariaDB, PostgreSQL and MSSQL
+ commentMe: {
+ type: DataTypes.INTEGER,
+ comment: 'This is a column name that has a comment',
+ },
},
+ {
+ sequelize,
+ modelName: 'foo',
- // Comments can only be added to columns in MySQL, MariaDB, PostgreSQL and MSSQL
- commentMe: {
- type: DataTypes.INTEGER,
- comment: 'This is a column name that has a comment'
- }
-}, {
- sequelize,
- modelName: 'foo',
-
- // Using `unique: true` in an attribute above is exactly the same as creating the index in the model's options:
- indexes: [{ unique: true, fields: ['someUnique'] }]
-});
+ // Using `unique: true` in an attribute above is exactly the same as creating the index in the model's options:
+ indexes: [{ unique: true, fields: ['someUnique'] }],
+ },
+);
```
## Taking advantage of Models being classes
@@ -491,10 +530,13 @@ class User extends Model {
return [this.firstname, this.lastname].join(' ');
}
}
-User.init({
- firstname: Sequelize.TEXT,
- lastname: Sequelize.TEXT
-}, { sequelize });
+User.init(
+ {
+ firstname: Sequelize.TEXT,
+ lastname: Sequelize.TEXT,
+ },
+ { sequelize },
+);
console.log(User.classLevelMethod()); // 'foo'
const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
diff --git a/versioned_docs/version-6.x.x/core-concepts/model-instances.md b/versioned_docs/version-6.x.x/core-concepts/model-instances.md
index 53cf388b..ac11e48c 100644
--- a/versioned_docs/version-6.x.x/core-concepts/model-instances.md
+++ b/versioned_docs/version-6.x.x/core-concepts/model-instances.md
@@ -8,17 +8,17 @@ As you already know, a model is an [ES6 class](https://developer.mozilla.org/en-
For this guide, the following setup will be assumed:
```js
-const { Sequelize, Model, DataTypes } = require("sequelize");
-const sequelize = new Sequelize("sqlite::memory:");
+const { Sequelize, Model, DataTypes } = require('sequelize');
+const sequelize = new Sequelize('sqlite::memory:');
-const User = sequelize.define("user", {
+const User = sequelize.define('user', {
name: DataTypes.TEXT,
favoriteColor: {
type: DataTypes.TEXT,
- defaultValue: 'green'
+ defaultValue: 'green',
},
age: DataTypes.INTEGER,
- cash: DataTypes.INTEGER
+ cash: DataTypes.INTEGER,
});
(async () => {
@@ -32,12 +32,12 @@ const User = sequelize.define("user", {
Although a model is a class, you should not create instances by using the `new` operator directly. Instead, the [`build`](pathname:///api/v6/class/src/model.js~Model.html#static-method-build) method should be used:
```js
-const jane = User.build({ name: "Jane" });
+const jane = User.build({ name: 'Jane' });
console.log(jane instanceof User); // true
console.log(jane.name); // "Jane"
```
-However, the code above does not communicate with the database at all (note that it is not even asynchronous)! This is because the [`build`](pathname:///api/v6/class/src/model.js~Model.html#static-method-build) method only creates an object that *represents* data that *can* be mapped to a database. In order to really save (i.e. persist) this instance in the database, the [`save`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-save) method should be used:
+However, the code above does not communicate with the database at all (note that it is not even asynchronous)! This is because the [`build`](pathname:///api/v6/class/src/model.js~Model.html#static-method-build) method only creates an object that _represents_ data that _can_ be mapped to a database. In order to really save (i.e. persist) this instance in the database, the [`save`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-save) method should be used:
```js
await jane.save();
@@ -51,7 +51,7 @@ Note, from the usage of `await` in the snippet above, that `save` is an asynchro
Sequelize provides the [`create`](pathname:///api/v6/class/src/model.js~Model.html#static-method-create) method, which combines the `build` and `save` methods shown above into a single method:
```js
-const jane = await User.create({ name: "Jane" });
+const jane = await User.create({ name: 'Jane' });
// Jane exists in the database now!
console.log(jane instanceof User); // true
console.log(jane.name); // "Jane"
@@ -62,7 +62,7 @@ console.log(jane.name); // "Jane"
Trying to log a model instance directly to `console.log` will produce a lot of clutter, since Sequelize instances have a lot of things attached to them. Instead, you can use the `.toJSON()` method (which, by the way, automatically guarantees the instances to be `JSON.stringify`-ed well).
```js
-const jane = await User.create({ name: "Jane" });
+const jane = await User.create({ name: 'Jane' });
// console.log(jane); // Don't do this
console.log(jane.toJSON()); // This is good!
console.log(JSON.stringify(jane, null, 4)); // This is also good!
@@ -73,7 +73,7 @@ console.log(JSON.stringify(jane, null, 4)); // This is also good!
Built instances will automatically get default values:
```js
-const jane = User.build({ name: "Jane" });
+const jane = User.build({ name: 'Jane' });
console.log(jane.favoriteColor); // "green"
```
@@ -82,9 +82,9 @@ console.log(jane.favoriteColor); // "green"
If you change the value of some field of an instance, calling `save` again will update it accordingly:
```js
-const jane = await User.create({ name: "Jane" });
+const jane = await User.create({ name: 'Jane' });
console.log(jane.name); // "Jane"
-jane.name = "Ada";
+jane.name = 'Ada';
// the name is still "Jane" in the database
await jane.save();
// Now the name was updated to "Ada" in the database!
@@ -93,11 +93,11 @@ await jane.save();
You can update several fields at once with the [`set`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-set) method:
```js
-const jane = await User.create({ name: "Jane" });
+const jane = await User.create({ name: 'Jane' });
jane.set({
- name: "Ada",
- favoriteColor: "blue"
+ name: 'Ada',
+ favoriteColor: 'blue',
});
// As above, the database still has "Jane" and "green"
await jane.save();
@@ -107,11 +107,11 @@ await jane.save();
Note that the `save()` here will also persist any other changes that have been made on this instance, not just those in the previous `set` call. If you want to update a specific set of fields, you can use [`update`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-update):
```js
-const jane = await User.create({ name: "Jane" });
-jane.favoriteColor = "blue"
-await jane.update({ name: "Ada" })
+const jane = await User.create({ name: 'Jane' });
+jane.favoriteColor = 'blue';
+await jane.update({ name: 'Ada' });
// The database now has "Ada" for name, but still has the default "green" for favorite color
-await jane.save()
+await jane.save();
// Now the database has "Ada" for name and "blue" for favorite color
```
@@ -120,7 +120,7 @@ await jane.save()
You can delete an instance by calling [`destroy`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-destroy):
```js
-const jane = await User.create({ name: "Jane" });
+const jane = await User.create({ name: 'Jane' });
console.log(jane.name); // "Jane"
await jane.destroy();
// Now this entry was removed from the database
@@ -131,9 +131,9 @@ await jane.destroy();
You can reload an instance from the database by calling [`reload`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-reload):
```js
-const jane = await User.create({ name: "Jane" });
+const jane = await User.create({ name: 'Jane' });
console.log(jane.name); // "Jane"
-jane.name = "Ada";
+jane.name = 'Ada';
// the name is still "Jane" in the database
await jane.reload();
console.log(jane.name); // "Jane"
@@ -148,11 +148,11 @@ It is possible to define which attributes should be saved when calling `save`, b
This is useful when you set attributes based on a previously defined object, for example, when you get the values of an object via a form of a web app. Furthermore, this is used internally in the `update` implementation. This is how it looks like:
```js
-const jane = await User.create({ name: "Jane" });
+const jane = await User.create({ name: 'Jane' });
console.log(jane.name); // "Jane"
console.log(jane.favoriteColor); // "green"
-jane.name = "Jane II";
-jane.favoriteColor = "blue";
+jane.name = 'Jane II';
+jane.favoriteColor = 'blue';
await jane.save({ fields: ['name'] });
console.log(jane.name); // "Jane II"
console.log(jane.favoriteColor); // "blue"
@@ -174,7 +174,7 @@ Also, if only a few attributes have changed when you call `save`, only those fie
In order to increment/decrement values of an instance without running into concurrency issues, Sequelize provides the [`increment`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-increment) and [`decrement`](pathname:///api/v6/class/src/model.js~Model.html#instance-method-decrement) instance methods.
```js
-const jane = await User.create({ name: "Jane", age: 100 });
+const jane = await User.create({ name: 'Jane', age: 100 });
const incrementResult = await jane.increment('age', { by: 2 });
// Note: to increment by 1 you can omit the `by` option and just do `user.increment('age')`
@@ -187,10 +187,10 @@ const incrementResult = await jane.increment('age', { by: 2 });
You can also increment multiple fields at once:
```js
-const jane = await User.create({ name: "Jane", age: 100, cash: 5000 });
+const jane = await User.create({ name: 'Jane', age: 100, cash: 5000 });
await jane.increment({
- 'age': 2,
- 'cash': 100
+ age: 2,
+ cash: 100,
});
// If the values are incremented by the same amount, you can use this other syntax as well:
diff --git a/versioned_docs/version-6.x.x/core-concepts/model-querying-basics.md b/versioned_docs/version-6.x.x/core-concepts/model-querying-basics.md
index 03d0fdcb..ece61885 100644
--- a/versioned_docs/version-6.x.x/core-concepts/model-querying-basics.md
+++ b/versioned_docs/version-6.x.x/core-concepts/model-querying-basics.md
@@ -5,7 +5,7 @@ title: Model Querying - Basics
Sequelize provides various methods to assist querying your database for data.
-*Important notice: to perform production-ready queries with Sequelize, make sure you have read the [Transactions guide](../other-topics/transactions.md) as well. Transactions are important to ensure data integrity and to provide other benefits.*
+_Important notice: to perform production-ready queries with Sequelize, make sure you have read the [Transactions guide](../other-topics/transactions.md) as well. Transactions are important to ensure data integrity and to provide other benefits._
This guide will show how to make the standard [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) queries.
@@ -15,7 +15,7 @@ First, a simple example:
```js
// Create a new user
-const jane = await User.create({ firstName: "Jane", lastName: "Doe" });
+const jane = await User.create({ firstName: 'Jane', lastName: 'Doe' });
console.log("Jane's auto-generated ID:", jane.id);
```
@@ -24,10 +24,13 @@ The [`Model.create()`](pathname:///api/v6/class/src/model.js~Model.html#static-m
It is also possible to define which attributes can be set in the `create` method. This can be especially useful if you create database entries based on a form which can be filled by a user. Using that would, for example, allow you to restrict the `User` model to set only an username but not an admin flag (i.e., `isAdmin`):
```js
-const user = await User.create({
- username: 'alice123',
- isAdmin: true
-}, { fields: ['username'] });
+const user = await User.create(
+ {
+ username: 'alice123',
+ isAdmin: true,
+ },
+ { fields: ['username'] },
+);
// let's assume the default of isAdmin is false
console.log(user.username); // 'alice123'
console.log(user.isAdmin); // false
@@ -41,7 +44,7 @@ You can read the whole table from the database with the [`findAll`](pathname:///
// Find all users
const users = await User.findAll();
console.log(users.every(user => user instanceof User)); // true
-console.log("All users:", JSON.stringify(users, null, 2));
+console.log('All users:', JSON.stringify(users, null, 2));
```
```sql
@@ -54,7 +57,7 @@ To select only some attributes, you can use the `attributes` option:
```js
Model.findAll({
- attributes: ['foo', 'bar']
+ attributes: ['foo', 'bar'],
});
```
@@ -66,7 +69,7 @@ Attributes can be renamed using a nested array:
```js
Model.findAll({
- attributes: ['foo', ['bar', 'baz'], 'qux']
+ attributes: ['foo', ['bar', 'baz'], 'qux'],
});
```
@@ -78,11 +81,7 @@ You can use [`sequelize.fn`](pathname:///api/v6/class/src/sequelize.js~Sequelize
```js
Model.findAll({
- attributes: [
- 'foo',
- [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'],
- 'bar'
- ]
+ attributes: ['foo', [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'], 'bar'],
});
```
@@ -98,18 +97,21 @@ Sometimes it may be tiresome to list all the attributes of the model if you only
// This is a tiresome way of getting the number of hats (along with every column)
Model.findAll({
attributes: [
- 'id', 'foo', 'bar', 'baz', 'qux', 'hats', // We had to list all attributes...
- [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'] // To add the aggregation...
- ]
+ 'id',
+ 'foo',
+ 'bar',
+ 'baz',
+ 'qux',
+ 'hats', // We had to list all attributes...
+ [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'], // To add the aggregation...
+ ],
});
// This is shorter, and less error prone because it still works if you add / remove attributes from your model later
Model.findAll({
attributes: {
- include: [
- [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']
- ]
- }
+ include: [[sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']],
+ },
});
```
@@ -121,7 +123,7 @@ Similarly, it's also possible to remove a selected few attributes:
```js
Model.findAll({
- attributes: { exclude: ['baz'] }
+ attributes: { exclude: ['baz'] },
});
```
@@ -139,8 +141,8 @@ The `where` option is used to filter the query. There are lots of operators to u
```js
Post.findAll({
where: {
- authorId: 2
- }
+ authorId: 2,
+ },
});
// SELECT * FROM post WHERE authorId = 2;
```
@@ -148,13 +150,13 @@ Post.findAll({
Observe that no operator (from `Op`) was explicitly passed, so Sequelize assumed an equality comparison by default. The above code is equivalent to:
```js
-const { Op } = require("sequelize");
+const { Op } = require('sequelize');
Post.findAll({
where: {
authorId: {
- [Op.eq]: 2
- }
- }
+ [Op.eq]: 2,
+ },
+ },
});
// SELECT * FROM post WHERE authorId = 2;
```
@@ -165,8 +167,8 @@ Multiple checks can be passed:
Post.findAll({
where: {
authorId: 12,
- status: 'active'
- }
+ status: 'active',
+ },
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';
```
@@ -174,14 +176,11 @@ Post.findAll({
Just like Sequelize inferred the `Op.eq` operator in the first example, here Sequelize inferred that the caller wanted an `AND` for the two checks. The code above is equivalent to:
```js
-const { Op } = require("sequelize");
+const { Op } = require('sequelize');
Post.findAll({
where: {
- [Op.and]: [
- { authorId: 12 },
- { status: 'active' }
- ]
- }
+ [Op.and]: [{ authorId: 12 }, { status: 'active' }],
+ },
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';
```
@@ -189,14 +188,11 @@ Post.findAll({
An `OR` can be easily performed in a similar way:
```js
-const { Op } = require("sequelize");
+const { Op } = require('sequelize');
Post.findAll({
where: {
- [Op.or]: [
- { authorId: 12 },
- { authorId: 13 }
- ]
- }
+ [Op.or]: [{ authorId: 12 }, { authorId: 13 }],
+ },
});
// SELECT * FROM post WHERE authorId = 12 OR authorId = 13;
```
@@ -204,13 +200,13 @@ Post.findAll({
Since the above was an `OR` involving the same field, Sequelize allows you to use a slightly different structure which is more readable and generates the same behavior:
```js
-const { Op } = require("sequelize");
+const { Op } = require('sequelize');
Post.destroy({
where: {
authorId: {
- [Op.or]: [12, 13]
- }
- }
+ [Op.or]: [12, 13],
+ },
+ },
});
// DELETE FROM post WHERE authorId = 12 OR authorId = 13;
```
@@ -282,8 +278,8 @@ Passing an array directly to the `where` option will implicitly use the `IN` ope
```js
Post.findAll({
where: {
- id: [1,2,3] // Same as using `id: { [Op.in]: [1,2,3] }`
- }
+ id: [1, 2, 3], // Same as using `id: { [Op.in]: [1,2,3] }`
+ },
});
// SELECT ... FROM "posts" AS "post" WHERE "post"."id" IN (1, 2, 3);
```
@@ -341,14 +337,14 @@ Project.findAll({
where: {
name: 'Some Project',
[Op.not]: [
- { id: [1,2,3] },
+ { id: [1, 2, 3] },
{
description: {
- [Op.like]: 'Hello%'
- }
- }
- ]
- }
+ [Op.like]: 'Hello%',
+ },
+ },
+ ],
+ },
});
```
@@ -373,12 +369,12 @@ What if you wanted to obtain something like `WHERE char_length("content") = 7`?
```js
Post.findAll({
- where: sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7)
+ where: sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7),
});
// SELECT ... FROM "posts" AS "post" WHERE char_length("content") = 7
```
-Note the usage of the [`sequelize.fn`](pathname:///api/v6/class/src/sequelize.js~Sequelize.html#static-method-fn) and [`sequelize.col`](pathname:///api/v6/class/src/sequelize.js~Sequelize.html#static-method-col) methods, which should be used to specify an SQL function call and a table column, respectively. These methods should be used instead of passing a plain string (such as `char_length(content)`) because Sequelize needs to treat this situation differently (for example, using other symbol escaping approaches).
+Note the usage of the [`sequelize.fn`](pathname:///api/v6/class/src/sequelize.js~Sequelize.html#static-method-fn) and [`sequelize.col`](pathname:///api/v6/class/src/sequelize.js~Sequelize.html#static-method-col) methods, which should be used to specify an SQL function call and a table column, respectively. These methods should be used instead of passing a plain string (such as `char_length(content)`) because Sequelize needs to treat this situation differently (for example, using other symbol escaping approaches).
What if you need something even more complex?
@@ -389,19 +385,19 @@ Post.findAll({
sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7),
{
content: {
- [Op.like]: 'Hello%'
- }
+ [Op.like]: 'Hello%',
+ },
},
{
[Op.and]: [
{ status: 'draft' },
sequelize.where(sequelize.fn('char_length', sequelize.col('content')), {
- [Op.gt]: 10
- })
- ]
- }
- ]
- }
+ [Op.gt]: 10,
+ }),
+ ],
+ },
+ ],
+ },
});
```
@@ -448,18 +444,18 @@ In Sequelize v4, it was possible to specify strings to refer to operators, inste
For example:
```js
-const { Sequelize, Op } = require("sequelize");
+const { Sequelize, Op } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:', {
operatorsAliases: {
- $gt: Op.gt
- }
+ $gt: Op.gt,
+ },
});
// Now we can use `$gt` instead of `[Op.gt]` in where clauses:
Foo.findAll({
where: {
- $gt: 6 // Works like using [Op.gt]
- }
+ $gt: 6, // Works like using [Op.gt]
+ },
});
```
@@ -469,11 +465,14 @@ Update queries also accept the `where` option, just like the read queries shown
```js
// Change everyone without a last name to "Doe"
-await User.update({ lastName: "Doe" }, {
- where: {
- lastName: null
- }
-});
+await User.update(
+ { lastName: 'Doe' },
+ {
+ where: {
+ lastName: null,
+ },
+ },
+);
```
## Simple DELETE queries
@@ -484,8 +483,8 @@ Delete queries also accept the `where` option, just like the read queries shown
// Delete everyone named "Jane"
await User.destroy({
where: {
- firstName: "Jane"
- }
+ firstName: 'Jane',
+ },
});
```
@@ -494,7 +493,7 @@ To destroy everything the `TRUNCATE` SQL can be used:
```js
// Truncate the table
await User.destroy({
- truncate: true
+ truncate: true,
});
```
@@ -505,10 +504,7 @@ Sequelize provides the `Model.bulkCreate` method to allow creating multiple reco
The usage of `Model.bulkCreate` is very similar to `Model.create`, by receiving an array of objects instead of a single object.
```js
-const captains = await Captain.bulkCreate([
- { name: 'Jack Sparrow' },
- { name: 'Davy Jones' }
-]);
+const captains = await Captain.bulkCreate([{ name: 'Jack Sparrow' }, { name: 'Davy Jones' }]);
console.log(captains.length); // 2
console.log(captains[0] instanceof Captain); // true
console.log(captains[0].name); // 'Jack Sparrow'
@@ -522,31 +518,26 @@ const Foo = sequelize.define('foo', {
name: {
type: DataTypes.TEXT,
validate: {
- len: [4, 6]
- }
- }
+ len: [4, 6],
+ },
+ },
});
// This will not throw an error, both instances will be created
-await Foo.bulkCreate([
- { name: 'abc123' },
- { name: 'name too long' }
-]);
+await Foo.bulkCreate([{ name: 'abc123' }, { name: 'name too long' }]);
// This will throw an error, nothing will be created
-await Foo.bulkCreate([
- { name: 'abc123' },
- { name: 'name too long' }
-], { validate: true });
+await Foo.bulkCreate([{ name: 'abc123' }, { name: 'name too long' }], {
+ validate: true,
+});
```
If you are accepting values directly from the user, it might be beneficial to limit the columns that you want to actually insert. To support this, `bulkCreate()` accepts a `fields` option, an array defining which fields must be considered (the rest will be ignored).
```js
-await User.bulkCreate([
- { username: 'foo' },
- { username: 'bar', admin: true }
-], { fields: ['username'] });
+await User.bulkCreate([{ username: 'foo' }, { username: 'bar', admin: true }], {
+ fields: ['username'],
+});
// Neither foo nor bar are admins.
```
@@ -556,7 +547,7 @@ Sequelize provides the `order` and `group` options to work with `ORDER BY` and `
### Ordering
-The `order` option takes an array of items to order the query by or a sequelize method. These *items* are themselves arrays in the form `[column, direction]`. The column will be escaped correctly and the direction will be checked in a whitelist of valid directions (such as `ASC`, `DESC`, `NULLS FIRST`, etc).
+The `order` option takes an array of items to order the query by or a sequelize method. These _items_ are themselves arrays in the form `[column, direction]`. The column will be escaped correctly and the direction will be checked in a whitelist of valid directions (such as `ASC`, `DESC`, `NULLS FIRST`, etc).
```js
Subtask.findAll({
@@ -592,10 +583,10 @@ Subtask.findAll({
[Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],
// Will order by an associated model's createdAt using a simple association object.
- [{model: Task, as: 'Task'}, 'createdAt', 'DESC'],
+ [{ model: Task, as: 'Task' }, 'createdAt', 'DESC'],
// Will order by a nested associated model's createdAt simple association objects.
- [{model: Task, as: 'Task'}, {model: Project, as: 'Project'}, 'createdAt', 'DESC']
+ [{ model: Task, as: 'Task' }, { model: Project, as: 'Project' }, 'createdAt', 'DESC'],
],
// Will order by max age descending
@@ -608,7 +599,7 @@ Subtask.findAll({
order: sequelize.col('age'),
// Will order randomly based on the dialect (instead of fn('RAND') or fn('RANDOM'))
- order: sequelize.random()
+ order: sequelize.random(),
});
Foo.findOne({
@@ -624,20 +615,20 @@ Foo.findOne({
// will return otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// will return otherfunction(awesomefunction(`col`)) DESC, This nesting is potentially infinite!
- [sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC']
- ]
+ [sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC'],
+ ],
});
```
To recap, the elements of the order array can be the following:
-* A string (which will be automatically quoted)
-* An array, whose first element will be quoted, second will be appended verbatim
-* An object with a `raw` field:
- * The content of `raw` will be added verbatim without quoting
- * Everything else is ignored, and if raw is not set, the query will fail
-* A call to `Sequelize.fn` (which will generate a function call in SQL)
-* A call to `Sequelize.col` (which will quote the column name)
+- A string (which will be automatically quoted)
+- An array, whose first element will be quoted, second will be appended verbatim
+- An object with a `raw` field:
+ - The content of `raw` will be added verbatim without quoting
+ - Everything else is ignored, and if raw is not set, the query will fail
+- A call to `Sequelize.fn` (which will generate a function call in SQL)
+- A call to `Sequelize.col` (which will quote the column name)
### Grouping
@@ -681,9 +672,9 @@ console.log(`There are ${await Project.count()} projects`);
const amount = await Project.count({
where: {
id: {
- [Op.gt]: 25
- }
- }
+ [Op.gt]: 25,
+ },
+ },
});
console.log(`There are ${amount} projects with an id greater than 25`);
```
@@ -710,6 +701,6 @@ Sequelize also provides the `increment` convenience method.
Let's assume we have a user, whose age is 10.
```js
-await User.increment({age: 5}, { where: { id: 1 } }) // Will increase age to 15
-await User.increment({age: -5}, { where: { id: 1 } }) // Will decrease age to 5
+await User.increment({ age: 5 }, { where: { id: 1 } }); // Will increase age to 15
+await User.increment({ age: -5 }, { where: { id: 1 } }); // Will decrease age to 5
```
diff --git a/versioned_docs/version-6.x.x/core-concepts/model-querying-finders.md b/versioned_docs/version-6.x.x/core-concepts/model-querying-finders.md
index 2d32a339..dd162378 100644
--- a/versioned_docs/version-6.x.x/core-concepts/model-querying-finders.md
+++ b/versioned_docs/version-6.x.x/core-concepts/model-querying-finders.md
@@ -51,8 +51,8 @@ Let's assume we have an empty database with a `User` model which has a `username
const [user, created] = await User.findOrCreate({
where: { username: 'sdepold' },
defaults: {
- job: 'Technical Lead JavaScript'
- }
+ job: 'Technical Lead JavaScript',
+ },
});
console.log(user.username); // 'sdepold'
console.log(user.job); // This may or may not be 'Technical Lead JavaScript'
@@ -68,23 +68,23 @@ The `findAndCountAll` method is a convenience method that combines `findAll` and
When `group` is not provided, the `findAndCountAll` method returns an object with two properties:
-* `count` - an integer - the total number records matching the query
-* `rows` - an array of objects - the obtained records
+- `count` - an integer - the total number records matching the query
+- `rows` - an array of objects - the obtained records
When `group` is provided, the `findAndCountAll` method returns an object with two properties:
-* `count` - an array of objects - contains the count in each group and the projected attributes
-* `rows` - an array of objects - the obtained records
+- `count` - an array of objects - contains the count in each group and the projected attributes
+- `rows` - an array of objects - the obtained records
```js
const { count, rows } = await Project.findAndCountAll({
where: {
title: {
- [Op.like]: 'foo%'
- }
+ [Op.like]: 'foo%',
+ },
},
offset: 10,
- limit: 2
+ limit: 2,
});
console.log(count);
console.log(rows);
diff --git a/versioned_docs/version-6.x.x/core-concepts/paranoid.md b/versioned_docs/version-6.x.x/core-concepts/paranoid.md
index 38664ecf..2b27ad63 100644
--- a/versioned_docs/version-6.x.x/core-concepts/paranoid.md
+++ b/versioned_docs/version-6.x.x/core-concepts/paranoid.md
@@ -1,108 +1,113 @@
----
-sidebar_position: 9
-title: Paranoid
----
-
-Sequelize supports the concept of *paranoid* tables. A *paranoid* table is one that, when told to delete a record, it will not truly delete it. Instead, a special column called `deletedAt` will have its value set to the timestamp of that deletion request.
-
-This means that paranoid tables perform a *soft-deletion* of records, instead of a *hard-deletion*.
-
-## Defining a model as paranoid
-
-To make a model paranoid, you must pass the `paranoid: true` option to the model definition. Paranoid requires timestamps to work (i.e. it won't work if you also pass `timestamps: false`).
-
-You can also change the default column name (which is `deletedAt`) to something else.
-
-```js
-class Post extends Model {}
-Post.init({ /* attributes here */ }, {
- sequelize,
- paranoid: true,
-
- // If you want to give a custom name to the deletedAt column
- deletedAt: 'destroyTime'
-});
-```
-
-## Deleting
-
-When you call the `destroy` method, a soft-deletion will happen:
-
-```js
-await Post.destroy({
- where: {
- id: 1
- }
-});
-// UPDATE "posts" SET "deletedAt"=[timestamp] WHERE "deletedAt" IS NULL AND "id" = 1
-```
-
-If you really want a hard-deletion and your model is paranoid, you can force it using the `force: true` option:
-
-```js
-await Post.destroy({
- where: {
- id: 1
- },
- force: true
-});
-// DELETE FROM "posts" WHERE "id" = 1
-```
-
-The above examples used the static `destroy` method as an example (`Post.destroy`), but everything works in the same way with the instance method:
-
-```js
-const post = await Post.create({ title: 'test' });
-console.log(post instanceof Post); // true
-await post.destroy(); // Would just set the `deletedAt` flag
-await post.destroy({ force: true }); // Would really delete the record
-```
-
-## Restoring
-
-To restore soft-deleted records, you can use the `restore` method, which comes both in the static version as well as in the instance version:
-
-```js
-// Example showing the instance `restore` method
-// We create a post, soft-delete it and then restore it back
-const post = await Post.create({ title: 'test' });
-console.log(post instanceof Post); // true
-await post.destroy();
-console.log('soft-deleted!');
-await post.restore();
-console.log('restored!');
-
-// Example showing the static `restore` method.
-// Restoring every soft-deleted post with more than 100 likes
-await Post.restore({
- where: {
- likes: {
- [Op.gt]: 100
- }
- }
-});
-```
-
-## Behavior with other queries
-
-Every query performed by Sequelize will automatically ignore soft-deleted records (except raw queries, of course).
-
-This means that, for example, the `findAll` method will not see the soft-deleted records, fetching only the ones that were not deleted.
-
-Even if you simply call `findByPk` providing the primary key of a soft-deleted record, the result will be `null` as if that record didn't exist.
-
-If you really want to let the query see the soft-deleted records, you can pass the `paranoid: false` option to the query method. For example:
-
-```js
-await Post.findByPk(123); // This will return `null` if the record of id 123 is soft-deleted
-await Post.findByPk(123, { paranoid: false }); // This will retrieve the record
-
-await Post.findAll({
- where: { foo: 'bar' }
-}); // This will not retrieve soft-deleted records
-
-await Post.findAll({
- where: { foo: 'bar' },
- paranoid: false
-}); // This will also retrieve soft-deleted records
-```
+---
+sidebar_position: 9
+title: Paranoid
+---
+
+Sequelize supports the concept of _paranoid_ tables. A _paranoid_ table is one that, when told to delete a record, it will not truly delete it. Instead, a special column called `deletedAt` will have its value set to the timestamp of that deletion request.
+
+This means that paranoid tables perform a _soft-deletion_ of records, instead of a _hard-deletion_.
+
+## Defining a model as paranoid
+
+To make a model paranoid, you must pass the `paranoid: true` option to the model definition. Paranoid requires timestamps to work (i.e. it won't work if you also pass `timestamps: false`).
+
+You can also change the default column name (which is `deletedAt`) to something else.
+
+```js
+class Post extends Model {}
+Post.init(
+ {
+ /* attributes here */
+ },
+ {
+ sequelize,
+ paranoid: true,
+
+ // If you want to give a custom name to the deletedAt column
+ deletedAt: 'destroyTime',
+ },
+);
+```
+
+## Deleting
+
+When you call the `destroy` method, a soft-deletion will happen:
+
+```js
+await Post.destroy({
+ where: {
+ id: 1,
+ },
+});
+// UPDATE "posts" SET "deletedAt"=[timestamp] WHERE "deletedAt" IS NULL AND "id" = 1
+```
+
+If you really want a hard-deletion and your model is paranoid, you can force it using the `force: true` option:
+
+```js
+await Post.destroy({
+ where: {
+ id: 1,
+ },
+ force: true,
+});
+// DELETE FROM "posts" WHERE "id" = 1
+```
+
+The above examples used the static `destroy` method as an example (`Post.destroy`), but everything works in the same way with the instance method:
+
+```js
+const post = await Post.create({ title: 'test' });
+console.log(post instanceof Post); // true
+await post.destroy(); // Would just set the `deletedAt` flag
+await post.destroy({ force: true }); // Would really delete the record
+```
+
+## Restoring
+
+To restore soft-deleted records, you can use the `restore` method, which comes both in the static version as well as in the instance version:
+
+```js
+// Example showing the instance `restore` method
+// We create a post, soft-delete it and then restore it back
+const post = await Post.create({ title: 'test' });
+console.log(post instanceof Post); // true
+await post.destroy();
+console.log('soft-deleted!');
+await post.restore();
+console.log('restored!');
+
+// Example showing the static `restore` method.
+// Restoring every soft-deleted post with more than 100 likes
+await Post.restore({
+ where: {
+ likes: {
+ [Op.gt]: 100,
+ },
+ },
+});
+```
+
+## Behavior with other queries
+
+Every query performed by Sequelize will automatically ignore soft-deleted records (except raw queries, of course).
+
+This means that, for example, the `findAll` method will not see the soft-deleted records, fetching only the ones that were not deleted.
+
+Even if you simply call `findByPk` providing the primary key of a soft-deleted record, the result will be `null` as if that record didn't exist.
+
+If you really want to let the query see the soft-deleted records, you can pass the `paranoid: false` option to the query method. For example:
+
+```js
+await Post.findByPk(123); // This will return `null` if the record of id 123 is soft-deleted
+await Post.findByPk(123, { paranoid: false }); // This will retrieve the record
+
+await Post.findAll({
+ where: { foo: 'bar' },
+}); // This will not retrieve soft-deleted records
+
+await Post.findAll({
+ where: { foo: 'bar' },
+ paranoid: false,
+}); // This will also retrieve soft-deleted records
+```
diff --git a/versioned_docs/version-6.x.x/core-concepts/raw-queries.md b/versioned_docs/version-6.x.x/core-concepts/raw-queries.md
index 678442a1..b192b47f 100644
--- a/versioned_docs/version-6.x.x/core-concepts/raw-queries.md
+++ b/versioned_docs/version-6.x.x/core-concepts/raw-queries.md
@@ -8,7 +8,7 @@ As there are often use cases in which it is just easier to execute raw / already
By default the function will return two arguments - a results array, and an object containing metadata (such as amount of affected rows, etc). Note that since this is a raw query, the metadata are dialect specific. Some dialects return the metadata "within" the results object (as properties on an array). However, two arguments will always be returned, but for MSSQL and MySQL it will be two references to the same object.
```js
-const [results, metadata] = await sequelize.query("UPDATE users SET y = 42 WHERE x = 12");
+const [results, metadata] = await sequelize.query('UPDATE users SET y = 42 WHERE x = 12');
// Results will be an empty array and metadata will contain the number of affected rows.
```
@@ -16,7 +16,9 @@ In cases where you don't need to access the metadata you can pass in a query typ
```js
const { QueryTypes } = require('sequelize');
-const users = await sequelize.query("SELECT * FROM `users`", { type: QueryTypes.SELECT });
+const users = await sequelize.query('SELECT * FROM `users`', {
+ type: QueryTypes.SELECT,
+});
// We didn't need to destructure the result here - the results were returned directly
```
@@ -28,7 +30,7 @@ A second option is the model. If you pass a model the returned data will be inst
// Callee is the model definition. This allows you to easily map a query to a predefined model
const projects = await sequelize.query('SELECT * FROM projects', {
model: Projects,
- mapToModel: true // pass true here if you have any mapped fields
+ mapToModel: true, // pass true here if you have any mapped fields
});
// Each element of `projects` is now an instance of Project
```
@@ -51,7 +53,7 @@ await sequelize.query('SELECT 1', {
raw: false,
// The type of query you are executing. The query type affects how results are formatted before they are passed back.
- type: QueryTypes.SELECT
+ type: QueryTypes.SELECT,
});
// Note the second argument being null!
@@ -64,12 +66,12 @@ console.log(await sequelize.query('SELECT * FROM projects', { raw: true }));
If an attribute name of the table contains dots, the resulting objects can become nested objects by setting the `nest: true` option. This is achieved with [dottie.js](https://github.com/mickhansen/dottie.js/) under the hood. See below:
-* Without `nest: true`:
+- Without `nest: true`:
```js
const { QueryTypes } = require('sequelize');
const records = await sequelize.query('select 1 as `foo.bar.baz`', {
- type: QueryTypes.SELECT
+ type: QueryTypes.SELECT,
});
console.log(JSON.stringify(records[0], null, 2));
```
@@ -80,13 +82,13 @@ If an attribute name of the table contains dots, the resulting objects can becom
}
```
-* With `nest: true`:
+- With `nest: true`:
```js
const { QueryTypes } = require('sequelize');
const records = await sequelize.query('select 1 as `foo.bar.baz`', {
nest: true,
- type: QueryTypes.SELECT
+ type: QueryTypes.SELECT,
});
console.log(JSON.stringify(records[0], null, 2));
```
@@ -105,27 +107,21 @@ If an attribute name of the table contains dots, the resulting objects can becom
Replacements in a query can be done in two different ways, either using named parameters (starting with `:`), or unnamed, represented by a `?`. Replacements are passed in the options object.
-* If an array is passed, `?` will be replaced in the order that they appear in the array
-* If an object is passed, `:key` will be replaced with the keys from that object. If the object contains keys not found in the query or vice versa, an exception will be thrown.
+- If an array is passed, `?` will be replaced in the order that they appear in the array
+- If an object is passed, `:key` will be replaced with the keys from that object. If the object contains keys not found in the query or vice versa, an exception will be thrown.
```js
const { QueryTypes } = require('sequelize');
-await sequelize.query(
- 'SELECT * FROM projects WHERE status = ?',
- {
- replacements: ['active'],
- type: QueryTypes.SELECT
- }
-);
+await sequelize.query('SELECT * FROM projects WHERE status = ?', {
+ replacements: ['active'],
+ type: QueryTypes.SELECT,
+});
-await sequelize.query(
- 'SELECT * FROM projects WHERE status = :status',
- {
- replacements: { status: 'active' },
- type: QueryTypes.SELECT
- }
-);
+await sequelize.query('SELECT * FROM projects WHERE status = :status', {
+ replacements: { status: 'active' },
+ type: QueryTypes.SELECT,
+});
```
Array replacements will automatically be handled, the following query searches for projects where the status matches an array of values.
@@ -133,13 +129,10 @@ Array replacements will automatically be handled, the following query searches f
```js
const { QueryTypes } = require('sequelize');
-await sequelize.query(
- 'SELECT * FROM projects WHERE status IN(:status)',
- {
- replacements: { status: ['active', 'inactive'] },
- type: QueryTypes.SELECT
- }
-);
+await sequelize.query('SELECT * FROM projects WHERE status IN(:status)', {
+ replacements: { status: ['active', 'inactive'] },
+ type: QueryTypes.SELECT,
+});
```
To use the wildcard operator `%`, append it to your replacement. The following query matches users with names that start with 'ben'.
@@ -147,22 +140,19 @@ To use the wildcard operator `%`, append it to your replacement. The following q
```js
const { QueryTypes } = require('sequelize');
-await sequelize.query(
- 'SELECT * FROM users WHERE name LIKE :search_name',
- {
- replacements: { search_name: 'ben%' },
- type: QueryTypes.SELECT
- }
-);
+await sequelize.query('SELECT * FROM users WHERE name LIKE :search_name', {
+ replacements: { search_name: 'ben%' },
+ type: QueryTypes.SELECT,
+});
```
## Bind Parameter
Bind parameters are like replacements. Except replacements are escaped and inserted into the query by sequelize before the query is sent to the database, while bind parameters are sent to the database outside the SQL query text. A query can have either bind parameters or replacements. Bind parameters are referred to by either $1, $2, ... (numeric) or $key (alpha-numeric). This is independent of the dialect.
-* If an array is passed, `$1` is bound to the 1st element in the array (`bind[0]`)
-* If an object is passed, `$key` is bound to `object['key']`. Each key must begin with a non-numeric char. `$1` is not a valid key, even if `object['1']` exists.
-* In either case `$$` can be used to escape a literal `$` sign.
+- If an array is passed, `$1` is bound to the 1st element in the array (`bind[0]`)
+- If an object is passed, `$key` is bound to `object['key']`. Each key must begin with a non-numeric char. `$1` is not a valid key, even if `object['1']` exists.
+- In either case `$$` can be used to escape a literal `$` sign.
The array or object must contain all bound values or Sequelize will throw an exception. This applies even to cases in which the database may ignore the bound parameter.
@@ -175,15 +165,15 @@ await sequelize.query(
'SELECT *, "text with literal $$1 and literal $$status" as t FROM projects WHERE status = $1',
{
bind: ['active'],
- type: QueryTypes.SELECT
- }
+ type: QueryTypes.SELECT,
+ },
);
await sequelize.query(
'SELECT *, "text with literal $$1 and literal $$status" as t FROM projects WHERE status = $status',
{
bind: { status: 'active' },
- type: QueryTypes.SELECT
- }
+ type: QueryTypes.SELECT,
+ },
);
```
diff --git a/versioned_docs/version-6.x.x/core-concepts/validations-and-constraints.md b/versioned_docs/version-6.x.x/core-concepts/validations-and-constraints.md
index 0f8bf1ef..0447a0f4 100644
--- a/versioned_docs/version-6.x.x/core-concepts/validations-and-constraints.md
+++ b/versioned_docs/version-6.x.x/core-concepts/validations-and-constraints.md
@@ -1,275 +1,287 @@
----
-sidebar_position: 6
-title: Validations & Constraints
----
-
-In this tutorial you will learn how to setup validations and constraints for your models in Sequelize.
-
-For this tutorial, the following setup will be assumed:
-
-```js
-const { Sequelize, Op, Model, DataTypes } = require("sequelize");
-const sequelize = new Sequelize("sqlite::memory:");
-
-const User = sequelize.define("user", {
- username: {
- type: DataTypes.TEXT,
- allowNull: false,
- unique: true
- },
- hashedPassword: {
- type: DataTypes.STRING(64),
- validate: {
- is: /^[0-9a-f]{64}$/i
- }
- }
-});
-
-(async () => {
- await sequelize.sync({ force: true });
- // Code here
-})();
-```
-
-## Difference between Validations and Constraints
-
-Validations are checks performed in the Sequelize level, in pure JavaScript. They can be arbitrarily complex if you provide a custom validator function, or can be one of the built-in validators offered by Sequelize. If a validation fails, no SQL query will be sent to the database at all.
-
-On the other hand, constraints are rules defined at SQL level. The most basic example of constraint is an Unique Constraint. If a constraint check fails, an error will be thrown by the database and Sequelize will forward this error to JavaScript (in this example, throwing a `SequelizeUniqueConstraintError`). Note that in this case, the SQL query was performed, unlike the case for validations.
-
-## Unique Constraint
-
-Our code example above defines a unique constraint on the `username` field:
-
-```js
-/* ... */ {
- username: {
- type: DataTypes.TEXT,
- allowNull: false,
- unique: true
- },
-} /* ... */
-```
-
-When this model is synchronized (by calling `sequelize.sync` for example), the `username` field will be created in the table as `` `username` TEXT UNIQUE``, and an attempt to insert an username that already exists there will throw a `SequelizeUniqueConstraintError`.
-
-## Allowing/disallowing null values
-
-By default, `null` is an allowed value for every column of a model. This can be disabled setting the `allowNull: false` option for a column, as it was done in the `username` field from our code example:
-
-```js
-/* ... */ {
- username: {
- type: DataTypes.TEXT,
- allowNull: false,
- unique: true
- },
-} /* ... */
-```
-
-Without `allowNull: false`, the call `User.create({})` would work.
-
-### Note about `allowNull` implementation
-
-The `allowNull` check is the only check in Sequelize that is a mix of a *validation* and a *constraint* in the senses described at the beginning of this tutorial. This is because:
-
-* If an attempt is made to set `null` to a field that does not allow null, a `ValidationError` will be thrown *without any SQL query being performed*.
-* In addition, after `sequelize.sync`, the column that has `allowNull: false` will be defined with a `NOT NULL` SQL constraint. This way, direct SQL queries that attempt to set the value to `null` will also fail.
-
-## Validators
-
-Model validators allow you to specify format/content/inheritance validations for each attribute of the model. Validations are automatically run on `create`, `update` and `save`. You can also call `validate()` to manually validate an instance.
-
-### Per-attribute validations
-
-You can define your custom validators or use several built-in validators, implemented by [validator.js (10.11.0)](https://github.com/chriso/validator.js), as shown below.
-
-```js
-sequelize.define('foo', {
- bar: {
- type: DataTypes.STRING,
- validate: {
- is: /^[a-z]+$/i, // matches this RegExp
- is: ["^[a-z]+$",'i'], // same as above, but constructing the RegExp from a string
- not: /^[a-z]+$/i, // does not match this RegExp
- not: ["^[a-z]+$",'i'], // same as above, but constructing the RegExp from a string
- isEmail: true, // checks for email format (foo@bar.com)
- isUrl: true, // checks for url format (https://foo.com)
- isIP: true, // checks for IPv4 (129.89.23.1) or IPv6 format
- isIPv4: true, // checks for IPv4 (129.89.23.1)
- isIPv6: true, // checks for IPv6 format
- isAlpha: true, // will only allow letters
- isAlphanumeric: true, // will only allow alphanumeric characters, so "_abc" will fail
- isNumeric: true, // will only allow numbers
- isInt: true, // checks for valid integers
- isFloat: true, // checks for valid floating point numbers
- isDecimal: true, // checks for any numbers
- isLowercase: true, // checks for lowercase
- isUppercase: true, // checks for uppercase
- notNull: true, // won't allow null
- isNull: true, // only allows null
- notEmpty: true, // don't allow empty strings
- equals: 'specific value', // only allow a specific value
- contains: 'foo', // force specific substrings
- notIn: [['foo', 'bar']], // check the value is not one of these
- isIn: [['foo', 'bar']], // check the value is one of these
- notContains: 'bar', // don't allow specific substrings
- len: [2,10], // only allow values with length between 2 and 10
- isUUID: 4, // only allow uuids
- isDate: true, // only allow date strings
- isAfter: "2011-11-05", // only allow date strings after a specific date
- isBefore: "2011-11-05", // only allow date strings before a specific date
- max: 23, // only allow values <= 23
- min: 23, // only allow values >= 23
- isCreditCard: true, // check for valid credit card numbers
-
- // Examples of custom validators:
- isEven(value) {
- if (parseInt(value) % 2 !== 0) {
- throw new Error('Only even values are allowed!');
- }
- }
- isGreaterThanOtherField(value) {
- if (parseInt(value) <= parseInt(this.otherField)) {
- throw new Error('Bar must be greater than otherField.');
- }
- }
- }
- }
-});
-```
-
-Note that where multiple arguments need to be passed to the built-in validation functions, the arguments to be passed must be in an array. But if a single array argument is to be passed, for instance an array of acceptable strings for `isIn`, this will be interpreted as multiple string arguments instead of one array argument. To work around this pass a single-length array of arguments, such as `[['foo', 'bar']]` as shown above.
-
-To use a custom error message instead of that provided by [validator.js](https://github.com/chriso/validator.js), use an object instead of the plain value or array of arguments, for example a validator which needs no argument can be given a custom message with
-
-```js
-isInt: {
- msg: "Must be an integer number of pennies"
-}
-```
-
-or if arguments need to also be passed add an `args` property:
-
-```js
-isIn: {
- args: [['en', 'zh']],
- msg: "Must be English or Chinese"
-}
-```
-
-When using custom validator functions the error message will be whatever message the thrown `Error` object holds.
-
-See [the validator.js project](https://github.com/chriso/validator.js) for more details on the built in validation methods.
-
-**Hint:** You can also define a custom function for the logging part. Just pass a function. The first parameter will be the string that is logged.
-
-### `allowNull` interaction with other validators
-
-If a particular field of a model is set to not allow null (with `allowNull: false`) and that value has been set to `null`, all validators will be skipped and a `ValidationError` will be thrown.
-
-On the other hand, if it is set to allow null (with `allowNull: true`) and that value has been set to `null`, only the built-in validators will be skipped, while the custom validators will still run.
-
-This means you can, for instance, have a string field which validates its length to be between 5 and 10 characters, but which also allows `null` (since the length validator will be skipped automatically when the value is `null`):
-
-```js
-class User extends Model {}
-User.init({
- username: {
- type: DataTypes.STRING,
- allowNull: true,
- validate: {
- len: [5, 10]
- }
- }
-}, { sequelize });
-```
-
-You also can conditionally allow `null` values, with a custom validator, since it won't be skipped:
-
-```js
-class User extends Model {}
-User.init({
- age: Sequelize.INTEGER,
- name: {
- type: DataTypes.STRING,
- allowNull: true,
- validate: {
- customValidator(value) {
- if (value === null && this.age !== 10) {
- throw new Error("name can't be null unless age is 10");
- }
- }
- }
- }
-}, { sequelize });
-```
-
-You can customize `allowNull` error message by setting the `notNull` validator:
-
-```js
-class User extends Model {}
-User.init({
- name: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- notNull: {
- msg: 'Please enter your name'
- }
- }
- }
-}, { sequelize });
-```
-
-### Model-wide validations
-
-Validations can also be defined to check the model after the field-specific validators. Using this you could, for example, ensure either neither of `latitude` and `longitude` are set or both, and fail if one but not the other is set.
-
-Model validator methods are called with the model object's context and are deemed to fail if they throw an error, otherwise pass. This is just the same as with custom field-specific validators.
-
-Any error messages collected are put in the validation result object alongside the field validation errors, with keys named after the failed validation method's key in the `validate` option object. Even though there can only be one error message for each model validation method at any one time, it is presented as a single string error in an array, to maximize consistency with the field errors.
-
-An example:
-
-```js
-class Place extends Model {}
-Place.init({
- name: Sequelize.STRING,
- address: Sequelize.STRING,
- latitude: {
- type: DataTypes.INTEGER,
- validate: {
- min: -90,
- max: 90
- }
- },
- longitude: {
- type: DataTypes.INTEGER,
- validate: {
- min: -180,
- max: 180
- }
- },
-}, {
- sequelize,
- validate: {
- bothCoordsOrNone() {
- if ((this.latitude === null) !== (this.longitude === null)) {
- throw new Error('Either both latitude and longitude, or neither!');
- }
- }
- }
-})
-```
-
-In this simple case an object fails validation if either latitude or longitude is given, but not both. If we try to build one with an out-of-range latitude and no longitude, `somePlace.validate()` might return:
-
-```js
-{
- 'latitude': ['Invalid number: latitude'],
- 'bothCoordsOrNone': ['Either both latitude and longitude, or neither!']
-}
-```
-
-Such validation could have also been done with a custom validator defined on a single attribute (such as the `latitude` attribute, by checking `(value === null) !== (this.longitude === null)`), but the model-wide validation approach is cleaner.
+---
+sidebar_position: 6
+title: Validations & Constraints
+---
+
+In this tutorial you will learn how to setup validations and constraints for your models in Sequelize.
+
+For this tutorial, the following setup will be assumed:
+
+```js
+const { Sequelize, Op, Model, DataTypes } = require('sequelize');
+const sequelize = new Sequelize('sqlite::memory:');
+
+const User = sequelize.define('user', {
+ username: {
+ type: DataTypes.TEXT,
+ allowNull: false,
+ unique: true,
+ },
+ hashedPassword: {
+ type: DataTypes.STRING(64),
+ validate: {
+ is: /^[0-9a-f]{64}$/i,
+ },
+ },
+});
+
+(async () => {
+ await sequelize.sync({ force: true });
+ // Code here
+})();
+```
+
+## Difference between Validations and Constraints
+
+Validations are checks performed in the Sequelize level, in pure JavaScript. They can be arbitrarily complex if you provide a custom validator function, or can be one of the built-in validators offered by Sequelize. If a validation fails, no SQL query will be sent to the database at all.
+
+On the other hand, constraints are rules defined at SQL level. The most basic example of constraint is an Unique Constraint. If a constraint check fails, an error will be thrown by the database and Sequelize will forward this error to JavaScript (in this example, throwing a `SequelizeUniqueConstraintError`). Note that in this case, the SQL query was performed, unlike the case for validations.
+
+## Unique Constraint
+
+Our code example above defines a unique constraint on the `username` field:
+
+```js
+/* ... */ {
+ username: {
+ type: DataTypes.TEXT,
+ allowNull: false,
+ unique: true
+ },
+} /* ... */
+```
+
+When this model is synchronized (by calling `sequelize.sync` for example), the `username` field will be created in the table as `` `username` TEXT UNIQUE``, and an attempt to insert an username that already exists there will throw a `SequelizeUniqueConstraintError`.
+
+## Allowing/disallowing null values
+
+By default, `null` is an allowed value for every column of a model. This can be disabled setting the `allowNull: false` option for a column, as it was done in the `username` field from our code example:
+
+```js
+/* ... */ {
+ username: {
+ type: DataTypes.TEXT,
+ allowNull: false,
+ unique: true
+ },
+} /* ... */
+```
+
+Without `allowNull: false`, the call `User.create({})` would work.
+
+### Note about `allowNull` implementation
+
+The `allowNull` check is the only check in Sequelize that is a mix of a _validation_ and a _constraint_ in the senses described at the beginning of this tutorial. This is because:
+
+- If an attempt is made to set `null` to a field that does not allow null, a `ValidationError` will be thrown _without any SQL query being performed_.
+- In addition, after `sequelize.sync`, the column that has `allowNull: false` will be defined with a `NOT NULL` SQL constraint. This way, direct SQL queries that attempt to set the value to `null` will also fail.
+
+## Validators
+
+Model validators allow you to specify format/content/inheritance validations for each attribute of the model. Validations are automatically run on `create`, `update` and `save`. You can also call `validate()` to manually validate an instance.
+
+### Per-attribute validations
+
+You can define your custom validators or use several built-in validators, implemented by [validator.js (10.11.0)](https://github.com/chriso/validator.js), as shown below.
+
+```js
+sequelize.define('foo', {
+ bar: {
+ type: DataTypes.STRING,
+ validate: {
+ is: /^[a-z]+$/i, // matches this RegExp
+ is: ["^[a-z]+$",'i'], // same as above, but constructing the RegExp from a string
+ not: /^[a-z]+$/i, // does not match this RegExp
+ not: ["^[a-z]+$",'i'], // same as above, but constructing the RegExp from a string
+ isEmail: true, // checks for email format (foo@bar.com)
+ isUrl: true, // checks for url format (https://foo.com)
+ isIP: true, // checks for IPv4 (129.89.23.1) or IPv6 format
+ isIPv4: true, // checks for IPv4 (129.89.23.1)
+ isIPv6: true, // checks for IPv6 format
+ isAlpha: true, // will only allow letters
+ isAlphanumeric: true, // will only allow alphanumeric characters, so "_abc" will fail
+ isNumeric: true, // will only allow numbers
+ isInt: true, // checks for valid integers
+ isFloat: true, // checks for valid floating point numbers
+ isDecimal: true, // checks for any numbers
+ isLowercase: true, // checks for lowercase
+ isUppercase: true, // checks for uppercase
+ notNull: true, // won't allow null
+ isNull: true, // only allows null
+ notEmpty: true, // don't allow empty strings
+ equals: 'specific value', // only allow a specific value
+ contains: 'foo', // force specific substrings
+ notIn: [['foo', 'bar']], // check the value is not one of these
+ isIn: [['foo', 'bar']], // check the value is one of these
+ notContains: 'bar', // don't allow specific substrings
+ len: [2,10], // only allow values with length between 2 and 10
+ isUUID: 4, // only allow uuids
+ isDate: true, // only allow date strings
+ isAfter: "2011-11-05", // only allow date strings after a specific date
+ isBefore: "2011-11-05", // only allow date strings before a specific date
+ max: 23, // only allow values <= 23
+ min: 23, // only allow values >= 23
+ isCreditCard: true, // check for valid credit card numbers
+
+ // Examples of custom validators:
+ isEven(value) {
+ if (parseInt(value) % 2 !== 0) {
+ throw new Error('Only even values are allowed!');
+ }
+ }
+ isGreaterThanOtherField(value) {
+ if (parseInt(value) <= parseInt(this.otherField)) {
+ throw new Error('Bar must be greater than otherField.');
+ }
+ }
+ }
+ }
+});
+```
+
+Note that where multiple arguments need to be passed to the built-in validation functions, the arguments to be passed must be in an array. But if a single array argument is to be passed, for instance an array of acceptable strings for `isIn`, this will be interpreted as multiple string arguments instead of one array argument. To work around this pass a single-length array of arguments, such as `[['foo', 'bar']]` as shown above.
+
+To use a custom error message instead of that provided by [validator.js](https://github.com/chriso/validator.js), use an object instead of the plain value or array of arguments, for example a validator which needs no argument can be given a custom message with
+
+```js
+isInt: {
+ msg: 'Must be an integer number of pennies';
+}
+```
+
+or if arguments need to also be passed add an `args` property:
+
+```js
+isIn: {
+ args: [['en', 'zh']],
+ msg: "Must be English or Chinese"
+}
+```
+
+When using custom validator functions the error message will be whatever message the thrown `Error` object holds.
+
+See [the validator.js project](https://github.com/chriso/validator.js) for more details on the built in validation methods.
+
+**Hint:** You can also define a custom function for the logging part. Just pass a function. The first parameter will be the string that is logged.
+
+### `allowNull` interaction with other validators
+
+If a particular field of a model is set to not allow null (with `allowNull: false`) and that value has been set to `null`, all validators will be skipped and a `ValidationError` will be thrown.
+
+On the other hand, if it is set to allow null (with `allowNull: true`) and that value has been set to `null`, only the built-in validators will be skipped, while the custom validators will still run.
+
+This means you can, for instance, have a string field which validates its length to be between 5 and 10 characters, but which also allows `null` (since the length validator will be skipped automatically when the value is `null`):
+
+```js
+class User extends Model {}
+User.init(
+ {
+ username: {
+ type: DataTypes.STRING,
+ allowNull: true,
+ validate: {
+ len: [5, 10],
+ },
+ },
+ },
+ { sequelize },
+);
+```
+
+You also can conditionally allow `null` values, with a custom validator, since it won't be skipped:
+
+```js
+class User extends Model {}
+User.init(
+ {
+ age: Sequelize.INTEGER,
+ name: {
+ type: DataTypes.STRING,
+ allowNull: true,
+ validate: {
+ customValidator(value) {
+ if (value === null && this.age !== 10) {
+ throw new Error("name can't be null unless age is 10");
+ }
+ },
+ },
+ },
+ },
+ { sequelize },
+);
+```
+
+You can customize `allowNull` error message by setting the `notNull` validator:
+
+```js
+class User extends Model {}
+User.init(
+ {
+ name: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: 'Please enter your name',
+ },
+ },
+ },
+ },
+ { sequelize },
+);
+```
+
+### Model-wide validations
+
+Validations can also be defined to check the model after the field-specific validators. Using this you could, for example, ensure either neither of `latitude` and `longitude` are set or both, and fail if one but not the other is set.
+
+Model validator methods are called with the model object's context and are deemed to fail if they throw an error, otherwise pass. This is just the same as with custom field-specific validators.
+
+Any error messages collected are put in the validation result object alongside the field validation errors, with keys named after the failed validation method's key in the `validate` option object. Even though there can only be one error message for each model validation method at any one time, it is presented as a single string error in an array, to maximize consistency with the field errors.
+
+An example:
+
+```js
+class Place extends Model {}
+Place.init(
+ {
+ name: Sequelize.STRING,
+ address: Sequelize.STRING,
+ latitude: {
+ type: DataTypes.INTEGER,
+ validate: {
+ min: -90,
+ max: 90,
+ },
+ },
+ longitude: {
+ type: DataTypes.INTEGER,
+ validate: {
+ min: -180,
+ max: 180,
+ },
+ },
+ },
+ {
+ sequelize,
+ validate: {
+ bothCoordsOrNone() {
+ if ((this.latitude === null) !== (this.longitude === null)) {
+ throw new Error('Either both latitude and longitude, or neither!');
+ }
+ },
+ },
+ },
+);
+```
+
+In this simple case an object fails validation if either latitude or longitude is given, but not both. If we try to build one with an out-of-range latitude and no longitude, `somePlace.validate()` might return:
+
+```js
+{
+ 'latitude': ['Invalid number: latitude'],
+ 'bothCoordsOrNone': ['Either both latitude and longitude, or neither!']
+}
+```
+
+Such validation could have also been done with a custom validator defined on a single attribute (such as the `latitude` attribute, by checking `(value === null) !== (this.longitude === null)`), but the model-wide validation approach is cleaner.
diff --git a/versioned_docs/version-6.x.x/getting-started.md b/versioned_docs/version-6.x.x/getting-started.md
index fccd7624..39259a1d 100644
--- a/versioned_docs/version-6.x.x/getting-started.md
+++ b/versioned_docs/version-6.x.x/getting-started.md
@@ -83,8 +83,8 @@ Observe that, in the examples above, `Sequelize` refers to the library itself wh
You are encouraged to run code examples locally while reading the Sequelize docs. This will help you learn faster. The easiest way to do this is using the SQLite dialect:
```js
-const { Sequelize, Op, Model, DataTypes } = require("sequelize");
-const sequelize = new Sequelize("sqlite::memory:");
+const { Sequelize, Op, Model, DataTypes } = require('sequelize');
+const sequelize = new Sequelize('sqlite::memory:');
// Code here! It works!
```
@@ -106,11 +106,11 @@ Common useful values for `options.logging`:
```js
const sequelize = new Sequelize('sqlite::memory:', {
// Choose one of the logging options
- logging: console.log, // Default, displays the first parameter of the log function call
+ logging: console.log, // Default, displays the first parameter of the log function call
logging: (...msg) => console.log(msg), // Displays all log function call parameters
- logging: false, // Disables logging
- logging: msg => logger.debug(msg), // Use custom logger (e.g. Winston or Bunyan), displays the first parameter
- logging: logger.debug.bind(logger) // Alternative way to use custom logger, displays all messages
+ logging: false, // Disables logging
+ logging: msg => logger.debug(msg), // Use custom logger (e.g. Winston or Bunyan), displays the first parameter
+ logging: logger.debug.bind(logger), // Alternative way to use custom logger, displays all messages
});
```
diff --git a/versioned_docs/version-6.x.x/index.md b/versioned_docs/version-6.x.x/index.md
index c8e04f09..71f73de2 100644
--- a/versioned_docs/version-6.x.x/index.md
+++ b/versioned_docs/version-6.x.x/index.md
@@ -29,16 +29,19 @@ const { Sequelize, Model, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
class User extends Model {}
-User.init({
- username: DataTypes.STRING,
- birthday: DataTypes.DATE
-}, { sequelize, modelName: 'user' });
+User.init(
+ {
+ username: DataTypes.STRING,
+ birthday: DataTypes.DATE,
+ },
+ { sequelize, modelName: 'user' },
+);
(async () => {
await sequelize.sync();
const jane = await User.create({
username: 'janedoe',
- birthday: new Date(1980, 6, 20)
+ birthday: new Date(1980, 6, 20),
});
console.log(jane.toJSON());
})();
diff --git a/versioned_docs/version-6.x.x/moved/_category_.json b/versioned_docs/version-6.x.x/moved/_category_.json
index f740d2b1..741dbc36 100644
--- a/versioned_docs/version-6.x.x/moved/_category_.json
+++ b/versioned_docs/version-6.x.x/moved/_category_.json
@@ -1,9 +1,9 @@
{
- "position": 5,
- "label": "Moved",
- "collapsible": true,
- "collapsed": true,
- "link": {
- "type": "generated-index"
- }
+ "position": 5,
+ "label": "Moved",
+ "collapsible": true,
+ "collapsed": true,
+ "link": {
+ "type": "generated-index"
}
+}
diff --git a/versioned_docs/version-6.x.x/moved/associations.md b/versioned_docs/version-6.x.x/moved/associations.md
index ba788487..4e41a99f 100644
--- a/versioned_docs/version-6.x.x/moved/associations.md
+++ b/versioned_docs/version-6.x.x/moved/associations.md
@@ -1,18 +1,18 @@
----
-title: Associations
----
-
-The contents of this page were moved to other specialized guides.
-
-If you're here, you might be looking for these topics:
-
-* **Core Concepts**
- * [Associations](../core-concepts/assocs.md)
-* **Advanced Association Concepts**
- * [Eager Loading](../advanced-association-concepts/eager-loading.md)
- * [Creating with Associations](../advanced-association-concepts/creating-with-associations.md)
- * [Advanced M:N Associations](../advanced-association-concepts/advanced-many-to-many.md)
- * [Polymorphism & Scopes](../advanced-association-concepts/polymorphic-associations.md)
-* **Other Topics**
- * [Naming Strategies](../other-topics/naming-strategies.md)
- * [Constraints & Circularities](../other-topics/constraints-and-circularities.md)
+---
+title: Associations
+---
+
+The contents of this page were moved to other specialized guides.
+
+If you're here, you might be looking for these topics:
+
+- **Core Concepts**
+ - [Associations](../core-concepts/assocs.md)
+- **Advanced Association Concepts**
+ - [Eager Loading](../advanced-association-concepts/eager-loading.md)
+ - [Creating with Associations](../advanced-association-concepts/creating-with-associations.md)
+ - [Advanced M:N Associations](../advanced-association-concepts/advanced-many-to-many.md)
+ - [Polymorphism & Scopes](../advanced-association-concepts/polymorphic-associations.md)
+- **Other Topics**
+ - [Naming Strategies](../other-topics/naming-strategies.md)
+ - [Constraints & Circularities](../other-topics/constraints-and-circularities.md)
diff --git a/versioned_docs/version-6.x.x/moved/data-types.md b/versioned_docs/version-6.x.x/moved/data-types.md
index 63804d95..70d7d471 100644
--- a/versioned_docs/version-6.x.x/moved/data-types.md
+++ b/versioned_docs/version-6.x.x/moved/data-types.md
@@ -1,14 +1,14 @@
----
-title: Data Types
----
-
-The contents of this page were moved to other specialized guides.
-
-If you're here, you might be looking for these topics:
-
-* **Core Concepts**
- * [Model Basics: Data Types](../core-concepts/model-basics.md#data-types)
-* **Other Topics**
- * [Other Data Types](../other-topics/other-data-types.mdx)
- * [Extending Data Types](../other-topics/extending-data-types.md)
- * [Dialect-Specific Things](../other-topics/dialect-specific-things.md)
+---
+title: Data Types
+---
+
+The contents of this page were moved to other specialized guides.
+
+If you're here, you might be looking for these topics:
+
+- **Core Concepts**
+ - [Model Basics: Data Types](../core-concepts/model-basics.md#data-types)
+- **Other Topics**
+ - [Other Data Types](../other-topics/other-data-types.mdx)
+ - [Extending Data Types](../other-topics/extending-data-types.md)
+ - [Dialect-Specific Things](../other-topics/dialect-specific-things.md)
diff --git a/versioned_docs/version-6.x.x/moved/models-definition.md b/versioned_docs/version-6.x.x/moved/models-definition.md
index 8c64e6af..80ac30bf 100644
--- a/versioned_docs/version-6.x.x/moved/models-definition.md
+++ b/versioned_docs/version-6.x.x/moved/models-definition.md
@@ -1,59 +1,59 @@
----
-title: Models Definition
----
-
-The contents of this page were moved to [Model Basics](../core-concepts/model-basics.md).
-
-The only exception is the guide on `sequelize.import`, which is deprecated and was removed from the docs. However, if you really need it, it was kept here.
-
-----
-
-## Deprecated: `sequelize.import`
-
-> _**Note:** You should not use `sequelize.import`. Please just use [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import), [`import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports), or [`require`](https://nodejs.org/api/modules.html#requireid) instead._
->
-> _This documentation has been kept just in case you really need to maintain old code that uses it._
-
-`sequelize.import` can only load [CommonJS](https://nodejs.org/api/modules.html) files, and is not capable of loading [`ecmascript modules`](https://nodejs.org/api/esm.html). Use native `import` if you need to load ecmascript modules.
-
-You can store your model definitions in a single file using the `sequelize.import` method. The returned object is exactly the same as defined in the imported file's function. The import is cached, just like `require`, so you won't run into trouble if importing a file more than once.
-
-```js
-// in your server file - e.g. app.js
-const Project = sequelize.import(__dirname + "/path/to/models/project");
-
-// The model definition is done in /path/to/models/project.js
-module.exports = (sequelize, DataTypes) => {
- return sequelize.define('project', {
- name: DataTypes.STRING,
- description: DataTypes.TEXT
- });
-};
-```
-
-The `import` method can also accept a callback as an argument.
-
-```js
-sequelize.import('project', (sequelize, DataTypes) => {
- return sequelize.define('project', {
- name: DataTypes.STRING,
- description: DataTypes.TEXT
- });
-});
-```
-
-This extra capability is useful when, for example, `Error: Cannot find module` is thrown even though `/path/to/models/project` seems to be correct. Some frameworks, such as Meteor, overload `require`, and might raise an error such as:
-
-```text
-Error: Cannot find module '/home/you/meteorApp/.meteor/local/build/programs/server/app/path/to/models/project.js'
-```
-
-This can be worked around by passing in Meteor's version of `require`:
-
-```js
-// If this fails...
-const AuthorModel = db.import('./path/to/models/project');
-
-// Try this instead!
-const AuthorModel = db.import('project', require('./path/to/models/project'));
-```
+---
+title: Models Definition
+---
+
+The contents of this page were moved to [Model Basics](../core-concepts/model-basics.md).
+
+The only exception is the guide on `sequelize.import`, which is deprecated and was removed from the docs. However, if you really need it, it was kept here.
+
+---
+
+## Deprecated: `sequelize.import`
+
+> _**Note:** You should not use `sequelize.import`. Please just use [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import), [`import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports), or [`require`](https://nodejs.org/api/modules.html#requireid) instead._
+>
+> _This documentation has been kept just in case you really need to maintain old code that uses it._
+
+`sequelize.import` can only load [CommonJS](https://nodejs.org/api/modules.html) files, and is not capable of loading [`ecmascript modules`](https://nodejs.org/api/esm.html). Use native `import` if you need to load ecmascript modules.
+
+You can store your model definitions in a single file using the `sequelize.import` method. The returned object is exactly the same as defined in the imported file's function. The import is cached, just like `require`, so you won't run into trouble if importing a file more than once.
+
+```js
+// in your server file - e.g. app.js
+const Project = sequelize.import(__dirname + '/path/to/models/project');
+
+// The model definition is done in /path/to/models/project.js
+module.exports = (sequelize, DataTypes) => {
+ return sequelize.define('project', {
+ name: DataTypes.STRING,
+ description: DataTypes.TEXT,
+ });
+};
+```
+
+The `import` method can also accept a callback as an argument.
+
+```js
+sequelize.import('project', (sequelize, DataTypes) => {
+ return sequelize.define('project', {
+ name: DataTypes.STRING,
+ description: DataTypes.TEXT,
+ });
+});
+```
+
+This extra capability is useful when, for example, `Error: Cannot find module` is thrown even though `/path/to/models/project` seems to be correct. Some frameworks, such as Meteor, overload `require`, and might raise an error such as:
+
+```text
+Error: Cannot find module '/home/you/meteorApp/.meteor/local/build/programs/server/app/path/to/models/project.js'
+```
+
+This can be worked around by passing in Meteor's version of `require`:
+
+```js
+// If this fails...
+const AuthorModel = db.import('./path/to/models/project');
+
+// Try this instead!
+const AuthorModel = db.import('project', require('./path/to/models/project'));
+```
diff --git a/versioned_docs/version-6.x.x/moved/models-usage.md b/versioned_docs/version-6.x.x/moved/models-usage.md
index e2d54785..27b0ceef 100644
--- a/versioned_docs/version-6.x.x/moved/models-usage.md
+++ b/versioned_docs/version-6.x.x/moved/models-usage.md
@@ -1,14 +1,14 @@
----
-title: Models Usage
----
-
-The contents of this page were moved to other specialized guides.
-
-If you're here, you might be looking for these topics:
-
-* **Core Concepts**
- * [Model Querying - Basics](../core-concepts/model-querying-basics.md)
- * [Model Querying - Finders](../core-concepts/model-querying-finders.md)
- * [Raw Queries](../core-concepts/raw-queries.md)
-* **Advanced Association Concepts**
- * [Eager Loading](../advanced-association-concepts/eager-loading.md)
+---
+title: Models Usage
+---
+
+The contents of this page were moved to other specialized guides.
+
+If you're here, you might be looking for these topics:
+
+- **Core Concepts**
+ - [Model Querying - Basics](../core-concepts/model-querying-basics.md)
+ - [Model Querying - Finders](../core-concepts/model-querying-finders.md)
+ - [Raw Queries](../core-concepts/raw-queries.md)
+- **Advanced Association Concepts**
+ - [Eager Loading](../advanced-association-concepts/eager-loading.md)
diff --git a/versioned_docs/version-6.x.x/moved/querying.md b/versioned_docs/version-6.x.x/moved/querying.md
index bd5b2030..7e97408c 100644
--- a/versioned_docs/version-6.x.x/moved/querying.md
+++ b/versioned_docs/version-6.x.x/moved/querying.md
@@ -1,15 +1,15 @@
----
-title: Querying
----
-
-The contents of this page were moved to other specialized guides.
-
-If you're here, you might be looking for these topics:
-
-* **Core Concepts**
- * [Model Querying - Basics](../core-concepts/model-querying-basics.md)
- * [Model Querying - Finders](../core-concepts/model-querying-finders.md)
- * [Raw Queries](../core-concepts/raw-queries.md)
- * [Associations](../core-concepts/assocs.md)
-* **Other Topics**
- * [Dialect-Specific Things](../other-topics/dialect-specific-things.md)
+---
+title: Querying
+---
+
+The contents of this page were moved to other specialized guides.
+
+If you're here, you might be looking for these topics:
+
+- **Core Concepts**
+ - [Model Querying - Basics](../core-concepts/model-querying-basics.md)
+ - [Model Querying - Finders](../core-concepts/model-querying-finders.md)
+ - [Raw Queries](../core-concepts/raw-queries.md)
+ - [Associations](../core-concepts/assocs.md)
+- **Other Topics**
+ - [Dialect-Specific Things](../other-topics/dialect-specific-things.md)
diff --git a/versioned_docs/version-6.x.x/other-topics/aws-lambda.md b/versioned_docs/version-6.x.x/other-topics/aws-lambda.md
index a7e88c72..705cdec0 100644
--- a/versioned_docs/version-6.x.x/other-topics/aws-lambda.md
+++ b/versioned_docs/version-6.x.x/other-topics/aws-lambda.md
@@ -256,7 +256,7 @@ AWS Lambda handlers come in two flavors in Node.js:
module.exports.handler = function (event, context, callback) {
try {
doSomething();
- callback(null, "Hello World!"); // Lambda returns "Hello World!"
+ callback(null, 'Hello World!'); // Lambda returns "Hello World!"
} catch (err) {
// try/catch is not required, uncaught exceptions invoke `callback(err)` implicitly
callback(err); // Lambda fails with `err`
@@ -272,7 +272,7 @@ module.exports.handler = function (event, context, callback) {
module.exports.handler = async function (event, context) {
try {
await doSomethingAsync();
- return "Hello World!"; // equivalent of: callback(null, "Hello World!");
+ return 'Hello World!'; // equivalent of: callback(null, "Hello World!");
} catch (err) {
// try/cath is not required, async functions always return a Promise
throw err; // equivalent of: callback(err);
@@ -289,7 +289,7 @@ module.exports.handler = function (event, context) {
*/
return Promise.resolve()
.then(() => doSomethingAsync())
- .then(() => "Hello World!");
+ .then(() => 'Hello World!');
};
```
@@ -321,7 +321,7 @@ module.exports.handler = function () {
module.exports.handler = function (event, context, callback) {
// Lambda finishes AFTER `doSomething()` is invoked
setTimeout(() => doSomething(), 1000);
- callback(null, "Hello World!");
+ callback(null, 'Hello World!');
};
// callback invoked, context.callbackWaitsForEmptyEventLoop = false
@@ -329,21 +329,21 @@ module.exports.handler = function (event, context, callback) {
// Lambda finishes BEFORE `doSomething()` is invoked
context.callbackWaitsForEmptyEventLoop = false;
setTimeout(() => doSomething(), 2000);
- setTimeout(() => callback(null, "Hello World!"), 1000);
+ setTimeout(() => callback(null, 'Hello World!'), 1000);
};
// async/await
module.exports.handler = async function () {
// Lambda finishes BEFORE `doSomething()` is invoked
setTimeout(() => doSomething(), 1000);
- return "Hello World!";
+ return 'Hello World!';
};
// Promise
module.exports.handler = function () {
// Lambda finishes BEFORE `doSomething()` is invoked
setTimeout(() => doSomething(), 1000);
- return Promise.resolve("Hello World!");
+ return Promise.resolve('Hello World!');
};
```
@@ -410,20 +410,20 @@ module.exports.handler = function (event, context, callback) {
setTimeout(() => {
console.log(
- "Slow timeout invoked. Request id:",
+ 'Slow timeout invoked. Request id:',
context.awsRequestId,
- "| Elapsed ms:",
- Date.now() - now
+ '| Elapsed ms:',
+ Date.now() - now,
);
counter++;
}, 1000);
setTimeout(() => {
console.log(
- "Fast timeout invoked. Request id:",
+ 'Fast timeout invoked. Request id:',
context.awsRequestId,
- "| Elapsed ms:",
- Date.now() - now
+ '| Elapsed ms:',
+ Date.now() - now,
);
counter++;
callback(null, counter);
@@ -528,7 +528,7 @@ class Runtime {
let [callback, callbackContext] = CallbackContext.build(
this.client,
invokeContext.invokeId,
- this.scheduleIteration.bind(this)
+ this.scheduleIteration.bind(this),
);
try {
@@ -541,14 +541,12 @@ class Runtime {
const result = this.handler(
JSON.parse(bodyJson),
invokeContext.attachEnvironmentData(callbackContext),
- callback
+ callback,
);
// finish the execution if the handler is async
if (_isPromise(result)) {
- result
- .then(callbackContext.succeed, callbackContext.fail)
- .catch(callbackContext.fail);
+ result.then(callbackContext.succeed, callbackContext.fail).catch(callbackContext.fail);
}
} catch (err) {
callback(err);
@@ -612,19 +610,19 @@ class ConnectionManager {
// uses mysql2's `new Connection()`
const connection = this.lib.createConnection(connectionConfig);
- const errorHandler = (e) => {
- connection.removeListener("connect", connectHandler);
- connection.removeListener("error", connectHandler);
+ const errorHandler = e => {
+ connection.removeListener('connect', connectHandler);
+ connection.removeListener('error', connectHandler);
reject(e);
};
const connectHandler = () => {
- connection.removeListener("error", errorHandler);
+ connection.removeListener('error', errorHandler);
resolve(connection);
};
- connection.on("error", errorHandler);
- connection.once("connect", connectHandler);
+ connection.on('error', errorHandler);
+ connection.once('connect', connectHandler);
});
}
}
diff --git a/versioned_docs/version-6.x.x/other-topics/constraints-and-circularities.md b/versioned_docs/version-6.x.x/other-topics/constraints-and-circularities.md
index b49ed157..615cff5d 100644
--- a/versioned_docs/version-6.x.x/other-topics/constraints-and-circularities.md
+++ b/versioned_docs/version-6.x.x/other-topics/constraints-and-circularities.md
@@ -1,115 +1,130 @@
----
-title: Constraints & Circularities
----
-
-Adding constraints between tables means that tables must be created in the database in a certain order, when using `sequelize.sync`. If `Task` has a reference to `User`, the `User` table must be created before the `Task` table can be created. This can sometimes lead to circular references, where Sequelize cannot find an order in which to sync. Imagine a scenario of documents and versions. A document can have multiple versions, and for convenience, a document has a reference to its current version.
-
-```js
-const { Sequelize, Model, DataTypes } = require("sequelize");
-
-class Document extends Model {}
-Document.init({
- author: DataTypes.STRING
-}, { sequelize, modelName: 'document' });
-
-class Version extends Model {}
-Version.init({
- timestamp: DataTypes.DATE
-}, { sequelize, modelName: 'version' });
-
-Document.hasMany(Version); // This adds documentId attribute to version
-Document.belongsTo(Version, {
- as: 'Current',
- foreignKey: 'currentVersionId'
-}); // This adds currentVersionId attribute to document
-```
-
-However, unfortunately the code above will result in the following error:
-
-```text
-Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents
-```
-
-In order to alleviate that, we can pass `constraints: false` to one of the associations:
-
-```js
-Document.hasMany(Version);
-Document.belongsTo(Version, {
- as: 'Current',
- foreignKey: 'currentVersionId',
- constraints: false
-});
-```
-
-Which will allow us to sync the tables correctly:
-
-```sql
-CREATE TABLE IF NOT EXISTS "documents" (
- "id" SERIAL,
- "author" VARCHAR(255),
- "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "currentVersionId" INTEGER,
- PRIMARY KEY ("id")
-);
-
-CREATE TABLE IF NOT EXISTS "versions" (
- "id" SERIAL,
- "timestamp" TIMESTAMP WITH TIME ZONE,
- "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "documentId" INTEGER REFERENCES "documents" ("id") ON DELETE
- SET
- NULL ON UPDATE CASCADE,
- PRIMARY KEY ("id")
-);
-```
-
-## Enforcing a foreign key reference without constraints
-
-Sometimes you may want to reference another table, without adding any constraints, or associations. In that case you can manually add the reference attributes to your schema definition, and mark the relations between them.
-
-```js
-class Trainer extends Model {}
-Trainer.init({
- firstName: Sequelize.STRING,
- lastName: Sequelize.STRING
-}, { sequelize, modelName: 'trainer' });
-
-// Series will have a trainerId = Trainer.id foreign reference key
-// after we call Trainer.hasMany(series)
-class Series extends Model {}
-Series.init({
- title: Sequelize.STRING,
- subTitle: Sequelize.STRING,
- description: Sequelize.TEXT,
- // Set FK relationship (hasMany) with `Trainer`
- trainerId: {
- type: DataTypes.INTEGER,
- references: {
- model: Trainer,
- key: 'id'
- }
- }
-}, { sequelize, modelName: 'series' });
-
-// Video will have seriesId = Series.id foreign reference key
-// after we call Series.hasOne(Video)
-class Video extends Model {}
-Video.init({
- title: Sequelize.STRING,
- sequence: Sequelize.INTEGER,
- description: Sequelize.TEXT,
- // set relationship (hasOne) with `Series`
- seriesId: {
- type: DataTypes.INTEGER,
- references: {
- model: Series, // Can be both a string representing the table name or a Sequelize model
- key: 'id'
- }
- }
-}, { sequelize, modelName: 'video' });
-
-Series.hasOne(Video);
-Trainer.hasMany(Series);
-```
\ No newline at end of file
+---
+title: Constraints & Circularities
+---
+
+Adding constraints between tables means that tables must be created in the database in a certain order, when using `sequelize.sync`. If `Task` has a reference to `User`, the `User` table must be created before the `Task` table can be created. This can sometimes lead to circular references, where Sequelize cannot find an order in which to sync. Imagine a scenario of documents and versions. A document can have multiple versions, and for convenience, a document has a reference to its current version.
+
+```js
+const { Sequelize, Model, DataTypes } = require('sequelize');
+
+class Document extends Model {}
+Document.init(
+ {
+ author: DataTypes.STRING,
+ },
+ { sequelize, modelName: 'document' },
+);
+
+class Version extends Model {}
+Version.init(
+ {
+ timestamp: DataTypes.DATE,
+ },
+ { sequelize, modelName: 'version' },
+);
+
+Document.hasMany(Version); // This adds documentId attribute to version
+Document.belongsTo(Version, {
+ as: 'Current',
+ foreignKey: 'currentVersionId',
+}); // This adds currentVersionId attribute to document
+```
+
+However, unfortunately the code above will result in the following error:
+
+```text
+Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents
+```
+
+In order to alleviate that, we can pass `constraints: false` to one of the associations:
+
+```js
+Document.hasMany(Version);
+Document.belongsTo(Version, {
+ as: 'Current',
+ foreignKey: 'currentVersionId',
+ constraints: false,
+});
+```
+
+Which will allow us to sync the tables correctly:
+
+```sql
+CREATE TABLE IF NOT EXISTS "documents" (
+ "id" SERIAL,
+ "author" VARCHAR(255),
+ "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "currentVersionId" INTEGER,
+ PRIMARY KEY ("id")
+);
+
+CREATE TABLE IF NOT EXISTS "versions" (
+ "id" SERIAL,
+ "timestamp" TIMESTAMP WITH TIME ZONE,
+ "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "documentId" INTEGER REFERENCES "documents" ("id") ON DELETE
+ SET
+ NULL ON UPDATE CASCADE,
+ PRIMARY KEY ("id")
+);
+```
+
+## Enforcing a foreign key reference without constraints
+
+Sometimes you may want to reference another table, without adding any constraints, or associations. In that case you can manually add the reference attributes to your schema definition, and mark the relations between them.
+
+```js
+class Trainer extends Model {}
+Trainer.init(
+ {
+ firstName: Sequelize.STRING,
+ lastName: Sequelize.STRING,
+ },
+ { sequelize, modelName: 'trainer' },
+);
+
+// Series will have a trainerId = Trainer.id foreign reference key
+// after we call Trainer.hasMany(series)
+class Series extends Model {}
+Series.init(
+ {
+ title: Sequelize.STRING,
+ subTitle: Sequelize.STRING,
+ description: Sequelize.TEXT,
+ // Set FK relationship (hasMany) with `Trainer`
+ trainerId: {
+ type: DataTypes.INTEGER,
+ references: {
+ model: Trainer,
+ key: 'id',
+ },
+ },
+ },
+ { sequelize, modelName: 'series' },
+);
+
+// Video will have seriesId = Series.id foreign reference key
+// after we call Series.hasOne(Video)
+class Video extends Model {}
+Video.init(
+ {
+ title: Sequelize.STRING,
+ sequence: Sequelize.INTEGER,
+ description: Sequelize.TEXT,
+ // set relationship (hasOne) with `Series`
+ seriesId: {
+ type: DataTypes.INTEGER,
+ references: {
+ model: Series, // Can be both a string representing the table name or a Sequelize model
+ key: 'id',
+ },
+ },
+ },
+ { sequelize, modelName: 'video' },
+);
+
+Series.hasOne(Video);
+Trainer.hasMany(Series);
+```
diff --git a/versioned_docs/version-6.x.x/other-topics/dialect-specific-things.md b/versioned_docs/version-6.x.x/other-topics/dialect-specific-things.md
index a38fd479..57f0ef4f 100644
--- a/versioned_docs/version-6.x.x/other-topics/dialect-specific-things.md
+++ b/versioned_docs/version-6.x.x/other-topics/dialect-specific-things.md
@@ -15,8 +15,8 @@ const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mysql',
dialectOptions: {
// Your mysql2 options here
- }
-})
+ },
+});
```
`dialectOptions` are passed directly to the MySQL connection constructor. A full list of options can be found in the [MySQL docs](https://www.npmjs.com/package/mysql#connection-options).
@@ -33,7 +33,7 @@ const sequelize = new Sequelize('database', 'username', 'password', {
dialectOptions: {
// Your mariadb options here
// connectTimeout: 1000
- }
+ },
});
```
@@ -80,7 +80,7 @@ const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'postgres',
dialectOptions: {
// Your pg options here
- }
+ },
});
```
@@ -98,7 +98,7 @@ To connect over a unix domain socket, specify the path to the socket directory i
```js
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'postgres',
- host: '/path/to/socket_directory'
+ host: '/path/to/socket_directory',
});
```
@@ -116,8 +116,8 @@ const sequelize = new Sequelize('database', 'username', 'password', {
dialectOptions: {
// Your pg options here
// ...
- clientMinMessages: 'ignore' // case insensitive
- }
+ clientMinMessages: 'ignore', // case insensitive
+ },
});
```
@@ -135,9 +135,9 @@ const sequelize = new Sequelize('database', 'username', 'password', {
options: {
// Your tedious options here
useUTC: false,
- dateFirst: 1
- }
- }
+ dateFirst: 1,
+ },
+ },
});
```
@@ -156,14 +156,14 @@ const sequelize = new Sequelize('database', null, null, {
options: {
domain: 'yourDomain',
userName: 'username',
- password: 'password'
- }
+ password: 'password',
+ },
},
options: {
- instanceName: 'SQLEXPRESS'
- }
- }
-})
+ instanceName: 'SQLEXPRESS',
+ },
+ },
+});
```
### Snowflake (Experimental)
@@ -177,18 +177,18 @@ const sequelize = new Sequelize('database', null, null, {
dialect: 'snowflake',
dialectOptions: {
// put your snowflake account here,
- account: 'myAccount', // my-app.us-east-1
+ account: 'myAccount', // my-app.us-east-1
// below option should be optional
role: 'myRole',
warehouse: 'myWarehouse',
- schema: 'mySchema'
+ schema: 'mySchema',
},
// same as other dialect
username: 'myUserName',
password: 'myPassword',
- database: 'myDatabaseName'
-})
+ database: 'myDatabaseName',
+});
```
**NOTE** There is no test sandbox provided so the snowflake integration test is not part of the pipeline. Also it is difficult for core team to triage and debug. This dialect needs to be maintained by the snowflake user/community for now.
@@ -232,10 +232,11 @@ const sequelize = new Sequelize({
username: 'user',
password: 'password',
dialectOptions: {
- connectString: 'inst1'
- }
+ connectString: 'inst1',
+ },
});
```
+
Note that the `database`, `host` and `port` will be overriden and the values in connectString will be used for authentication.
Please refer to [Connect String](https://node-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#connectionstrings) for more about connect strings.
@@ -267,9 +268,9 @@ Table hints override the default behavior of MSSQL query optimizer by specifying
const { TableHints } = require('sequelize');
Project.findAll({
// adding the table hint NOLOCK
- tableHint: TableHints.NOLOCK
+ tableHint: TableHints.NOLOCK,
// this will generate the SQL 'WITH (NOLOCK)'
-})
+});
```
## Index Hints - MySQL/MariaDB only
@@ -279,19 +280,17 @@ The `indexHints` option can be used to define index hints. The hint type must be
Index hints [override the default behavior of the MySQL query optimizer](https://dev.mysql.com/doc/refman/5.7/en/index-hints.html).
```js
-const { IndexHints } = require("sequelize");
+const { IndexHints } = require('sequelize');
Project.findAll({
- indexHints: [
- { type: IndexHints.USE, values: ['index_project_on_name'] }
- ],
+ indexHints: [{ type: IndexHints.USE, values: ['index_project_on_name'] }],
where: {
id: {
- [Op.gt]: 623
+ [Op.gt]: 623,
},
name: {
- [Op.like]: 'Foo %'
- }
- }
+ [Op.like]: 'Foo %',
+ },
+ },
});
```
@@ -312,17 +311,23 @@ The default engine for a model is InnoDB.
You can change the engine for a model with the `engine` option (e.g., to MyISAM):
```js
-const Person = sequelize.define('person', { /* attributes */ }, {
- engine: 'MYISAM'
-});
+const Person = sequelize.define(
+ 'person',
+ {
+ /* attributes */
+ },
+ {
+ engine: 'MYISAM',
+ },
+);
```
Like every option for the definition of a model, this setting can also be changed globally with the `define` option of the Sequelize constructor:
```js
const sequelize = new Sequelize(db, user, pw, {
- define: { engine: 'MYISAM' }
-})
+ define: { engine: 'MYISAM' },
+});
```
## Table comments - MySQL/MariaDB/PostgreSQL only
@@ -331,10 +336,15 @@ You can specify a comment for a table when defining the model:
```js
class Person extends Model {}
-Person.init({ /* attributes */ }, {
- comment: "I'm a table comment!",
- sequelize
-})
+Person.init(
+ {
+ /* attributes */
+ },
+ {
+ comment: "I'm a table comment!",
+ sequelize,
+ },
+);
```
The comment will be set when calling `sync()`.
diff --git a/versioned_docs/version-6.x.x/other-topics/extending-data-types.md b/versioned_docs/version-6.x.x/other-topics/extending-data-types.md
index c8e1dbf5..b03cda42 100644
--- a/versioned_docs/version-6.x.x/other-topics/extending-data-types.md
+++ b/versioned_docs/version-6.x.x/other-topics/extending-data-types.md
@@ -18,16 +18,15 @@ createTheNewDataType();
const sequelize = new Sequelize('sqlite::memory:');
function createTheNewDataType() {
-
class SOMETYPE extends DataTypes.ABSTRACT {
// Mandatory: complete definition of the new type in the database
toSql() {
- return 'INTEGER(11) UNSIGNED ZEROFILL'
+ return 'INTEGER(11) UNSIGNED ZEROFILL';
}
// Optional: validator function
validate(value, options) {
- return (typeof value === 'number') && (!Number.isNaN(value));
+ return typeof value === 'number' && !Number.isNaN(value);
}
// Optional: sanitizer
@@ -56,7 +55,6 @@ function createTheNewDataType() {
// Optional: disable escaping after stringifier. Do this at your own risk, since this opens opportunity for SQL injections.
// DataTypes.SOMETYPE.escape = false;
-
}
```
diff --git a/versioned_docs/version-6.x.x/other-topics/hooks.md b/versioned_docs/version-6.x.x/other-topics/hooks.md
index feae9512..5804d748 100644
--- a/versioned_docs/version-6.x.x/other-topics/hooks.md
+++ b/versioned_docs/version-6.x.x/other-topics/hooks.md
@@ -59,23 +59,26 @@ There are currently three ways to programmatically add hooks:
```js
// Method 1 via the .init() method
class User extends Model {}
-User.init({
- username: DataTypes.STRING,
- mood: {
- type: DataTypes.ENUM,
- values: ['happy', 'sad', 'neutral']
- }
-}, {
- hooks: {
- beforeValidate: (user, options) => {
- user.mood = 'happy';
+User.init(
+ {
+ username: DataTypes.STRING,
+ mood: {
+ type: DataTypes.ENUM,
+ values: ['happy', 'sad', 'neutral'],
},
- afterValidate: (user, options) => {
- user.username = 'Toni';
- }
},
- sequelize
-});
+ {
+ hooks: {
+ beforeValidate: (user, options) => {
+ user.mood = 'happy';
+ },
+ afterValidate: (user, options) => {
+ user.username = 'Toni';
+ },
+ },
+ sequelize,
+ },
+);
// Method 2 via the .addHook() method
User.addHook('beforeValidate', (user, options) => {
@@ -103,9 +106,12 @@ Only a hook with name param can be removed.
```js
class Book extends Model {}
-Book.init({
- title: DataTypes.STRING
-}, { sequelize });
+Book.init(
+ {
+ title: DataTypes.STRING,
+ },
+ { sequelize },
+);
Book.addHook('afterCreate', 'notifyUsers', (book, options) => {
// ...
@@ -121,18 +127,31 @@ You can have many hooks with same name. Calling `.removeHook()` will remove all
Global hooks are hooks that are run for all models. They are especially useful for plugins and can define behaviours that you want for all your models, for example to allow customization on timestamps using `sequelize.define` on your models:
```js
-const User = sequelize.define('User', {}, {
+const User = sequelize.define(
+ 'User',
+ {},
+ {
tableName: 'users',
- hooks : {
- beforeCreate : (record, options) => {
- record.dataValues.createdAt = new Date().toISOString().replace(/T/, ' ').replace(/\..+/g, '');
- record.dataValues.updatedAt = new Date().toISOString().replace(/T/, ' ').replace(/\..+/g, '');
- },
- beforeUpdate : (record, options) => {
- record.dataValues.updatedAt = new Date().toISOString().replace(/T/, ' ').replace(/\..+/g, '');
- }
- }
-});
+ hooks: {
+ beforeCreate: (record, options) => {
+ record.dataValues.createdAt = new Date()
+ .toISOString()
+ .replace(/T/, ' ')
+ .replace(/\..+/g, '');
+ record.dataValues.updatedAt = new Date()
+ .toISOString()
+ .replace(/T/, ' ')
+ .replace(/\..+/g, '');
+ },
+ beforeUpdate: (record, options) => {
+ record.dataValues.updatedAt = new Date()
+ .toISOString()
+ .replace(/T/, ' ')
+ .replace(/\..+/g, '');
+ },
+ },
+ },
+);
```
They can be defined in many ways, which have slightly different semantics:
@@ -155,15 +174,19 @@ This adds a default hook to all models, which is run if the model does not defin
```js
const User = sequelize.define('User', {});
-const Project = sequelize.define('Project', {}, {
- hooks: {
- beforeCreate() {
- // Do other stuff
- }
- }
-});
+const Project = sequelize.define(
+ 'Project',
+ {},
+ {
+ hooks: {
+ beforeCreate() {
+ // Do other stuff
+ },
+ },
+ },
+);
-await User.create({}); // Runs the global hook
+await User.create({}); // Runs the global hook
await Project.create({}); // Runs its own hook (because the global hook is overwritten)
```
@@ -179,15 +202,19 @@ This hook is always run, whether or not the model specifies its own `beforeCreat
```js
const User = sequelize.define('User', {});
-const Project = sequelize.define('Project', {}, {
- hooks: {
- beforeCreate() {
- // Do other stuff
- }
- }
-});
+const Project = sequelize.define(
+ 'Project',
+ {},
+ {
+ hooks: {
+ beforeCreate() {
+ // Do other stuff
+ },
+ },
+ },
+);
-await User.create({}); // Runs the global hook
+await User.create({}); // Runs the global hook
await Project.create({}); // Runs its own hook, followed by the global hook
```
@@ -203,51 +230,52 @@ new Sequelize(..., {
});
```
-Note that the above is not the same as the *Default Hooks* mentioned above. That one uses the `define` option of the constructor. This one does not.
+Note that the above is not the same as the _Default Hooks_ mentioned above. That one uses the `define` option of the constructor. This one does not.
### Connection Hooks
Sequelize provides four hooks that are executed immediately before and after a database connection is obtained or released:
-* `sequelize.beforeConnect(callback)`
- * The callback has the form `async (config) => /* ... */`
-* `sequelize.afterConnect(callback)`
- * The callback has the form `async (connection, config) => /* ... */`
-* `sequelize.beforeDisconnect(callback)`
- * The callback has the form `async (connection) => /* ... */`
-* `sequelize.afterDisconnect(callback)`
- * The callback has the form `async (connection) => /* ... */`
+- `sequelize.beforeConnect(callback)`
+ - The callback has the form `async (config) => /* ... */`
+- `sequelize.afterConnect(callback)`
+ - The callback has the form `async (connection, config) => /* ... */`
+- `sequelize.beforeDisconnect(callback)`
+ - The callback has the form `async (connection) => /* ... */`
+- `sequelize.afterDisconnect(callback)`
+ - The callback has the form `async (connection) => /* ... */`
These hooks can be useful if you need to asynchronously obtain database credentials, or need to directly access the low-level database connection after it has been created.
For example, we can asynchronously obtain a database password from a rotating token store, and mutate Sequelize's configuration object with the new credentials:
```js
-sequelize.beforeConnect(async (config) => {
+sequelize.beforeConnect(async config => {
config.password = await getAuthToken();
});
```
+
You can also use two hooks that are executed immediately before and after a pool connection is acquired:
-* `sequelize.beforePoolAcquire(callback)`
- * The callback has the form `async (config) => /* ... */`
-* `sequelize.afterPoolAcquire(callback)`
- * The callback has the form `async (connection, config) => /* ... */`
+- `sequelize.beforePoolAcquire(callback)`
+ - The callback has the form `async (config) => /* ... */`
+- `sequelize.afterPoolAcquire(callback)`
+ - The callback has the form `async (connection, config) => /* ... */`
-These hooks may *only* be declared as a permanent global hook, as the connection pool is shared by all models.
+These hooks may _only_ be declared as a permanent global hook, as the connection pool is shared by all models.
## Instance hooks
The following hooks will emit whenever you're editing a single object:
-* `beforeValidate`
-* `afterValidate` / `validationFailed`
-* `beforeCreate` / `beforeUpdate` / `beforeSave` / `beforeDestroy`
-* `afterCreate` / `afterUpdate` / `afterSave` / `afterDestroy`
+- `beforeValidate`
+- `afterValidate` / `validationFailed`
+- `beforeCreate` / `beforeUpdate` / `beforeSave` / `beforeDestroy`
+- `afterCreate` / `afterUpdate` / `afterSave` / `afterDestroy`
```js
User.beforeCreate(user => {
- if (user.accessLevel > 10 && user.username !== "Boss") {
+ if (user.accessLevel > 10 && user.username !== 'Boss') {
throw new Error("You can't grant this user an access level above 10!");
}
});
@@ -260,7 +288,7 @@ try {
await User.create({ username: 'Not a Boss', accessLevel: 20 });
} catch (error) {
console.log(error); // You can't grant this user an access level above 10!
-};
+}
```
The following example will be successful:
@@ -274,32 +302,35 @@ console.log(user); // user object with username 'Boss' and accessLevel of 20
Sometimes you'll be editing more than one record at a time by using methods like `bulkCreate`, `update` and `destroy`. The following hooks will emit whenever you're using one of those methods:
-* `YourModel.beforeBulkCreate(callback)`
- * The callback has the form `(instances, options) => /* ... */`
-* `YourModel.beforeBulkUpdate(callback)`
- * The callback has the form `(options) => /* ... */`
-* `YourModel.beforeBulkDestroy(callback)`
- * The callback has the form `(options) => /* ... */`
-* `YourModel.afterBulkCreate(callback)`
- * The callback has the form `(instances, options) => /* ... */`
-* `YourModel.afterBulkUpdate(callback)`
- * The callback has the form `(options) => /* ... */`
-* `YourModel.afterBulkDestroy(callback)`
- * The callback has the form `(options) => /* ... */`
+- `YourModel.beforeBulkCreate(callback)`
+ - The callback has the form `(instances, options) => /* ... */`
+- `YourModel.beforeBulkUpdate(callback)`
+ - The callback has the form `(options) => /* ... */`
+- `YourModel.beforeBulkDestroy(callback)`
+ - The callback has the form `(options) => /* ... */`
+- `YourModel.afterBulkCreate(callback)`
+ - The callback has the form `(instances, options) => /* ... */`
+- `YourModel.afterBulkUpdate(callback)`
+ - The callback has the form `(options) => /* ... */`
+- `YourModel.afterBulkDestroy(callback)`
+ - The callback has the form `(options) => /* ... */`
Note: methods like `bulkCreate` do not emit individual hooks by default - only the bulk hooks. However, if you want individual hooks to be emitted as well, you can pass the `{ individualHooks: true }` option to the query call. However, this can drastically impact performance, depending on the number of records involved (since, among other things, all instances will be loaded into memory). Examples:
```js
await Model.destroy({
where: { accessLevel: 0 },
- individualHooks: true
+ individualHooks: true,
});
// This will select all records that are about to be deleted and emit `beforeDestroy` and `afterDestroy` on each instance.
-await Model.update({ username: 'Tony' }, {
- where: { accessLevel: 0 },
- individualHooks: true
-});
+await Model.update(
+ { username: 'Tony' },
+ {
+ where: { accessLevel: 0 },
+ individualHooks: true,
+ },
+);
// This will select all records that are about to be updated and emit `beforeUpdate` and `afterUpdate` on each instance.
```
@@ -320,17 +351,20 @@ User.beforeBulkCreate((users, options) => {
});
// Bulk updating existing users with updateOnDuplicate option
-await Users.bulkCreate([
- { id: 1, isMember: true },
- { id: 2, isMember: false }
-], {
- updateOnDuplicate: ['isMember']
-});
+await Users.bulkCreate(
+ [
+ { id: 1, isMember: true },
+ { id: 2, isMember: false },
+ ],
+ {
+ updateOnDuplicate: ['isMember'],
+ },
+);
```
## Exceptions
-Only __Model methods__ trigger hooks. This means there are a number of cases where Sequelize will interact with the database without triggering hooks.
+Only **Model methods** trigger hooks. This means there are a number of cases where Sequelize will interact with the database without triggering hooks.
These include but are not limited to:
- Instances being deleted by the database because of an `ON DELETE CASCADE` constraint, [except if the `hooks` option is true](#hooks-for-cascade-deletes).
@@ -353,7 +387,7 @@ Using this option is discouraged for the following reasons:
- This option requires many extra queries. The `destroy` method normally executes a single query.
If this option is enabled, an extra `SELECT` query, as well as an extra `DELETE` query for each row returned by the select will be executed.
- If you do not run this query in a transaction, and an error occurs, you may end up with some rows deleted and some not deleted.
-- This option only works when the *instance* version of `destroy` is used. The static version will not trigger the hooks, even with `individualHooks`.
+- This option only works when the _instance_ version of `destroy` is used. The static version will not trigger the hooks, even with `individualHooks`.
- This option will not work in `paranoid` mode.
- This option will not work if you only define the association on the model that owns the foreign key. You need to define the reverse association as well.
@@ -366,7 +400,9 @@ Here is an example of how to use this option:
```ts
import { Model } from 'sequelize';
-const sequelize = new Sequelize({ /* options */ });
+const sequelize = new Sequelize({
+ /* options */
+});
class User extends Model {}
@@ -398,41 +434,48 @@ For the most part hooks will work the same for instances when being associated.
### One-to-One and One-to-Many associations
-* When using `add`/`set` mixin methods the `beforeUpdate` and `afterUpdate` hooks will run.
+- When using `add`/`set` mixin methods the `beforeUpdate` and `afterUpdate` hooks will run.
### Many-to-Many associations
-* When using `add` mixin methods for `belongsToMany` relationships (that will add one or more records to the junction table) the `beforeBulkCreate` and `afterBulkCreate` hooks in the junction model will run.
- * If `{ individualHooks: true }` was passed to the call, then each individual hook will also run.
+- When using `add` mixin methods for `belongsToMany` relationships (that will add one or more records to the junction table) the `beforeBulkCreate` and `afterBulkCreate` hooks in the junction model will run.
+
+ - If `{ individualHooks: true }` was passed to the call, then each individual hook will also run.
-* When using `remove` mixin methods for `belongsToMany` relationships (that will remove one or more records to the junction table) the `beforeBulkDestroy` and `afterBulkDestroy` hooks in the junction model will run.
- * If `{ individualHooks: true }` was passed to the call, then each individual hook will also run.
+- When using `remove` mixin methods for `belongsToMany` relationships (that will remove one or more records to the junction table) the `beforeBulkDestroy` and `afterBulkDestroy` hooks in the junction model will run.
+ - If `{ individualHooks: true }` was passed to the call, then each individual hook will also run.
If your association is Many-to-Many, you may be interested in firing hooks on the through model when using the `remove` call. Internally, sequelize is using `Model.destroy` resulting in calling the `bulkDestroy` instead of the `before/afterDestroy` hooks on each through instance.
## Hooks and Transactions
-Many model operations in Sequelize allow you to specify a transaction in the options parameter of the method. If a transaction *is* specified in the original call, it will be present in the options parameter passed to the hook function. For example, consider the following snippet:
+Many model operations in Sequelize allow you to specify a transaction in the options parameter of the method. If a transaction _is_ specified in the original call, it will be present in the options parameter passed to the hook function. For example, consider the following snippet:
```js
User.addHook('afterCreate', async (user, options) => {
// We can use `options.transaction` to perform some other call
// using the same transaction of the call that triggered this hook
- await User.update({ mood: 'sad' }, {
- where: {
- id: user.id
+ await User.update(
+ { mood: 'sad' },
+ {
+ where: {
+ id: user.id,
+ },
+ transaction: options.transaction,
},
- transaction: options.transaction
- });
+ );
});
await sequelize.transaction(async t => {
- await User.create({
- username: 'someguy',
- mood: 'happy'
- }, {
- transaction: t
- });
+ await User.create(
+ {
+ username: 'someguy',
+ mood: 'happy',
+ },
+ {
+ transaction: t,
+ },
+ );
});
```
@@ -442,7 +485,7 @@ If we had not included the transaction option in our call to `User.update` in th
It is very important to recognize that sequelize may make use of transactions internally for certain operations such as `Model.findOrCreate`. If your hook functions execute read or write operations that rely on the object's presence in the database, or modify the object's stored values like the example in the preceding section, you should always specify `{ transaction: options.transaction }`:
-* If a transaction was used, then `{ transaction: options.transaction }` will ensure it is used again;
-* Otherwise, `{ transaction: options.transaction }` will be equivalent to `{ transaction: undefined }`, which won't use a transaction (which is ok).
+- If a transaction was used, then `{ transaction: options.transaction }` will ensure it is used again;
+- Otherwise, `{ transaction: options.transaction }` will be equivalent to `{ transaction: undefined }`, which won't use a transaction (which is ok).
This way your hooks will always behave correctly.
diff --git a/versioned_docs/version-6.x.x/other-topics/indexes.md b/versioned_docs/version-6.x.x/other-topics/indexes.md
index c431f940..8dc43017 100644
--- a/versioned_docs/version-6.x.x/other-topics/indexes.md
+++ b/versioned_docs/version-6.x.x/other-topics/indexes.md
@@ -5,45 +5,51 @@ title: Indexes
Sequelize supports adding indexes to the model definition which will be created on [`sequelize.sync()`](pathname:///api/v6/class/src/sequelize.js~Sequelize.html#instance-method-sync).
```js
-const User = sequelize.define('User', { /* attributes */ }, {
- indexes: [
- // Create a unique index on email
- {
- unique: true,
- fields: ['email']
- },
+const User = sequelize.define(
+ 'User',
+ {
+ /* attributes */
+ },
+ {
+ indexes: [
+ // Create a unique index on email
+ {
+ unique: true,
+ fields: ['email'],
+ },
- // Creates a gin index on data with the jsonb_path_ops operator
- {
- fields: ['data'],
- using: 'gin',
- operator: 'jsonb_path_ops'
- },
+ // Creates a gin index on data with the jsonb_path_ops operator
+ {
+ fields: ['data'],
+ using: 'gin',
+ operator: 'jsonb_path_ops',
+ },
- // By default index name will be [table]_[fields]
- // Creates a multi column partial index
- {
- name: 'public_by_author',
- fields: ['author', 'status'],
- where: {
- status: 'public'
- }
- },
+ // By default index name will be [table]_[fields]
+ // Creates a multi column partial index
+ {
+ name: 'public_by_author',
+ fields: ['author', 'status'],
+ where: {
+ status: 'public',
+ },
+ },
- // A BTREE index with an ordered field
- {
- name: 'title_index',
- using: 'BTREE',
- fields: [
- 'author',
- {
- name: 'title',
- collate: 'en_US',
- order: 'DESC',
- length: 5
- }
- ]
- }
- ]
-});
+ // A BTREE index with an ordered field
+ {
+ name: 'title_index',
+ using: 'BTREE',
+ fields: [
+ 'author',
+ {
+ name: 'title',
+ collate: 'en_US',
+ order: 'DESC',
+ length: 5,
+ },
+ ],
+ },
+ ],
+ },
+);
```
diff --git a/versioned_docs/version-6.x.x/other-topics/legacy.md b/versioned_docs/version-6.x.x/other-topics/legacy.md
index 7e513314..201d5476 100644
--- a/versioned_docs/version-6.x.x/other-topics/legacy.md
+++ b/versioned_docs/version-6.x.x/other-topics/legacy.md
@@ -8,25 +8,31 @@ While out of the box Sequelize will seem a bit opinionated it's easy to work leg
```js
class User extends Model {}
-User.init({
- // ...
-}, {
- modelName: 'user',
- tableName: 'users',
- sequelize,
-});
+User.init(
+ {
+ // ...
+ },
+ {
+ modelName: 'user',
+ tableName: 'users',
+ sequelize,
+ },
+);
```
## Fields
```js
class MyModel extends Model {}
-MyModel.init({
- userId: {
- type: DataTypes.INTEGER,
- field: 'user_id'
- }
-}, { sequelize });
+MyModel.init(
+ {
+ userId: {
+ type: DataTypes.INTEGER,
+ field: 'user_id',
+ },
+ },
+ { sequelize },
+);
```
## Primary keys
@@ -37,21 +43,27 @@ To define your own primary key:
```js
class Collection extends Model {}
-Collection.init({
- uid: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true // Automatically gets converted to SERIAL for postgres
- }
-}, { sequelize });
+Collection.init(
+ {
+ uid: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true, // Automatically gets converted to SERIAL for postgres
+ },
+ },
+ { sequelize },
+);
class Collection extends Model {}
-Collection.init({
- uuid: {
- type: DataTypes.UUID,
- primaryKey: true
- }
-}, { sequelize });
+Collection.init(
+ {
+ uuid: {
+ type: DataTypes.UUID,
+ primaryKey: true,
+ },
+ },
+ { sequelize },
+);
```
And if your model has no primary key at all you can use `Model.removeAttribute('id');`
@@ -86,6 +98,12 @@ Project.hasMany(Task, { foreignKey: 'tasks_pk' });
Task.belongsTo(Project, { foreignKey: 'tasks_pk' });
// N:M
-User.belongsToMany(Role, { through: 'user_has_roles', foreignKey: 'user_role_user_id' });
-Role.belongsToMany(User, { through: 'user_has_roles', foreignKey: 'roles_identifier' });
+User.belongsToMany(Role, {
+ through: 'user_has_roles',
+ foreignKey: 'user_role_user_id',
+});
+Role.belongsToMany(User, {
+ through: 'user_has_roles',
+ foreignKey: 'roles_identifier',
+});
```
diff --git a/versioned_docs/version-6.x.x/other-topics/migrations.md b/versioned_docs/version-6.x.x/other-topics/migrations.md
index f5163d53..e8c6a217 100644
--- a/versioned_docs/version-6.x.x/other-topics/migrations.md
+++ b/versioned_docs/version-6.x.x/other-topics/migrations.md
@@ -142,17 +142,19 @@ Now we should edit this file to insert demo user to `User` table.
```js
module.exports = {
up: (queryInterface, Sequelize) => {
- return queryInterface.bulkInsert('Users', [{
- firstName: 'John',
- lastName: 'Doe',
- email: 'example@example.com',
- createdAt: new Date(),
- updatedAt: new Date()
- }]);
+ return queryInterface.bulkInsert('Users', [
+ {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'example@example.com',
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ },
+ ]);
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('Users', null, {});
- }
+ },
};
```
@@ -201,8 +203,8 @@ module.exports = {
},
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
- }
-}
+ },
+};
```
We can generate this file using `migration:generate`. This will create `xxx-migration-skeleton.js` in your migration folder.
@@ -221,13 +223,13 @@ module.exports = {
isBetaMember: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
- allowNull: false
- }
+ allowNull: false,
+ },
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
- }
+ },
};
```
@@ -238,12 +240,22 @@ module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(t => {
return Promise.all([
- queryInterface.addColumn('Person', 'petName', {
- type: Sequelize.DataTypes.STRING
- }, { transaction: t }),
- queryInterface.addColumn('Person', 'favoriteColor', {
- type: Sequelize.DataTypes.STRING,
- }, { transaction: t })
+ queryInterface.addColumn(
+ 'Person',
+ 'petName',
+ {
+ type: Sequelize.DataTypes.STRING,
+ },
+ { transaction: t },
+ ),
+ queryInterface.addColumn(
+ 'Person',
+ 'favoriteColor',
+ {
+ type: Sequelize.DataTypes.STRING,
+ },
+ { transaction: t },
+ ),
]);
});
},
@@ -251,10 +263,12 @@ module.exports = {
return queryInterface.sequelize.transaction(t => {
return Promise.all([
queryInterface.removeColumn('Person', 'petName', { transaction: t }),
- queryInterface.removeColumn('Person', 'favoriteColor', { transaction: t })
+ queryInterface.removeColumn('Person', 'favoriteColor', {
+ transaction: t,
+ }),
]);
});
- }
+ },
};
```
@@ -268,25 +282,25 @@ module.exports = {
isBetaMember: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
- allowNull: false
+ allowNull: false,
},
userId: {
type: Sequelize.DataTypes.INTEGER,
references: {
model: {
tableName: 'users',
- schema: 'schema'
+ schema: 'schema',
},
- key: 'id'
+ key: 'id',
},
- allowNull: false
+ allowNull: false,
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
- }
-}
+ },
+};
```
The next example is of a migration that uses async/await where you create an unique index on a new column, with a manually-managed transaction:
@@ -302,17 +316,13 @@ module.exports = {
{
type: Sequelize.DataTypes.STRING,
},
- { transaction }
- );
- await queryInterface.addIndex(
- 'Person',
- 'petName',
- {
- fields: 'petName',
- unique: true,
- transaction,
- }
+ { transaction },
);
+ await queryInterface.addIndex('Person', 'petName', {
+ fields: 'petName',
+ unique: true,
+ transaction,
+ });
await transaction.commit();
} catch (err) {
await transaction.rollback();
@@ -328,7 +338,7 @@ module.exports = {
await transaction.rollback();
throw err;
}
- }
+ },
};
```
@@ -337,27 +347,25 @@ The next example is of a migration that creates an unique index composed of mult
```js
module.exports = {
up: (queryInterface, Sequelize) => {
- queryInterface.createTable('Person', {
- name: Sequelize.DataTypes.STRING,
- bool: {
- type: Sequelize.DataTypes.BOOLEAN,
- defaultValue: false
- }
- }).then((queryInterface, Sequelize) => {
- queryInterface.addIndex(
- 'Person',
- ['name', 'bool'],
- {
+ queryInterface
+ .createTable('Person', {
+ name: Sequelize.DataTypes.STRING,
+ bool: {
+ type: Sequelize.DataTypes.BOOLEAN,
+ defaultValue: false,
+ },
+ })
+ .then((queryInterface, Sequelize) => {
+ queryInterface.addIndex('Person', ['name', 'bool'], {
indicesType: 'UNIQUE',
- where: { bool : 'true' },
- }
- );
- });
+ where: { bool: 'true' },
+ });
+ });
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
- }
-}
+ },
+};
```
### The `.sequelizerc` file
@@ -388,10 +396,10 @@ To begin, let's create the `.sequelizerc` file in the root directory of your pro
const path = require('path');
module.exports = {
- 'config': path.resolve('config', 'database.json'),
+ config: path.resolve('config', 'database.json'),
'models-path': path.resolve('db', 'models'),
'seeders-path': path.resolve('db', 'seeders'),
- 'migrations-path': path.resolve('db', 'migrations')
+ 'migrations-path': path.resolve('db', 'migrations'),
};
```
@@ -412,8 +420,8 @@ Thankfully, the Sequelize CLI can read from both `.json` and `.js` files. This c
const path = require('path');
module.exports = {
- 'config': path.resolve('config', 'config.js')
-}
+ config: path.resolve('config', 'config.js'),
+};
```
Now the Sequelize CLI will load `config/config.js` for getting configuration options.
@@ -432,8 +440,8 @@ module.exports = {
port: 3306,
dialect: 'mysql',
dialectOptions: {
- bigNumberStrings: true
- }
+ bigNumberStrings: true,
+ },
},
test: {
username: process.env.CI_DB_USERNAME,
@@ -443,8 +451,8 @@ module.exports = {
port: 3306,
dialect: 'mysql',
dialectOptions: {
- bigNumberStrings: true
- }
+ bigNumberStrings: true,
+ },
},
production: {
username: process.env.PROD_DB_USERNAME,
@@ -456,10 +464,10 @@ module.exports = {
dialectOptions: {
bigNumberStrings: true,
ssl: {
- ca: fs.readFileSync(__dirname + '/mysql-ca-main.crt')
- }
- }
- }
+ ca: fs.readFileSync(__dirname + '/mysql-ca-main.crt'),
+ },
+ },
+ },
};
```
@@ -476,16 +484,16 @@ npm i --save-dev babel-register
```js
// .sequelizerc
-require("babel-register");
+require('babel-register');
const path = require('path');
module.exports = {
- 'config': path.resolve('config', 'config.json'),
+ config: path.resolve('config', 'config.json'),
'models-path': path.resolve('models'),
'seeders-path': path.resolve('seeders'),
- 'migrations-path': path.resolve('migrations')
-}
+ 'migrations-path': path.resolve('migrations'),
+};
```
Of course, the outcome will depend upon your babel configuration (such as in a `.babelrc` file). Learn more at [babeljs.io](https://babeljs.io).
diff --git a/versioned_docs/version-6.x.x/other-topics/naming-strategies.md b/versioned_docs/version-6.x.x/other-topics/naming-strategies.md
index 2a60cba6..89398f69 100644
--- a/versioned_docs/version-6.x.x/other-topics/naming-strategies.md
+++ b/versioned_docs/version-6.x.x/other-topics/naming-strategies.md
@@ -1,159 +1,176 @@
----
-title: Naming Strategies
----
-
-## The `underscored` option
-
-Sequelize provides the `underscored` option for a model. When `true`, this option will set the `field` option on all attributes to the [snake_case](https://en.wikipedia.org/wiki/Snake_case) version of its name. This also applies to foreign keys automatically generated by associations and other automatically generated fields. Example:
-
-```js
-const User = sequelize.define('user', { username: Sequelize.STRING }, {
- underscored: true
-});
-const Task = sequelize.define('task', { title: Sequelize.STRING }, {
- underscored: true
-});
-User.hasMany(Task);
-Task.belongsTo(User);
-```
-
-Above we have the models User and Task, both using the `underscored` option. We also have a One-to-Many relationship between them. Also, recall that since `timestamps` is true by default, we should expect the `createdAt` and `updatedAt` fields to be automatically created as well.
-
-Without the `underscored` option, Sequelize would automatically define:
-
-* A `createdAt` attribute for each model, pointing to a column named `createdAt` in each table
-* An `updatedAt` attribute for each model, pointing to a column named `updatedAt` in each table
-* A `userId` attribute in the `Task` model, pointing to a column named `userId` in the task table
-
-With the `underscored` option enabled, Sequelize will instead define:
-
-* A `createdAt` attribute for each model, pointing to a column named `created_at` in each table
-* An `updatedAt` attribute for each model, pointing to a column named `updated_at` in each table
-* A `userId` attribute in the `Task` model, pointing to a column named `user_id` in the task table
-
-Note that in both cases the fields are still [camelCase](https://en.wikipedia.org/wiki/Camel_case) in the JavaScript side; this option only changes how these fields are mapped to the database itself. The `field` option of every attribute is set to their snake_case version, but the attribute itself remains camelCase.
-
-This way, calling `sync()` on the above code will generate the following:
-
-```sql
-CREATE TABLE IF NOT EXISTS "users" (
- "id" SERIAL,
- "username" VARCHAR(255),
- "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- PRIMARY KEY ("id")
-);
-CREATE TABLE IF NOT EXISTS "tasks" (
- "id" SERIAL,
- "title" VARCHAR(255),
- "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- "user_id" INTEGER REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
- PRIMARY KEY ("id")
-);
-```
-
-## Singular vs. Plural
-
-At a first glance, it can be confusing whether the singular form or plural form of a name shall be used around in Sequelize. This section aims at clarifying that a bit.
-
-Recall that Sequelize uses a library called [inflection](https://www.npmjs.com/package/inflection) under the hood, so that irregular plurals (such as `person -> people`) are computed correctly. However, if you're working in another language, you may want to define the singular and plural forms of names directly; sequelize allows you to do this with some options.
-
-### When defining models
-
-Models should be defined with the singular form of a word. Example:
-
-```js
-sequelize.define('foo', { name: DataTypes.STRING });
-```
-
-Above, the model name is `foo` (singular), and the respective table name is `foos`, since Sequelize automatically gets the plural for the table name.
-
-### When defining a reference key in a model
-
-```js
-sequelize.define('foo', {
- name: DataTypes.STRING,
- barId: {
- type: DataTypes.INTEGER,
- allowNull: false,
- references: {
- model: "bars",
- key: "id"
- },
- onDelete: "CASCADE"
- },
-});
-```
-
-In the above example we are manually defining a key that references another model. It's not usual to do this, but if you have to, you should use the table name there. This is because the reference is created upon the referenced table name. In the example above, the plural form was used (`bars`), assuming that the `bar` model was created with the default settings (making its underlying table automatically pluralized).
-
-### When retrieving data from eager loading
-
-When you perform an `include` in a query, the included data will be added to an extra field in the returned objects, according to the following rules:
-
-* When including something from a single association (`hasOne` or `belongsTo`) - the field name will be the singular version of the model name;
-* When including something from a multiple association (`hasMany` or `belongsToMany`) - the field name will be the plural form of the model.
-
-In short, the name of the field will take the most logical form in each situation.
-
-Examples:
-
-```js
-// Assuming Foo.hasMany(Bar)
-const foo = Foo.findOne({ include: Bar });
-// foo.bars will be an array
-// foo.bar will not exist since it doens't make sense
-
-// Assuming Foo.hasOne(Bar)
-const foo = Foo.findOne({ include: Bar });
-// foo.bar will be an object (possibly null if there is no associated model)
-// foo.bars will not exist since it doens't make sense
-
-// And so on.
-```
-
-### Overriding singulars and plurals when defining aliases
-
-When defining an alias for an association, instead of using simply `{ as: 'myAlias' }`, you can pass an object to specify the singular and plural forms:
-
-```js
-Project.belongsToMany(User, {
- as: {
- singular: 'líder',
- plural: 'líderes'
- }
-});
-```
-
-If you know that a model will always use the same alias in associations, you can provide the singular and plural forms directly to the model itself:
-
-```js
-const User = sequelize.define('user', { /* ... */ }, {
- name: {
- singular: 'líder',
- plural: 'líderes',
- }
-});
-Project.belongsToMany(User);
-```
-
-The mixins added to the user instances will use the correct forms. For example, instead of `project.addUser()`, Sequelize will provide `project.getLíder()`. Also, instead of `project.setUsers()`, Sequelize will provide `project.setLíderes()`.
-
-Note: recall that using `as` to change the name of the association will also change the name of the foreign key. Therefore it is recommended to also specify the foreign key(s) involved directly in this case.
-
-```js
-// Example of possible mistake
-Invoice.belongsTo(Subscription, { as: 'TheSubscription' });
-Subscription.hasMany(Invoice);
-```
-
-The first call above will establish a foreign key called `theSubscriptionId` on `Invoice`. However, the second call will also establish a foreign key on `Invoice` (since as we know, `hasMany` calls places foreign keys in the target model) - however, it will be named `subscriptionId`. This way you will have both `subscriptionId` and `theSubscriptionId` columns.
-
-The best approach is to choose a name for the foreign key and place it explicitly in both calls. For example, if `subscription_id` was chosen:
-
-```js
-// Fixed example
-Invoice.belongsTo(Subscription, { as: 'TheSubscription', foreignKey: 'subscription_id' });
-Subscription.hasMany(Invoice, { foreignKey: 'subscription_id' });
-```
+---
+title: Naming Strategies
+---
+
+## The `underscored` option
+
+Sequelize provides the `underscored` option for a model. When `true`, this option will set the `field` option on all attributes to the [snake_case](https://en.wikipedia.org/wiki/Snake_case) version of its name. This also applies to foreign keys automatically generated by associations and other automatically generated fields. Example:
+
+```js
+const User = sequelize.define(
+ 'user',
+ { username: Sequelize.STRING },
+ {
+ underscored: true,
+ },
+);
+const Task = sequelize.define(
+ 'task',
+ { title: Sequelize.STRING },
+ {
+ underscored: true,
+ },
+);
+User.hasMany(Task);
+Task.belongsTo(User);
+```
+
+Above we have the models User and Task, both using the `underscored` option. We also have a One-to-Many relationship between them. Also, recall that since `timestamps` is true by default, we should expect the `createdAt` and `updatedAt` fields to be automatically created as well.
+
+Without the `underscored` option, Sequelize would automatically define:
+
+- A `createdAt` attribute for each model, pointing to a column named `createdAt` in each table
+- An `updatedAt` attribute for each model, pointing to a column named `updatedAt` in each table
+- A `userId` attribute in the `Task` model, pointing to a column named `userId` in the task table
+
+With the `underscored` option enabled, Sequelize will instead define:
+
+- A `createdAt` attribute for each model, pointing to a column named `created_at` in each table
+- An `updatedAt` attribute for each model, pointing to a column named `updated_at` in each table
+- A `userId` attribute in the `Task` model, pointing to a column named `user_id` in the task table
+
+Note that in both cases the fields are still [camelCase](https://en.wikipedia.org/wiki/Camel_case) in the JavaScript side; this option only changes how these fields are mapped to the database itself. The `field` option of every attribute is set to their snake_case version, but the attribute itself remains camelCase.
+
+This way, calling `sync()` on the above code will generate the following:
+
+```sql
+CREATE TABLE IF NOT EXISTS "users" (
+ "id" SERIAL,
+ "username" VARCHAR(255),
+ "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ PRIMARY KEY ("id")
+);
+CREATE TABLE IF NOT EXISTS "tasks" (
+ "id" SERIAL,
+ "title" VARCHAR(255),
+ "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "user_id" INTEGER REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
+ PRIMARY KEY ("id")
+);
+```
+
+## Singular vs. Plural
+
+At a first glance, it can be confusing whether the singular form or plural form of a name shall be used around in Sequelize. This section aims at clarifying that a bit.
+
+Recall that Sequelize uses a library called [inflection](https://www.npmjs.com/package/inflection) under the hood, so that irregular plurals (such as `person -> people`) are computed correctly. However, if you're working in another language, you may want to define the singular and plural forms of names directly; sequelize allows you to do this with some options.
+
+### When defining models
+
+Models should be defined with the singular form of a word. Example:
+
+```js
+sequelize.define('foo', { name: DataTypes.STRING });
+```
+
+Above, the model name is `foo` (singular), and the respective table name is `foos`, since Sequelize automatically gets the plural for the table name.
+
+### When defining a reference key in a model
+
+```js
+sequelize.define('foo', {
+ name: DataTypes.STRING,
+ barId: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'bars',
+ key: 'id',
+ },
+ onDelete: 'CASCADE',
+ },
+});
+```
+
+In the above example we are manually defining a key that references another model. It's not usual to do this, but if you have to, you should use the table name there. This is because the reference is created upon the referenced table name. In the example above, the plural form was used (`bars`), assuming that the `bar` model was created with the default settings (making its underlying table automatically pluralized).
+
+### When retrieving data from eager loading
+
+When you perform an `include` in a query, the included data will be added to an extra field in the returned objects, according to the following rules:
+
+- When including something from a single association (`hasOne` or `belongsTo`) - the field name will be the singular version of the model name;
+- When including something from a multiple association (`hasMany` or `belongsToMany`) - the field name will be the plural form of the model.
+
+In short, the name of the field will take the most logical form in each situation.
+
+Examples:
+
+```js
+// Assuming Foo.hasMany(Bar)
+const foo = Foo.findOne({ include: Bar });
+// foo.bars will be an array
+// foo.bar will not exist since it doens't make sense
+
+// Assuming Foo.hasOne(Bar)
+const foo = Foo.findOne({ include: Bar });
+// foo.bar will be an object (possibly null if there is no associated model)
+// foo.bars will not exist since it doens't make sense
+
+// And so on.
+```
+
+### Overriding singulars and plurals when defining aliases
+
+When defining an alias for an association, instead of using simply `{ as: 'myAlias' }`, you can pass an object to specify the singular and plural forms:
+
+```js
+Project.belongsToMany(User, {
+ as: {
+ singular: 'líder',
+ plural: 'líderes',
+ },
+});
+```
+
+If you know that a model will always use the same alias in associations, you can provide the singular and plural forms directly to the model itself:
+
+```js
+const User = sequelize.define(
+ 'user',
+ {
+ /* ... */
+ },
+ {
+ name: {
+ singular: 'líder',
+ plural: 'líderes',
+ },
+ },
+);
+Project.belongsToMany(User);
+```
+
+The mixins added to the user instances will use the correct forms. For example, instead of `project.addUser()`, Sequelize will provide `project.getLíder()`. Also, instead of `project.setUsers()`, Sequelize will provide `project.setLíderes()`.
+
+Note: recall that using `as` to change the name of the association will also change the name of the foreign key. Therefore it is recommended to also specify the foreign key(s) involved directly in this case.
+
+```js
+// Example of possible mistake
+Invoice.belongsTo(Subscription, { as: 'TheSubscription' });
+Subscription.hasMany(Invoice);
+```
+
+The first call above will establish a foreign key called `theSubscriptionId` on `Invoice`. However, the second call will also establish a foreign key on `Invoice` (since as we know, `hasMany` calls places foreign keys in the target model) - however, it will be named `subscriptionId`. This way you will have both `subscriptionId` and `theSubscriptionId` columns.
+
+The best approach is to choose a name for the foreign key and place it explicitly in both calls. For example, if `subscription_id` was chosen:
+
+```js
+// Fixed example
+Invoice.belongsTo(Subscription, {
+ as: 'TheSubscription',
+ foreignKey: 'subscription_id',
+});
+Subscription.hasMany(Invoice, { foreignKey: 'subscription_id' });
+```
diff --git a/versioned_docs/version-6.x.x/other-topics/optimistic-locking.md b/versioned_docs/version-6.x.x/other-topics/optimistic-locking.md
index c099f8aa..eeb4cc5e 100644
--- a/versioned_docs/version-6.x.x/other-topics/optimistic-locking.md
+++ b/versioned_docs/version-6.x.x/other-topics/optimistic-locking.md
@@ -1,9 +1,9 @@
----
-title: Optimistic Locking
----
-
-Sequelize has built-in support for optimistic locking through a model instance version count.
-
-Optimistic locking is disabled by default and can be enabled by setting the `version` property to true in a specific model definition or global model configuration. See [model configuration](../core-concepts/model-basics.md) for more details.
-
-Optimistic locking allows concurrent access to model records for edits and prevents conflicts from overwriting data. It does this by checking whether another process has made changes to a record since it was read and throws an OptimisticLockError when a conflict is detected.
+---
+title: Optimistic Locking
+---
+
+Sequelize has built-in support for optimistic locking through a model instance version count.
+
+Optimistic locking is disabled by default and can be enabled by setting the `version` property to true in a specific model definition or global model configuration. See [model configuration](../core-concepts/model-basics.md) for more details.
+
+Optimistic locking allows concurrent access to model records for edits and prevents conflicts from overwriting data. It does this by checking whether another process has made changes to a record since it was read and throws an OptimisticLockError when a conflict is detected.
diff --git a/versioned_docs/version-6.x.x/other-topics/other-data-types.mdx b/versioned_docs/version-6.x.x/other-topics/other-data-types.mdx
index 4bb0c8f8..6a7ca8e3 100644
--- a/versioned_docs/version-6.x.x/other-topics/other-data-types.mdx
+++ b/versioned_docs/version-6.x.x/other-topics/other-data-types.mdx
@@ -1,232 +1,236 @@
----
-sidebar_position: 2
-title: Other Data Types
----
-
-import { DialectTableFilter } from '@site/src/components/dialect-table-filter.tsx';
-
-Apart from the most common data types mentioned in the Model Basics guide, Sequelize provides several other data types.
-
-## Ranges (PostgreSQL only)
-
-```js
-DataTypes.RANGE(DataTypes.INTEGER) // int4range
-DataTypes.RANGE(DataTypes.BIGINT) // int8range
-DataTypes.RANGE(DataTypes.DATE) // tstzrange
-DataTypes.RANGE(DataTypes.DATEONLY) // daterange
-DataTypes.RANGE(DataTypes.DECIMAL) // numrange
-```
-
-Since range types have extra information for their bound inclusion/exclusion it's not very straightforward to just use a tuple to represent them in javascript.
-
-When supplying ranges as values you can choose from the following APIs:
-
-```js
-// defaults to inclusive lower bound, exclusive upper bound
-const range = [
- new Date(Date.UTC(2016, 0, 1)),
- new Date(Date.UTC(2016, 1, 1))
-];
-// '["2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00")'
-
-// control inclusion
-const range = [
- { value: new Date(Date.UTC(2016, 0, 1)), inclusive: false },
- { value: new Date(Date.UTC(2016, 1, 1)), inclusive: true },
-];
-// '("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00"]'
-
-// composite form
-const range = [
- { value: new Date(Date.UTC(2016, 0, 1)), inclusive: false },
- new Date(Date.UTC(2016, 1, 1)),
-];
-// '("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00")'
-
-const Timeline = sequelize.define('Timeline', {
- range: DataTypes.RANGE(DataTypes.DATE)
-});
-
-await Timeline.create({ range });
-```
-
-However, retrieved range values always come in the form of an array of objects. For example, if the stored value is `("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00"]`, after a finder query you will get:
-
-```js
-[
- { value: Date, inclusive: false },
- { value: Date, inclusive: true }
-]
-```
-
-You will need to call `reload()` after updating an instance with a range type or use the `returning: true` option.
-
-### Special Cases
-
-```js
-// empty range:
-Timeline.create({ range: [] }); // range = 'empty'
-
-// Unbounded range:
-Timeline.create({ range: [null, null] }); // range = '[,)'
-// range = '[,"2016-01-01 00:00:00+00:00")'
-Timeline.create({ range: [null, new Date(Date.UTC(2016, 0, 1))] });
-
-// Infinite range:
-// range = '[-infinity,"2016-01-01 00:00:00+00:00")'
-Timeline.create({ range: [-Infinity, new Date(Date.UTC(2016, 0, 1))] });
-```
-
-## Network Addresses
-
-
-
-| Sequelize DataType | PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
-|--------------------|--------------------------------------------------------------------------|---------|-------|-------|--------|-----------|-----|------|
-| `CIDR` | [`CIDR`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `INET` | [`INET`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `MACADDR` | [`MACADDR`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-
-
-
-## Arrays (PostgreSQL only)
-
-```typescript
-// Defines an array of DataTypes.SOMETHING.
-DataTypes.ARRAY(/* DataTypes.SOMETHING */)
-
-// For example
-// VARCHAR(255)[]
-DataTypes.ARRAY(DataTypes.STRING)
-// VARCHAR(255)[][]
-DataTypes.ARRAY(DataTypes.ARRAY(DataTypes.STRING))
-```
-
-## BLOBs
-
-```js
-DataTypes.BLOB // BLOB (bytea for PostgreSQL)
-DataTypes.BLOB('tiny') // TINYBLOB (bytea for PostgreSQL)
-DataTypes.BLOB('medium') // MEDIUMBLOB (bytea for PostgreSQL)
-DataTypes.BLOB('long') // LONGBLOB (bytea for PostgreSQL)
-```
-
-The blob datatype allows you to insert data both as strings and as buffers. However, when a blob is retrieved from database with Sequelize, it will always be retrieved as a buffer.
-
-## ENUMs
-
-The ENUM is a data type that accepts only a few values, specified as a list.
-
-```js
-DataTypes.ENUM('foo', 'bar') // An ENUM with allowed values 'foo' and 'bar'
-```
-
-ENUMs can also be specified with the `values` field of the column definition, as follows:
-
-```js
-sequelize.define('foo', {
- states: {
- type: DataTypes.ENUM,
- values: ['active', 'pending', 'deleted']
- }
-});
-```
-
-## JSON (SQLite, MySQL, MariaDB, Oracle and PostgreSQL only)
-
-The `DataTypes.JSON` data type is only supported for SQLite, MySQL, MariaDB, Oracle and PostgreSQL. However, there is a minimum support for MSSQL (see below).
-
-### Note for PostgreSQL
-
-The JSON data type in PostgreSQL stores the value as plain text, as opposed to binary representation. If you simply want to store and retrieve a JSON representation, using JSON will take less disk space and less time to build from its input representation. However, if you want to do any operations on the JSON value, you should prefer the JSONB data type described below.
-
-### JSONB (PostgreSQL only)
-
-PostgreSQL also supports a JSONB data type: `DataTypes.JSONB`. It can be queried in three different ways:
-
-```js
-// Nested object
-await Foo.findOne({
- where: {
- meta: {
- video: {
- url: {
- [Op.ne]: null
- }
- }
- }
- }
-});
-
-// Nested key
-await Foo.findOne({
- where: {
- "meta.audio.length": {
- [Op.gt]: 20
- }
- }
-});
-
-// Containment
-await Foo.findOne({
- where: {
- meta: {
- [Op.contains]: {
- site: {
- url: 'https://google.com'
- }
- }
- }
- }
-});
-```
-
-### MSSQL
-
-MSSQL does not have a JSON data type, however it does provide some support for JSON stored as strings through certain functions since SQL Server 2016. Using these functions, you will be able to query the JSON stored in the string, but any returned values will need to be parsed separately.
-
-```js
-// ISJSON - to test if a string contains valid JSON
-await User.findAll({
- where: sequelize.where(sequelize.fn('ISJSON', sequelize.col('userDetails')), 1)
-})
-
-// JSON_VALUE - extract a scalar value from a JSON string
-await User.findAll({
- attributes: [[ sequelize.fn('JSON_VALUE', sequelize.col('userDetails'), '$.address.Line1'), 'address line 1']]
-})
-
-// JSON_VALUE - query a scalar value from a JSON string
-await User.findAll({
- where: sequelize.where(sequelize.fn('JSON_VALUE', sequelize.col('userDetails'), '$.address.Line1'), '14, Foo Street')
-})
-
-// JSON_QUERY - extract an object or array
-await User.findAll({
- attributes: [[ sequelize.fn('JSON_QUERY', sequelize.col('userDetails'), '$.address'), 'full address']]
-})
-```
-
-## Miscellaneous DataTypes
-
-
-
-| Sequelize DataType | PostgreSQL | [MariaDB](https://mariadb.com/kb/en/geometry-types/) | [MySQL](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html) | MSSQL | SQLite | Snowflake | db2 | ibmi |
-|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------|-------|--------|-----------|-----|------|
-| [`GEOMETRY`](https://sequelize.org/api/v6/class/src/data-types.js~geometry) | [`GEOMETRY`](https://postgis.net/workshops/postgis-intro/geometries.html) | `GEOMETRY` | `GEOMETRY` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `GEOMETRY('POINT')` | `GEOMETRY(POINT)` | `POINT` | `POINT` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `GEOMETRY('POINT', 4326)` | `GEOMETRY(POINT,4326)` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `GEOMETRY('POLYGON')` | `GEOMETRY(POLYGON)` | `POLYGON` | `POLYGON` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `GEOMETRY('LINESTRING')` | `GEOMETRY(LINESTRING)` | `LINESTRING` | `LINESTRING` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| [`GEOGRAPHY`](https://sequelize.org/api/v6/class/src/data-types.js~geography) | [`GEOGRAPHY`](https://postgis.net/workshops/postgis-intro/geography.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `HSTORE` | [`HSTORE`](https://www.postgresql.org/docs/9.1/hstore.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-
-
-
-:::note
-
-In Postgres, the GEOMETRY and GEOGRAPHY types are implemented by [the PostGIS extension](https://postgis.net/workshops/postgis-intro/geometries.html).
-
-In Postgres, You must install the [pg-hstore](https://www.npmjs.com/package/pg-hstore) package if you use `DataTypes.HSTORE`
-
-:::
+---
+sidebar_position: 2
+title: Other Data Types
+---
+
+import { DialectTableFilter } from '@site/src/components/dialect-table-filter.tsx';
+
+Apart from the most common data types mentioned in the Model Basics guide, Sequelize provides several other data types.
+
+## Ranges (PostgreSQL only)
+
+```js
+DataTypes.RANGE(DataTypes.INTEGER); // int4range
+DataTypes.RANGE(DataTypes.BIGINT); // int8range
+DataTypes.RANGE(DataTypes.DATE); // tstzrange
+DataTypes.RANGE(DataTypes.DATEONLY); // daterange
+DataTypes.RANGE(DataTypes.DECIMAL); // numrange
+```
+
+Since range types have extra information for their bound inclusion/exclusion it's not very straightforward to just use a tuple to represent them in javascript.
+
+When supplying ranges as values you can choose from the following APIs:
+
+```js
+// defaults to inclusive lower bound, exclusive upper bound
+const range = [new Date(Date.UTC(2016, 0, 1)), new Date(Date.UTC(2016, 1, 1))];
+// '["2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00")'
+
+// control inclusion
+const range = [
+ { value: new Date(Date.UTC(2016, 0, 1)), inclusive: false },
+ { value: new Date(Date.UTC(2016, 1, 1)), inclusive: true },
+];
+// '("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00"]'
+
+// composite form
+const range = [
+ { value: new Date(Date.UTC(2016, 0, 1)), inclusive: false },
+ new Date(Date.UTC(2016, 1, 1)),
+];
+// '("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00")'
+
+const Timeline = sequelize.define('Timeline', {
+ range: DataTypes.RANGE(DataTypes.DATE),
+});
+
+await Timeline.create({ range });
+```
+
+However, retrieved range values always come in the form of an array of objects. For example, if the stored value is `("2016-01-01 00:00:00+00:00", "2016-02-01 00:00:00+00:00"]`, after a finder query you will get:
+
+```js
+[
+ { value: Date, inclusive: false },
+ { value: Date, inclusive: true },
+];
+```
+
+You will need to call `reload()` after updating an instance with a range type or use the `returning: true` option.
+
+### Special Cases
+
+```js
+// empty range:
+Timeline.create({ range: [] }); // range = 'empty'
+
+// Unbounded range:
+Timeline.create({ range: [null, null] }); // range = '[,)'
+// range = '[,"2016-01-01 00:00:00+00:00")'
+Timeline.create({ range: [null, new Date(Date.UTC(2016, 0, 1))] });
+
+// Infinite range:
+// range = '[-infinity,"2016-01-01 00:00:00+00:00")'
+Timeline.create({ range: [-Infinity, new Date(Date.UTC(2016, 0, 1))] });
+```
+
+## Network Addresses
+
+
+
+| Sequelize DataType | PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
+| ------------------ | ------------------------------------------------------------------------ | ------- | ----- | ----- | ------ | --------- | --- | ---- |
+| `CIDR` | [`CIDR`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `INET` | [`INET`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `MACADDR` | [`MACADDR`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+
+
+
+## Arrays (PostgreSQL only)
+
+```typescript
+// Defines an array of DataTypes.SOMETHING.
+DataTypes.ARRAY(/* DataTypes.SOMETHING */);
+
+// For example
+// VARCHAR(255)[]
+DataTypes.ARRAY(DataTypes.STRING);
+// VARCHAR(255)[][]
+DataTypes.ARRAY(DataTypes.ARRAY(DataTypes.STRING));
+```
+
+## BLOBs
+
+```js
+DataTypes.BLOB; // BLOB (bytea for PostgreSQL)
+DataTypes.BLOB('tiny'); // TINYBLOB (bytea for PostgreSQL)
+DataTypes.BLOB('medium'); // MEDIUMBLOB (bytea for PostgreSQL)
+DataTypes.BLOB('long'); // LONGBLOB (bytea for PostgreSQL)
+```
+
+The blob datatype allows you to insert data both as strings and as buffers. However, when a blob is retrieved from database with Sequelize, it will always be retrieved as a buffer.
+
+## ENUMs
+
+The ENUM is a data type that accepts only a few values, specified as a list.
+
+```js
+DataTypes.ENUM('foo', 'bar'); // An ENUM with allowed values 'foo' and 'bar'
+```
+
+ENUMs can also be specified with the `values` field of the column definition, as follows:
+
+```js
+sequelize.define('foo', {
+ states: {
+ type: DataTypes.ENUM,
+ values: ['active', 'pending', 'deleted'],
+ },
+});
+```
+
+## JSON (SQLite, MySQL, MariaDB, Oracle and PostgreSQL only)
+
+The `DataTypes.JSON` data type is only supported for SQLite, MySQL, MariaDB, Oracle and PostgreSQL. However, there is a minimum support for MSSQL (see below).
+
+### Note for PostgreSQL
+
+The JSON data type in PostgreSQL stores the value as plain text, as opposed to binary representation. If you simply want to store and retrieve a JSON representation, using JSON will take less disk space and less time to build from its input representation. However, if you want to do any operations on the JSON value, you should prefer the JSONB data type described below.
+
+### JSONB (PostgreSQL only)
+
+PostgreSQL also supports a JSONB data type: `DataTypes.JSONB`. It can be queried in three different ways:
+
+```js
+// Nested object
+await Foo.findOne({
+ where: {
+ meta: {
+ video: {
+ url: {
+ [Op.ne]: null,
+ },
+ },
+ },
+ },
+});
+
+// Nested key
+await Foo.findOne({
+ where: {
+ 'meta.audio.length': {
+ [Op.gt]: 20,
+ },
+ },
+});
+
+// Containment
+await Foo.findOne({
+ where: {
+ meta: {
+ [Op.contains]: {
+ site: {
+ url: 'https://google.com',
+ },
+ },
+ },
+ },
+});
+```
+
+### MSSQL
+
+MSSQL does not have a JSON data type, however it does provide some support for JSON stored as strings through certain functions since SQL Server 2016. Using these functions, you will be able to query the JSON stored in the string, but any returned values will need to be parsed separately.
+
+```js
+// ISJSON - to test if a string contains valid JSON
+await User.findAll({
+ where: sequelize.where(sequelize.fn('ISJSON', sequelize.col('userDetails')), 1),
+});
+
+// JSON_VALUE - extract a scalar value from a JSON string
+await User.findAll({
+ attributes: [
+ [sequelize.fn('JSON_VALUE', sequelize.col('userDetails'), '$.address.Line1'), 'address line 1'],
+ ],
+});
+
+// JSON_VALUE - query a scalar value from a JSON string
+await User.findAll({
+ where: sequelize.where(
+ sequelize.fn('JSON_VALUE', sequelize.col('userDetails'), '$.address.Line1'),
+ '14, Foo Street',
+ ),
+});
+
+// JSON_QUERY - extract an object or array
+await User.findAll({
+ attributes: [
+ [sequelize.fn('JSON_QUERY', sequelize.col('userDetails'), '$.address'), 'full address'],
+ ],
+});
+```
+
+## Miscellaneous DataTypes
+
+
+
+| Sequelize DataType | PostgreSQL | [MariaDB](https://mariadb.com/kb/en/geometry-types/) | [MySQL](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html) | MSSQL | SQLite | Snowflake | db2 | ibmi |
+| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------- | ----- | ------ | --------- | --- | ---- |
+| [`GEOMETRY`](https://sequelize.org/api/v6/class/src/data-types.js~geometry) | [`GEOMETRY`](https://postgis.net/workshops/postgis-intro/geometries.html) | `GEOMETRY` | `GEOMETRY` | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `GEOMETRY('POINT')` | `GEOMETRY(POINT)` | `POINT` | `POINT` | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `GEOMETRY('POINT', 4326)` | `GEOMETRY(POINT,4326)` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `GEOMETRY('POLYGON')` | `GEOMETRY(POLYGON)` | `POLYGON` | `POLYGON` | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `GEOMETRY('LINESTRING')` | `GEOMETRY(LINESTRING)` | `LINESTRING` | `LINESTRING` | ❌ | ❌ | ❌ | ❌ | ❌ |
+| [`GEOGRAPHY`](https://sequelize.org/api/v6/class/src/data-types.js~geography) | [`GEOGRAPHY`](https://postgis.net/workshops/postgis-intro/geography.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `HSTORE` | [`HSTORE`](https://www.postgresql.org/docs/9.1/hstore.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+
+
+
+:::note
+
+In Postgres, the GEOMETRY and GEOGRAPHY types are implemented by [the PostGIS extension](https://postgis.net/workshops/postgis-intro/geometries.html).
+
+In Postgres, You must install the [pg-hstore](https://www.npmjs.com/package/pg-hstore) package if you use `DataTypes.HSTORE`
+
+:::
diff --git a/versioned_docs/version-6.x.x/other-topics/query-interface.md b/versioned_docs/version-6.x.x/other-topics/query-interface.md
index 8a910f4e..28ee94a7 100644
--- a/versioned_docs/version-6.x.x/other-topics/query-interface.md
+++ b/versioned_docs/version-6.x.x/other-topics/query-interface.md
@@ -26,8 +26,8 @@ queryInterface.createTable('Person', {
isBetaMember: {
type: DataTypes.BOOLEAN,
defaultValue: false,
- allowNull: false
- }
+ allowNull: false,
+ },
});
```
@@ -60,7 +60,7 @@ ALTER TABLE `Person` ADD `petName` VARCHAR(255);
queryInterface.changeColumn('Person', 'foo', {
type: DataTypes.FLOAT,
defaultValue: 3.14,
- allowNull: false
+ allowNull: false,
});
```
@@ -73,7 +73,9 @@ ALTER TABLE `Person` CHANGE `foo` `foo` FLOAT NOT NULL DEFAULT 3.14;
## Removing a column
```js
-queryInterface.removeColumn('Person', 'petName', { /* query options */ });
+queryInterface.removeColumn('Person', 'petName', {
+ /* query options */
+});
```
Generated SQL (using PostgreSQL):
@@ -95,17 +97,17 @@ queryInterface.createTable('Person', {
isBetaMember: {
type: DataTypes.BOOLEAN,
defaultValue: false,
- allowNull: false
+ allowNull: false,
},
petName: DataTypes.STRING,
- foo: DataTypes.INTEGER
+ foo: DataTypes.INTEGER,
});
// And we change a column:
queryInterface.changeColumn('Person', 'foo', {
type: DataTypes.FLOAT,
defaultValue: 3.14,
- allowNull: false
+ allowNull: false,
});
```
diff --git a/versioned_docs/version-6.x.x/other-topics/read-replication.md b/versioned_docs/version-6.x.x/other-topics/read-replication.md
index 303f1c59..85c7cc00 100644
--- a/versioned_docs/version-6.x.x/other-topics/read-replication.md
+++ b/versioned_docs/version-6.x.x/other-topics/read-replication.md
@@ -10,16 +10,29 @@ const sequelize = new Sequelize('database', null, null, {
port: 3306,
replication: {
read: [
- { host: '8.8.8.8', username: 'read-1-username', password: process.env.READ_DB_1_PW },
- { host: '9.9.9.9', username: 'read-2-username', password: process.env.READ_DB_2_PW }
+ {
+ host: '8.8.8.8',
+ username: 'read-1-username',
+ password: process.env.READ_DB_1_PW,
+ },
+ {
+ host: '9.9.9.9',
+ username: 'read-2-username',
+ password: process.env.READ_DB_2_PW,
+ },
],
- write: { host: '1.1.1.1', username: 'write-username', password: process.env.WRITE_DB_PW }
+ write: {
+ host: '1.1.1.1',
+ username: 'write-username',
+ password: process.env.WRITE_DB_PW,
+ },
},
- pool: { // If you want to override the options used for the read/write pool you can do so here
+ pool: {
+ // If you want to override the options used for the read/write pool you can do so here
max: 20,
- idle: 30000
+ idle: 30000,
},
-})
+});
```
If you have any general settings that apply to all replicas you do not need to provide them for each instance. In the code above, database name and port is propagated to all replicas. The same will happen for user and password, if you leave them out for any of the replicas. Each replica has the following options:`host`,`port`,`username`,`password`,`database`.
diff --git a/versioned_docs/version-6.x.x/other-topics/resources.md b/versioned_docs/version-6.x.x/other-topics/resources.md
index 4a350f1f..a83942aa 100644
--- a/versioned_docs/version-6.x.x/other-topics/resources.md
+++ b/versioned_docs/version-6.x.x/other-topics/resources.md
@@ -6,80 +6,80 @@ title: Resources
### ACL
-* [ssacl](https://github.com/pumpupapp/ssacl)
-* [ssacl-attribute-roles](https://github.com/mickhansen/ssacl-attribute-roles)
-* [SequelizeGuard](https://github.com/lotivo/sequelize-acl) - Role, Permission based Authorization for Sequelize.
+- [ssacl](https://github.com/pumpupapp/ssacl)
+- [ssacl-attribute-roles](https://github.com/mickhansen/ssacl-attribute-roles)
+- [SequelizeGuard](https://github.com/lotivo/sequelize-acl) - Role, Permission based Authorization for Sequelize.
### Auto Code Generation & Scaffolding
-* [meteor modeler](https://www.datensen.com/) - Desktop tool for visual definition of Sequelize models and associations.
-* [sequelize-ui](https://github.com/tomjschuster/sequelize-ui) - Online tool for building models, relations and more.
-* [sequelizer](https://github.com/andyforever/sequelizer) - A GUI Desktop App for generating Sequelize models. Support for Mysql, Mariadb, Postgres, Sqlite, Mssql.
-* [sequelize-auto](https://github.com/sequelize/sequelize-auto) Generating models for SequelizeJS via the command line is another choice.
-* [pg-generator](https://www.pg-generator.com/builtin-templates/sequelize/) - Auto generate/scaffold Sequelize models for PostgreSQL database.
-* [sequelizejs-decorators](https://www.npmjs.com/package/sequelizejs-decorators) decorators for composing sequelize models
+- [meteor modeler](https://www.datensen.com/) - Desktop tool for visual definition of Sequelize models and associations.
+- [sequelize-ui](https://github.com/tomjschuster/sequelize-ui) - Online tool for building models, relations and more.
+- [sequelizer](https://github.com/andyforever/sequelizer) - A GUI Desktop App for generating Sequelize models. Support for Mysql, Mariadb, Postgres, Sqlite, Mssql.
+- [sequelize-auto](https://github.com/sequelize/sequelize-auto) Generating models for SequelizeJS via the command line is another choice.
+- [pg-generator](https://www.pg-generator.com/builtin-templates/sequelize/) - Auto generate/scaffold Sequelize models for PostgreSQL database.
+- [sequelizejs-decorators](https://www.npmjs.com/package/sequelizejs-decorators) decorators for composing sequelize models
### Autoloader
-* [sequelize-autoload](https://github.com/boxsnake-nodejs/sequelize-autoload) - An autoloader for Sequelize, inspired by [PSR-0](https://www.php-fig.org/psr/psr-0/) and [PSR-4](https://www.php-fig.org/psr/psr-4/).
+- [sequelize-autoload](https://github.com/boxsnake-nodejs/sequelize-autoload) - An autoloader for Sequelize, inspired by [PSR-0](https://www.php-fig.org/psr/psr-0/) and [PSR-4](https://www.php-fig.org/psr/psr-4/).
### Bcrypt
-* [sequelize-bcrypt](https://github.com/mattiamalonni/sequelize-bcrypt) - Utility to integrate bcrypt into sequelize models
+- [sequelize-bcrypt](https://github.com/mattiamalonni/sequelize-bcrypt) - Utility to integrate bcrypt into sequelize models
### Browser
-* [sequelize-browser](https://npmjs.com/package/sequelize-browser) - A web-browser-compatible build of Sequelize
+- [sequelize-browser](https://npmjs.com/package/sequelize-browser) - A web-browser-compatible build of Sequelize
### Caching
-* [sequelize-transparent-cache](https://github.com/DanielHreben/sequelize-transparent-cache)
+- [sequelize-transparent-cache](https://github.com/DanielHreben/sequelize-transparent-cache)
### Filters
-* [sequelize-transforms](https://www.npmjs.com/package/sequelize-transforms) - Add configurable attribute transforms.
+- [sequelize-transforms](https://www.npmjs.com/package/sequelize-transforms) - Add configurable attribute transforms.
### Fixtures / mock data
-* [Fixer](https://github.com/olalonde/fixer)
-* [Sequelize-fixtures](https://github.com/domasx2/sequelize-fixtures)
-* [Sequelize-fixture](https://github.com/xudejian/sequelize-fixture)
+- [Fixer](https://github.com/olalonde/fixer)
+- [Sequelize-fixtures](https://github.com/domasx2/sequelize-fixtures)
+- [Sequelize-fixture](https://github.com/xudejian/sequelize-fixture)
### Hierarchies
-* [sequelize-hierarchy](https://www.npmjs.com/package/sequelize-hierarchy) - Nested hierarchies for Sequelize.
+- [sequelize-hierarchy](https://www.npmjs.com/package/sequelize-hierarchy) - Nested hierarchies for Sequelize.
### Historical records / Time travel
-* [sequelize-temporal](https://github.com/bonaval/sequelize-temporal) - Temporal tables (aka historical records)
+- [sequelize-temporal](https://github.com/bonaval/sequelize-temporal) - Temporal tables (aka historical records)
### Integrations
-* [kysely-sequelize](https://www.npmjs.com/pacakge/kysely-sequelize) - A toolkit (dialect, type translators, etc.) that allows using your existing Sequelize instance with [Kysely](https://www.kysely.dev).
+- [kysely-sequelize](https://www.npmjs.com/pacakge/kysely-sequelize) - A toolkit (dialect, type translators, etc.) that allows using your existing Sequelize instance with [Kysely](https://www.kysely.dev).
### Joi
-* [sequelize-joi](https://github.com/mattiamalonni/sequelize-joi) - Allows specifying [Joi](https://github.com/sideway/joi) validation schema for model attributes in Sequelize.
+- [sequelize-joi](https://github.com/mattiamalonni/sequelize-joi) - Allows specifying [Joi](https://github.com/sideway/joi) validation schema for model attributes in Sequelize.
### Migrations
-* [umzug](https://github.com/sequelize/umzug)
-* [sequelizemm](https://github.com/hasinoorit/sequelizemm) - CLI tool to generate a migration script from models
+- [umzug](https://github.com/sequelize/umzug)
+- [sequelizemm](https://github.com/hasinoorit/sequelizemm) - CLI tool to generate a migration script from models
### Slugification
-* [sequelize-slugify](https://www.npmjs.com/package/sequelize-slugify) - Add slugs to sequelize models
+- [sequelize-slugify](https://www.npmjs.com/package/sequelize-slugify) - Add slugs to sequelize models
### Tokens
-* [sequelize-tokenify](https://github.com/pipll/sequelize-tokenify) - Add unique tokens to sequelize models
+- [sequelize-tokenify](https://github.com/pipll/sequelize-tokenify) - Add unique tokens to sequelize models
### Miscellaneous
-* [sequelize-deep-update](https://www.npmjs.com/package/sequelize-deep-update) - Update a sequelize instance and its included associated instances with new properties.
-* [sequelize-noupdate-attributes](https://www.npmjs.com/package/sequelize-noupdate-attributes) - Adds no update/readonly attributes support to models.
-* [sqlcommenter-sequelize](https://github.com/google/sqlcommenter/tree/master/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-sequelize) A [sqlcommenter](https://google.github.io/sqlcommenter/) plugin with [support for Sequelize](https://google.github.io/sqlcommenter/node/sequelize/) to augment SQL statements with comments that can be used later to correlate application code with SQL statements.
-* [automated-express-backend](https://github.com/ruyd/automated-express-backend) - Sample Sequelize Backend with runtime generation of API
-* [@rematter/paranoid-sql](https://www.npmjs.com/package/@rematter/paranoid-sql) - Add conditions to verify rows are not soft deleted.
-* [@rematter/sequelize-paranoid-delete](https://www.npmjs.com/package/@rematter/sequelize-paranoid-delete) - Enables onDelete when using paranoid mode.
-* [@hatchifyjs/sequelize-create-with-associations](https://github.com/bitovi/sequelize-create-with-associations) - Automatically creates, bulkCreates and updates records that have relationships to each other without extra code.
+- [sequelize-deep-update](https://www.npmjs.com/package/sequelize-deep-update) - Update a sequelize instance and its included associated instances with new properties.
+- [sequelize-noupdate-attributes](https://www.npmjs.com/package/sequelize-noupdate-attributes) - Adds no update/readonly attributes support to models.
+- [sqlcommenter-sequelize](https://github.com/google/sqlcommenter/tree/master/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-sequelize) A [sqlcommenter](https://google.github.io/sqlcommenter/) plugin with [support for Sequelize](https://google.github.io/sqlcommenter/node/sequelize/) to augment SQL statements with comments that can be used later to correlate application code with SQL statements.
+- [automated-express-backend](https://github.com/ruyd/automated-express-backend) - Sample Sequelize Backend with runtime generation of API
+- [@rematter/paranoid-sql](https://www.npmjs.com/package/@rematter/paranoid-sql) - Add conditions to verify rows are not soft deleted.
+- [@rematter/sequelize-paranoid-delete](https://www.npmjs.com/package/@rematter/sequelize-paranoid-delete) - Enables onDelete when using paranoid mode.
+- [@hatchifyjs/sequelize-create-with-associations](https://github.com/bitovi/sequelize-create-with-associations) - Automatically creates, bulkCreates and updates records that have relationships to each other without extra code.
diff --git a/versioned_docs/version-6.x.x/other-topics/scopes.md b/versioned_docs/version-6.x.x/other-topics/scopes.md
index 0e0453c3..89b40349 100644
--- a/versioned_docs/version-6.x.x/other-topics/scopes.md
+++ b/versioned_docs/version-6.x.x/other-topics/scopes.md
@@ -12,45 +12,46 @@ Scopes are defined in the model definition and can be finder objects, or functio
```js
class Project extends Model {}
-Project.init({
- // Attributes
-}, {
- defaultScope: {
- where: {
- active: true
- }
+Project.init(
+ {
+ // Attributes
},
- scopes: {
- deleted: {
+ {
+ defaultScope: {
where: {
- deleted: true
- }
- },
- activeUsers: {
- include: [
- { model: User, where: { active: true } }
- ]
- },
- random() {
- return {
- where: {
- someNumber: Math.random()
- }
- }
+ active: true,
+ },
},
- accessLevel(value) {
- return {
+ scopes: {
+ deleted: {
where: {
- accessLevel: {
- [Op.gte]: value
- }
- }
- }
+ deleted: true,
+ },
+ },
+ activeUsers: {
+ include: [{ model: User, where: { active: true } }],
+ },
+ random() {
+ return {
+ where: {
+ someNumber: Math.random(),
+ },
+ };
+ },
+ accessLevel(value) {
+ return {
+ where: {
+ accessLevel: {
+ [Op.gte]: value,
+ },
+ },
+ };
+ },
+ sequelize,
+ modelName: 'project',
},
- sequelize,
- modelName: 'project'
- }
-});
+ },
+);
```
You can also add scopes after a model has been defined by calling [`YourModel.addScope`](pathname:///api/v6/class/src/model.js~Model.html#static-method-addScope). This is especially useful for scopes with includes, where the model in the include might not be defined at the time the other model is being defined.
@@ -76,9 +77,7 @@ It is also possible to include scoped models in a scope definition. This allows
```js
// The `activeUsers` scope defined in the example above could also have been defined this way:
Project.addScope('activeUsers', {
- include: [
- { model: User.scope('active') }
- ]
+ include: [{ model: User.scope('active') }],
});
```
@@ -93,8 +92,8 @@ await DeletedProjects.findAll();
// The above is equivalent to:
await Project.findAll({
where: {
- deleted: true
- }
+ deleted: true,
+ },
});
```
@@ -150,18 +149,18 @@ YourModel.addScope('scope1', {
where: {
firstName: 'bob',
age: {
- [Op.gt]: 20
- }
+ [Op.gt]: 20,
+ },
},
- limit: 2
+ limit: 2,
});
YourModel.addScope('scope2', {
where: {
age: {
- [Op.lt]: 30
- }
+ [Op.lt]: 30,
+ },
},
- limit: 10
+ limit: 10,
});
```
@@ -176,10 +175,15 @@ Note how `limit` and `age` are overwritten by `scope2`, while `firstName` is pre
For instance, if `YourModel` was initialized as such:
```js
-YourModel.init({ /* attributes */ }, {
- // ... other init options
- whereMergeStrategy: 'and',
-});
+YourModel.init(
+ {
+ /* attributes */
+ },
+ {
+ // ... other init options
+ whereMergeStrategy: 'and',
+ },
+);
```
Using `.scope('scope1', 'scope2')` will yield the following WHERE clause:
@@ -195,9 +199,9 @@ The same merge logic applies when passing a find object directly to `findAll` (a
```js
Project.scope('deleted').findAll({
where: {
- firstName: 'john'
- }
-})
+ firstName: 'john',
+ },
+});
```
Generated where clause:
@@ -230,40 +234,52 @@ Now, consider the following four scopes defined on Foo:
Foo.addScope('includeEverything', {
include: {
model: Bar,
- include: [{
- model: Baz,
- include: Qux
- }]
- }
+ include: [
+ {
+ model: Baz,
+ include: Qux,
+ },
+ ],
+ },
});
Foo.addScope('limitedBars', {
- include: [{
- model: Bar,
- limit: 2
- }]
+ include: [
+ {
+ model: Bar,
+ limit: 2,
+ },
+ ],
});
Foo.addScope('limitedBazs', {
- include: [{
- model: Bar,
- include: [{
- model: Baz,
- limit: 2
- }]
- }]
+ include: [
+ {
+ model: Bar,
+ include: [
+ {
+ model: Baz,
+ limit: 2,
+ },
+ ],
+ },
+ ],
});
Foo.addScope('excludeBazName', {
- include: [{
- model: Bar,
- include: [{
- model: Baz,
- attributes: {
- exclude: ['name']
- }
- }]
- }]
+ include: [
+ {
+ model: Bar,
+ include: [
+ {
+ model: Baz,
+ attributes: {
+ exclude: ['name'],
+ },
+ },
+ ],
+ },
+ ],
});
```
@@ -274,24 +290,21 @@ await Foo.findAll({
include: {
model: Bar,
limit: 2,
- include: [{
- model: Baz,
- limit: 2,
- attributes: {
- exclude: ['name']
+ include: [
+ {
+ model: Baz,
+ limit: 2,
+ attributes: {
+ exclude: ['name'],
+ },
+ include: Qux,
},
- include: Qux
- }]
- }
+ ],
+ },
});
// The above is equivalent to:
-await Foo.scope([
- 'includeEverything',
- 'limitedBars',
- 'limitedBazs',
- 'excludeBazName'
-]).findAll();
+await Foo.scope(['includeEverything', 'limitedBars', 'limitedBazs', 'excludeBazName']).findAll();
```
Observe how the four scopes were merged into one. The includes of scopes are merged based on the model being included. If one scope includes model A and another includes model B, the merged result will include both models A and B. On the other hand, if both scopes include the same model A, but with different options (such as nested includes or other attributes), those will be merged recursively, as shown above.
diff --git a/versioned_docs/version-6.x.x/other-topics/sub-queries.md b/versioned_docs/version-6.x.x/other-topics/sub-queries.md
index dd4b9eae..d1d1e22c 100644
--- a/versioned_docs/version-6.x.x/other-topics/sub-queries.md
+++ b/versioned_docs/version-6.x.x/other-topics/sub-queries.md
@@ -1,166 +1,175 @@
----
-title: Sub Queries
----
-
-Consider you have two models, `Post` and `Reaction`, with a One-to-Many relationship set up, so that one post has many reactions:
-
-```js
-const Post = sequelize.define('post', {
- content: DataTypes.STRING
-}, { timestamps: false });
-
-const Reaction = sequelize.define('reaction', {
- type: DataTypes.STRING
-}, { timestamps: false });
-
-Post.hasMany(Reaction);
-Reaction.belongsTo(Post);
-```
-
-*Note: we have disabled timestamps just to have shorter queries for the next examples.*
-
-Let's fill our tables with some data:
-
-```js
-async function makePostWithReactions(content, reactionTypes) {
- const post = await Post.create({ content });
- await Reaction.bulkCreate(
- reactionTypes.map(type => ({ type, postId: post.id }))
- );
- return post;
-}
-
-await makePostWithReactions('Hello World', [
- 'Like', 'Angry', 'Laugh', 'Like', 'Like', 'Angry', 'Sad', 'Like'
-]);
-await makePostWithReactions('My Second Post', [
- 'Laugh', 'Laugh', 'Like', 'Laugh'
-]);
-```
-
-Now, we are ready for examples of the power of subqueries.
-
-Let's say we wanted to compute via SQL a `laughReactionsCount` for each post. We can achieve that with a sub-query, such as the following:
-
-```sql
-SELECT
- *,
- (
- SELECT COUNT(*)
- FROM reactions AS reaction
- WHERE
- reaction.postId = post.id
- AND
- reaction.type = "Laugh"
- ) AS laughReactionsCount
-FROM posts AS post
-```
-
-If we run the above raw SQL query through Sequelize, we get:
-
-```json
-[
- {
- "id": 1,
- "content": "Hello World",
- "laughReactionsCount": 1
- },
- {
- "id": 2,
- "content": "My Second Post",
- "laughReactionsCount": 3
- }
-]
-```
-
-So how can we achieve that with more help from Sequelize, without having to write the whole raw query by hand?
-
-The answer: by combining the `attributes` option of the finder methods (such as `findAll`) with the `sequelize.literal` utility function, that allows you to directly insert arbitrary content into the query without any automatic escaping.
-
-This means that Sequelize will help you with the main, larger query, but you will still have to write that sub-query by yourself:
-
-```js
-Post.findAll({
- attributes: {
- include: [
- [
- // Note the wrapping parentheses in the call below!
- sequelize.literal(`(
- SELECT COUNT(*)
- FROM reactions AS reaction
- WHERE
- reaction.postId = post.id
- AND
- reaction.type = "Laugh"
- )`),
- 'laughReactionsCount'
- ]
- ]
- }
-});
-```
-
-*Important Note: Since `sequelize.literal` inserts arbitrary content without escaping to the query, it deserves very special attention since it may be a source of (major) security vulnerabilities. It should not be used on user-generated content.* However, here, we are using `sequelize.literal` with a fixed string, carefully written by us (the coders). This is ok, since we know what we are doing.
-
-The above gives the following output:
-
-```json
-[
- {
- "id": 1,
- "content": "Hello World",
- "laughReactionsCount": 1
- },
- {
- "id": 2,
- "content": "My Second Post",
- "laughReactionsCount": 3
- }
-]
-```
-
-Success!
-
-## Using sub-queries for complex ordering
-
-This idea can be used to enable complex ordering, such as ordering posts by the number of laugh reactions they have:
-
-```js
-Post.findAll({
- attributes: {
- include: [
- [
- sequelize.literal(`(
- SELECT COUNT(*)
- FROM reactions AS reaction
- WHERE
- reaction.postId = post.id
- AND
- reaction.type = "Laugh"
- )`),
- 'laughReactionsCount'
- ]
- ]
- },
- order: [
- [sequelize.literal('laughReactionsCount'), 'DESC']
- ]
-});
-```
-
-Result:
-
-```json
-[
- {
- "id": 2,
- "content": "My Second Post",
- "laughReactionsCount": 3
- },
- {
- "id": 1,
- "content": "Hello World",
- "laughReactionsCount": 1
- }
-]
-```
\ No newline at end of file
+---
+title: Sub Queries
+---
+
+Consider you have two models, `Post` and `Reaction`, with a One-to-Many relationship set up, so that one post has many reactions:
+
+```js
+const Post = sequelize.define(
+ 'post',
+ {
+ content: DataTypes.STRING,
+ },
+ { timestamps: false },
+);
+
+const Reaction = sequelize.define(
+ 'reaction',
+ {
+ type: DataTypes.STRING,
+ },
+ { timestamps: false },
+);
+
+Post.hasMany(Reaction);
+Reaction.belongsTo(Post);
+```
+
+_Note: we have disabled timestamps just to have shorter queries for the next examples._
+
+Let's fill our tables with some data:
+
+```js
+async function makePostWithReactions(content, reactionTypes) {
+ const post = await Post.create({ content });
+ await Reaction.bulkCreate(reactionTypes.map(type => ({ type, postId: post.id })));
+ return post;
+}
+
+await makePostWithReactions('Hello World', [
+ 'Like',
+ 'Angry',
+ 'Laugh',
+ 'Like',
+ 'Like',
+ 'Angry',
+ 'Sad',
+ 'Like',
+]);
+await makePostWithReactions('My Second Post', ['Laugh', 'Laugh', 'Like', 'Laugh']);
+```
+
+Now, we are ready for examples of the power of subqueries.
+
+Let's say we wanted to compute via SQL a `laughReactionsCount` for each post. We can achieve that with a sub-query, such as the following:
+
+```sql
+SELECT
+ *,
+ (
+ SELECT COUNT(*)
+ FROM reactions AS reaction
+ WHERE
+ reaction.postId = post.id
+ AND
+ reaction.type = "Laugh"
+ ) AS laughReactionsCount
+FROM posts AS post
+```
+
+If we run the above raw SQL query through Sequelize, we get:
+
+```json
+[
+ {
+ "id": 1,
+ "content": "Hello World",
+ "laughReactionsCount": 1
+ },
+ {
+ "id": 2,
+ "content": "My Second Post",
+ "laughReactionsCount": 3
+ }
+]
+```
+
+So how can we achieve that with more help from Sequelize, without having to write the whole raw query by hand?
+
+The answer: by combining the `attributes` option of the finder methods (such as `findAll`) with the `sequelize.literal` utility function, that allows you to directly insert arbitrary content into the query without any automatic escaping.
+
+This means that Sequelize will help you with the main, larger query, but you will still have to write that sub-query by yourself:
+
+```js
+Post.findAll({
+ attributes: {
+ include: [
+ [
+ // Note the wrapping parentheses in the call below!
+ sequelize.literal(`(
+ SELECT COUNT(*)
+ FROM reactions AS reaction
+ WHERE
+ reaction.postId = post.id
+ AND
+ reaction.type = "Laugh"
+ )`),
+ 'laughReactionsCount',
+ ],
+ ],
+ },
+});
+```
+
+_Important Note: Since `sequelize.literal` inserts arbitrary content without escaping to the query, it deserves very special attention since it may be a source of (major) security vulnerabilities. It should not be used on user-generated content._ However, here, we are using `sequelize.literal` with a fixed string, carefully written by us (the coders). This is ok, since we know what we are doing.
+
+The above gives the following output:
+
+```json
+[
+ {
+ "id": 1,
+ "content": "Hello World",
+ "laughReactionsCount": 1
+ },
+ {
+ "id": 2,
+ "content": "My Second Post",
+ "laughReactionsCount": 3
+ }
+]
+```
+
+Success!
+
+## Using sub-queries for complex ordering
+
+This idea can be used to enable complex ordering, such as ordering posts by the number of laugh reactions they have:
+
+```js
+Post.findAll({
+ attributes: {
+ include: [
+ [
+ sequelize.literal(`(
+ SELECT COUNT(*)
+ FROM reactions AS reaction
+ WHERE
+ reaction.postId = post.id
+ AND
+ reaction.type = "Laugh"
+ )`),
+ 'laughReactionsCount',
+ ],
+ ],
+ },
+ order: [[sequelize.literal('laughReactionsCount'), 'DESC']],
+});
+```
+
+Result:
+
+```json
+[
+ {
+ "id": 2,
+ "content": "My Second Post",
+ "laughReactionsCount": 3
+ },
+ {
+ "id": 1,
+ "content": "Hello World",
+ "laughReactionsCount": 1
+ }
+]
+```
diff --git a/versioned_docs/version-6.x.x/other-topics/transactions.md b/versioned_docs/version-6.x.x/other-topics/transactions.md
index 8a847235..ecc2bbdf 100644
--- a/versioned_docs/version-6.x.x/other-topics/transactions.md
+++ b/versioned_docs/version-6.x.x/other-topics/transactions.md
@@ -19,33 +19,35 @@ Let's start with an example:
const t = await sequelize.transaction();
try {
-
// Then, we do some calls passing this transaction as an option:
- const user = await User.create({
- firstName: 'Bart',
- lastName: 'Simpson'
- }, { transaction: t });
-
- await user.addSibling({
- firstName: 'Lisa',
- lastName: 'Simpson'
- }, { transaction: t });
+ const user = await User.create(
+ {
+ firstName: 'Bart',
+ lastName: 'Simpson',
+ },
+ { transaction: t },
+ );
+
+ await user.addSibling(
+ {
+ firstName: 'Lisa',
+ lastName: 'Simpson',
+ },
+ { transaction: t },
+ );
// If the execution reaches this line, no errors were thrown.
// We commit the transaction.
await t.commit();
-
} catch (error) {
-
// If the execution reaches this line, an error was thrown.
// We rollback the transaction.
await t.rollback();
-
}
```
-As shown above, the *unmanaged transaction* approach requires that you commit and rollback the transaction manually, when necessary.
+As shown above, the _unmanaged transaction_ approach requires that you commit and rollback the transaction manually, when necessary.
## Managed transactions
@@ -53,43 +55,43 @@ Managed transactions handle committing or rolling back the transaction automatic
The following will happen in this case:
-* Sequelize will automatically start a transaction and obtain a transaction object `t`
-* Then, Sequelize will execute the callback you provided, passing `t` into it
-* If your callback throws an error, Sequelize will automatically rollback the transaction
-* If your callback succeeds, Sequelize will automatically commit the transaction
-* Only then the `sequelize.transaction` call will settle:
- * Either resolving with the resolution of your callback
- * Or, if your callback throws, rejecting with the thrown error
+- Sequelize will automatically start a transaction and obtain a transaction object `t`
+- Then, Sequelize will execute the callback you provided, passing `t` into it
+- If your callback throws an error, Sequelize will automatically rollback the transaction
+- If your callback succeeds, Sequelize will automatically commit the transaction
+- Only then the `sequelize.transaction` call will settle:
+ - Either resolving with the resolution of your callback
+ - Or, if your callback throws, rejecting with the thrown error
Example code:
```js
try {
-
- const result = await sequelize.transaction(async (t) => {
-
- const user = await User.create({
- firstName: 'Abraham',
- lastName: 'Lincoln'
- }, { transaction: t });
-
- await user.setShooter({
- firstName: 'John',
- lastName: 'Boothe'
- }, { transaction: t });
+ const result = await sequelize.transaction(async t => {
+ const user = await User.create(
+ {
+ firstName: 'Abraham',
+ lastName: 'Lincoln',
+ },
+ { transaction: t },
+ );
+
+ await user.setShooter(
+ {
+ firstName: 'John',
+ lastName: 'Boothe',
+ },
+ { transaction: t },
+ );
return user;
-
});
// If the execution reaches this line, the transaction has been committed successfully
// `result` is whatever was returned from the transaction callback (the `user`, in this case)
-
} catch (error) {
-
// If the execution reaches this line, an error occurred.
// The transaction has already been rolled back automatically by Sequelize!
-
}
```
@@ -97,14 +99,17 @@ Note that `t.commit()` and `t.rollback()` were not called directly (which is cor
### Throw errors to rollback
-When using the managed transaction you should *never* commit or rollback the transaction manually. If all queries are successful (in the sense of not throwing any error), but you still want to rollback the transaction, you should throw an error yourself:
+When using the managed transaction you should _never_ commit or rollback the transaction manually. If all queries are successful (in the sense of not throwing any error), but you still want to rollback the transaction, you should throw an error yourself:
```js
await sequelize.transaction(async t => {
- const user = await User.create({
- firstName: 'Abraham',
- lastName: 'Lincoln'
- }, { transaction: t });
+ const user = await User.create(
+ {
+ firstName: 'Abraham',
+ lastName: 'Lincoln',
+ },
+ { transaction: t },
+ );
// Woops, the query was successful but we still want to roll back!
// We throw an error manually, so that Sequelize handles everything automatically.
@@ -130,16 +135,16 @@ Sequelize.useCLS(namespace);
new Sequelize(....);
```
-Notice, that the `useCLS()` method is on the *constructor*, not on an instance of sequelize. This means that all instances will share the same namespace, and that CLS is all-or-nothing - you cannot enable it only for some instances.
+Notice, that the `useCLS()` method is on the _constructor_, not on an instance of sequelize. This means that all instances will share the same namespace, and that CLS is all-or-nothing - you cannot enable it only for some instances.
CLS works like a thread-local storage for callbacks. What this means in practice is that different callback chains can access local variables by using the CLS namespace. When CLS is enabled sequelize will set the `transaction` property on the namespace when a new transaction is created. Since variables set within a callback chain are private to that chain several concurrent transactions can exist at the same time:
```js
-sequelize.transaction((t1) => {
+sequelize.transaction(t1 => {
namespace.get('transaction') === t1; // true
});
-sequelize.transaction((t2) => {
+sequelize.transaction(t2 => {
namespace.get('transaction') === t2; // true
});
```
@@ -147,7 +152,7 @@ sequelize.transaction((t2) => {
In most case you won't need to access `namespace.get('transaction')` directly, since all queries will automatically look for a transaction on the namespace:
```js
-sequelize.transaction((t1) => {
+sequelize.transaction(t1 => {
// With CLS enabled, the user will be created inside the transaction
return User.create({ name: 'Alice' });
});
@@ -157,19 +162,19 @@ sequelize.transaction((t1) => {
You can have concurrent transactions within a sequence of queries or have some of them excluded from any transactions. Use the `transaction` option to control which transaction a query belongs to:
-**Note:** *SQLite does not support more than one transaction at the same time.*
+**Note:** _SQLite does not support more than one transaction at the same time._
### With CLS enabled
```js
-sequelize.transaction((t1) => {
- return sequelize.transaction((t2) => {
+sequelize.transaction(t1 => {
+ return sequelize.transaction(t2 => {
// With CLS enabled, queries here will by default use t2.
// Pass in the `transaction` option to define/alter the transaction they belong to.
return Promise.all([
- User.create({ name: 'Bob' }, { transaction: null }),
- User.create({ name: 'Mallory' }, { transaction: t1 }),
- User.create({ name: 'John' }) // this would default to t2
+ User.create({ name: 'Bob' }, { transaction: null }),
+ User.create({ name: 'Mallory' }, { transaction: t1 }),
+ User.create({ name: 'John' }), // this would default to t2
]);
});
});
@@ -191,10 +196,10 @@ The possible isolations levels to use when starting a transaction:
const { Transaction } = require('sequelize');
// The following are valid isolation levels:
-Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED // "READ UNCOMMITTED"
-Transaction.ISOLATION_LEVELS.READ_COMMITTED // "READ COMMITTED"
-Transaction.ISOLATION_LEVELS.REPEATABLE_READ // "REPEATABLE READ"
-Transaction.ISOLATION_LEVELS.SERIALIZABLE // "SERIALIZABLE"
+Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED; // "READ UNCOMMITTED"
+Transaction.ISOLATION_LEVELS.READ_COMMITTED; // "READ COMMITTED"
+Transaction.ISOLATION_LEVELS.REPEATABLE_READ; // "REPEATABLE READ"
+Transaction.ISOLATION_LEVELS.SERIALIZABLE; // "SERIALIZABLE"
```
By default, sequelize uses the isolation level of the database. If you want to use a different isolation level, pass in the desired level as the first argument:
@@ -202,11 +207,14 @@ By default, sequelize uses the isolation level of the database. If you want to u
```js
const { Transaction } = require('sequelize');
-await sequelize.transaction({
- isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
-}, async (t) => {
- // Your code
-});
+await sequelize.transaction(
+ {
+ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE,
+ },
+ async t => {
+ // Your code
+ },
+);
```
You can also overwrite the `isolationLevel` setting globally with an option in the Sequelize constructor:
@@ -215,7 +223,7 @@ You can also overwrite the `isolationLevel` setting globally with an option in t
const { Sequelize, Transaction } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:', {
- isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
+ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE,
});
```
@@ -236,9 +244,9 @@ await User.create({ name: 'Foo Bar' }, { transaction: t });
await User.findAll({
where: {
- name: 'Foo Bar'
+ name: 'Foo Bar',
},
- transaction: t
+ transaction: t,
});
```
@@ -250,7 +258,7 @@ An `afterCommit` hook can be added to both managed and unmanaged transaction obj
```js
// Managed transaction:
-await sequelize.transaction(async (t) => {
+await sequelize.transaction(async t => {
t.afterCommit(() => {
// Your logic
});
@@ -266,13 +274,13 @@ await t.commit();
The callback passed to `afterCommit` can be `async`. In this case:
-* For a managed transaction: the `sequelize.transaction` call will wait for it before settling;
-* For an unmanaged transaction: the `t.commit` call will wait for it before settling.
+- For a managed transaction: the `sequelize.transaction` call will wait for it before settling;
+- For an unmanaged transaction: the `t.commit` call will wait for it before settling.
Notes:
-* The `afterCommit` hook is not raised if the transaction is rolled back;
-* The `afterCommit` hook does not modify the return value of the transaction (unlike most hooks)
+- The `afterCommit` hook is not raised if the transaction is rolled back;
+- The `afterCommit` hook does not modify the return value of the transaction (unlike most hooks)
You can use the `afterCommit` hook in conjunction with model hooks to know when a instance is saved and available outside of a transaction
@@ -297,7 +305,7 @@ Queries within a `transaction` can be performed with locks:
return User.findAll({
limit: 1,
lock: true,
- transaction: t1
+ transaction: t1,
});
```
@@ -308,6 +316,6 @@ return User.findAll({
limit: 1,
lock: true,
skipLocked: true,
- transaction: t2
+ transaction: t2,
});
```
diff --git a/versioned_docs/version-6.x.x/other-topics/typescript.md b/versioned_docs/version-6.x.x/other-topics/typescript.md
index 8cf8b7a1..18a09766 100644
--- a/versioned_docs/version-6.x.x/other-topics/typescript.md
+++ b/versioned_docs/version-6.x.x/other-topics/typescript.md
@@ -35,8 +35,8 @@ import { Model, Optional } from 'sequelize';
// We don't recommend doing this. Read on for the new way of declaring Model typings.
type UserAttributes = {
- id: number,
- name: string,
+ id: number;
+ name: string;
// other attributes...
};
@@ -80,7 +80,7 @@ Important things to know about `InferAttributes` & `InferCreationAttributes` wor
- Getter & setters are not automatically excluded. Set their return / parameter type to `NonAttribute`,
or add them to `omit` to exclude them.
-`InferCreationAttributes` works the same way as `InferAttributes` with one exception:Properties typed using the `CreationOptional` type
+`InferCreationAttributes` works the same way as `InferAttributes` with one exception:Properties typed using the `CreationOptional` type
will be marked as optional.
Note that attributes that accept `null`, or `undefined` do not need to use `CreationOptional`:
@@ -124,7 +124,13 @@ Some attributes don't actually need to be passed to `Model.init`, this is how yo
Use the `ForeignKey<>` branded type to make `Model.init` aware of the fact that it isn't necessary to configure the foreign key:
```typescript
- import { Model, InferAttributes, InferCreationAttributes, DataTypes, ForeignKey } from 'sequelize';
+ import {
+ Model,
+ InferAttributes,
+ InferCreationAttributes,
+ DataTypes,
+ ForeignKey,
+ } from 'sequelize';
class Project extends Model, InferCreationAttributes> {
id: number;
@@ -135,13 +141,16 @@ Some attributes don't actually need to be passed to `Model.init`, this is how yo
Project.belongsTo(User);
// therefore, `userId` doesn't need to be specified here.
- Project.init({
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
+ Project.init(
+ {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ },
},
- }, { sequelize });
+ { sequelize },
+ );
```
- Timestamp attributes managed by Sequelize (by default, `createdAt`, `updatedAt`, and `deletedAt`) don't need to be configured using `Model.init`,
@@ -156,17 +165,20 @@ Some attributes don't actually need to be passed to `Model.init`, this is how yo
updatedAt: Date;
}
- User.init({
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
+ User.init(
+ {
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ },
+ // technically, `createdAt` & `updatedAt` are added by Sequelize and don't need to be configured in Model.init
+ // but the typings of Model.init do not know this. Add the following to mute the typing error:
+ createdAt: DataTypes.DATE,
+ updatedAt: DataTypes.DATE,
},
- // technically, `createdAt` & `updatedAt` are added by Sequelize and don't need to be configured in Model.init
- // but the typings of Model.init do not know this. Add the following to mute the typing error:
- createdAt: DataTypes.DATE,
- updatedAt: DataTypes.DATE,
- }, { sequelize });
+ { sequelize },
+ );
```
### Usage without strict types for attributes
@@ -195,12 +207,19 @@ import defineExample from '!!raw-loader!@site/.sequelize/v6/test/types/typescrip
### Requesting a Model Class
-`ModelStatic` is designed to be used to type a Model *class*.
+`ModelStatic` is designed to be used to type a Model _class_.
Here is an example of a utility method that requests a Model Class, and returns the list of primary keys defined in that class:
```typescript
-import { ModelStatic, ModelAttributeColumnOptions, Model, InferAttributes, InferCreationAttributes, CreationOptional } from 'sequelize';
+import {
+ ModelStatic,
+ ModelAttributeColumnOptions,
+ Model,
+ InferAttributes,
+ InferCreationAttributes,
+ CreationOptional,
+} from 'sequelize';
/**
* Returns the list of attributes that are part of the model's primary key.
@@ -221,13 +240,16 @@ class User extends Model, InferCreationAttributes> {
id: CreationOptional;
}
-User.init({
- id: {
- type: DataTypes.INTEGER.UNSIGNED,
- autoIncrement: true,
- primaryKey: true
+User.init(
+ {
+ id: {
+ type: DataTypes.INTEGER.UNSIGNED,
+ autoIncrement: true,
+ primaryKey: true,
+ },
},
-}, { sequelize });
+ { sequelize },
+);
const primaryAttributes = getPrimaryKeyAttributes(User);
```
@@ -255,10 +277,13 @@ import {
InferAttributes,
InferCreationAttributes,
CreationOptional,
- Attributes
+ Attributes,
} from 'sequelize';
-export function getAttributeMetadata(model: ModelStatic, attributeName: keyof Attributes): ModelAttributeColumnOptions {
+export function getAttributeMetadata(
+ model: ModelStatic,
+ attributeName: keyof Attributes,
+): ModelAttributeColumnOptions {
const attribute = model.rawAttributes[attributeName];
if (attribute == null) {
throw new Error(`Attribute ${attributeName} does not exist on model ${model.name}`);
@@ -271,13 +296,16 @@ class User extends Model, InferCreationAttributes> {
id: CreationOptional;
}
-User.init({
- id: {
- type: DataTypes.INTEGER.UNSIGNED,
- autoIncrement: true,
- primaryKey: true
+User.init(
+ {
+ id: {
+ type: DataTypes.INTEGER.UNSIGNED,
+ autoIncrement: true,
+ primaryKey: true,
+ },
},
-}, { sequelize });
+ { sequelize },
+);
const idAttributeMeta = getAttributeMetadata(User, 'id'); // works!
diff --git a/versioned_sidebars/version-6.x.x-sidebars.json b/versioned_sidebars/version-6.x.x-sidebars.json
index 9c56c9d0..caea0c03 100644
--- a/versioned_sidebars/version-6.x.x-sidebars.json
+++ b/versioned_sidebars/version-6.x.x-sidebars.json
@@ -1,6 +1,8 @@
{
- "tutorialSidebar": [{
- "type": "autogenerated",
- "dirName": "."
- }]
+ "tutorialSidebar": [
+ {
+ "type": "autogenerated",
+ "dirName": "."
+ }
+ ]
}
diff --git a/versions.json b/versions.json
index 7b9a139d..11191859 100644
--- a/versions.json
+++ b/versions.json
@@ -1,3 +1 @@
-[
- "6.x.x"
-]
+["6.x.x"]
diff --git a/yarn.lock b/yarn.lock
index db2c02f4..ee64f747 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1208,6 +1208,13 @@
dependencies:
regenerator-runtime "^0.14.0"
+"@babel/runtime@^7.21.0":
+ version "7.24.4"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd"
+ integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==
+ dependencies:
+ regenerator-runtime "^0.14.0"
+
"@babel/template@^7.12.7", "@babel/template@^7.22.15", "@babel/template@^7.23.9":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a"
@@ -1724,27 +1731,26 @@
url-loader "^4.1.1"
webpack "^5.88.1"
-"@ephys/eslint-config-typescript@19.0.2":
- version "19.0.2"
- resolved "https://registry.yarnpkg.com/@ephys/eslint-config-typescript/-/eslint-config-typescript-19.0.2.tgz#53ca3acaf741126e1d8302da1537e95cbd33209a"
- integrity sha512-poWjpwmHWseBZD74Q8J7RR993hFnsUMvEYeoETkNKh7HaeLtfMXmcH43y4e/u64OsUuZIrMRZ3YYAEpakr7g2A==
+"@ephys/eslint-config-typescript@20.1.3":
+ version "20.1.3"
+ resolved "https://registry.yarnpkg.com/@ephys/eslint-config-typescript/-/eslint-config-typescript-20.1.3.tgz#e3d16232a8c2799bfb8a1225fb944cef80fba29c"
+ integrity sha512-BDyOGe6XOowVJtIU1ouK41Lq30ROwuvi7cLnQhwuVu7v2SgrJ95JWDlrZ26OLd/KA/clb0Y5OcoY04d3a24oEA==
dependencies:
- "@ephys/eslint-config" "^19.0.2"
- "@typescript-eslint/eslint-plugin" "^5.58.0"
- "@typescript-eslint/parser" "^5.58.0"
- eslint-import-resolver-typescript "^3.5.5"
+ "@ephys/eslint-config" "^20.1.3"
+ "@typescript-eslint/eslint-plugin" "^7.2.0"
+ "@typescript-eslint/parser" "^7.2.0"
+ eslint-import-resolver-typescript "^3.6.1"
-"@ephys/eslint-config@^19.0.2":
- version "19.0.2"
- resolved "https://registry.yarnpkg.com/@ephys/eslint-config/-/eslint-config-19.0.2.tgz#f9adc763a61cf683a65a761b49ab35c9354374d3"
- integrity sha512-WvWiebxqBHwDqcAxNLFRVabZK8bCPf44RO0OGttwUxnPpDzB4aMrZP5xZTFqw5hfQdA7pD/TdY++hIHFzRQx9w==
+"@ephys/eslint-config@^20.1.3":
+ version "20.1.3"
+ resolved "https://registry.yarnpkg.com/@ephys/eslint-config/-/eslint-config-20.1.3.tgz#c19b489f7d6ccf8711c8764d07e3838761b7f535"
+ integrity sha512-rcWsUZjBMSYr9tEB2lnY1RlpPQ7FyzlStMBpJRU2VuniRv/Qabe+FAmkl8kLPymPDEcQTQL3yqGUHZeTMv8KZg==
dependencies:
"@babel/core" "^7.21.4"
"@babel/eslint-parser" "^7.21.3"
eslint "^8.38.0"
eslint-plugin-eslint-comments "^3.2.0"
eslint-plugin-import "^2.27.5"
- eslint-plugin-import-newlines "^1.3.1"
eslint-plugin-jest "^27.2.1"
eslint-plugin-json "^3.1.0"
eslint-plugin-jsx-a11y "^6.7.1"
@@ -1752,16 +1758,17 @@
eslint-plugin-react "^7.32.2"
eslint-plugin-react-hooks "^4.6.0"
eslint-plugin-small-import "^1.0.0"
+ eslint-plugin-sort-destructure-keys "^1.5.0"
eslint-plugin-unicorn "^46.0.0"
-"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0":
+"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
dependencies:
eslint-visitor-keys "^3.3.0"
-"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
version "4.10.0"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
@@ -1998,6 +2005,14 @@
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.2.tgz#053f1540703faa81dea2966b768ee5581c66aeda"
integrity sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==
+"@sequelize/utils@7.0.0-alpha.40":
+ version "7.0.0-alpha.40"
+ resolved "https://registry.yarnpkg.com/@sequelize/utils/-/utils-7.0.0-alpha.40.tgz#86a8a4a9f0120c221fc088af13ab12493582cea1"
+ integrity sha512-npN1C6m3QPvrDlZSveB9DHSyx2I4EGEshIv7R7jCXt7iKFVG2o/LLYlFK4rwzND5YtNRI6ES69UxLg/65KSa5g==
+ dependencies:
+ "@types/lodash" "^4.17.0"
+ lodash "^4.17.21"
+
"@sideway/address@^4.1.5":
version "4.1.5"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
@@ -2324,7 +2339,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
@@ -2334,6 +2349,11 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
+"@types/lodash@^4.17.0":
+ version "4.17.0"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.0.tgz#d774355e41f372d5350a4d0714abb48194a489c3"
+ integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==
+
"@types/mdast@^3.0.0":
version "3.0.15"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.15.tgz#49c524a263f30ffa28b71ae282f813ed000ab9f5"
@@ -2474,6 +2494,11 @@
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.7.tgz#326f5fdda70d13580777bcaa1bc6fa772a5aef0e"
integrity sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==
+"@types/semver@^7.5.8":
+ version "7.5.8"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
+ integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
+
"@types/send@*":
version "0.17.4"
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
@@ -2534,30 +2559,32 @@
dependencies:
"@types/yargs-parser" "*"
-"@typescript-eslint/eslint-plugin@^5.58.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
- integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==
- dependencies:
- "@eslint-community/regexpp" "^4.4.0"
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/type-utils" "5.62.0"
- "@typescript-eslint/utils" "5.62.0"
+"@typescript-eslint/eslint-plugin@^7.2.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz#1f5df5cda490a0bcb6fbdd3382e19f1241024242"
+ integrity sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==
+ dependencies:
+ "@eslint-community/regexpp" "^4.10.0"
+ "@typescript-eslint/scope-manager" "7.6.0"
+ "@typescript-eslint/type-utils" "7.6.0"
+ "@typescript-eslint/utils" "7.6.0"
+ "@typescript-eslint/visitor-keys" "7.6.0"
debug "^4.3.4"
graphemer "^1.4.0"
- ignore "^5.2.0"
- natural-compare-lite "^1.4.0"
- semver "^7.3.7"
- tsutils "^3.21.0"
+ ignore "^5.3.1"
+ natural-compare "^1.4.0"
+ semver "^7.6.0"
+ ts-api-utils "^1.3.0"
-"@typescript-eslint/parser@^5.58.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
- integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==
+"@typescript-eslint/parser@^7.2.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.6.0.tgz#0aca5de3045d68b36e88903d15addaf13d040a95"
+ integrity sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==
dependencies:
- "@typescript-eslint/scope-manager" "5.62.0"
- "@typescript-eslint/types" "5.62.0"
- "@typescript-eslint/typescript-estree" "5.62.0"
+ "@typescript-eslint/scope-manager" "7.6.0"
+ "@typescript-eslint/types" "7.6.0"
+ "@typescript-eslint/typescript-estree" "7.6.0"
+ "@typescript-eslint/visitor-keys" "7.6.0"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.62.0":
@@ -2568,21 +2595,34 @@
"@typescript-eslint/types" "5.62.0"
"@typescript-eslint/visitor-keys" "5.62.0"
-"@typescript-eslint/type-utils@5.62.0":
- version "5.62.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a"
- integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==
+"@typescript-eslint/scope-manager@7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz#1e9972f654210bd7500b31feadb61a233f5b5e9d"
+ integrity sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==
dependencies:
- "@typescript-eslint/typescript-estree" "5.62.0"
- "@typescript-eslint/utils" "5.62.0"
+ "@typescript-eslint/types" "7.6.0"
+ "@typescript-eslint/visitor-keys" "7.6.0"
+
+"@typescript-eslint/type-utils@7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz#644f75075f379827d25fe0713e252ccd4e4a428c"
+ integrity sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==
+ dependencies:
+ "@typescript-eslint/typescript-estree" "7.6.0"
+ "@typescript-eslint/utils" "7.6.0"
debug "^4.3.4"
- tsutils "^3.21.0"
+ ts-api-utils "^1.3.0"
"@typescript-eslint/types@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
+"@typescript-eslint/types@7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.6.0.tgz#53dba7c30c87e5f10a731054266dd905f1fbae38"
+ integrity sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==
+
"@typescript-eslint/typescript-estree@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
@@ -2596,7 +2636,34 @@
semver "^7.3.7"
tsutils "^3.21.0"
-"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0":
+"@typescript-eslint/typescript-estree@7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz#112a3775563799fd3f011890ac8322f80830ac17"
+ integrity sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==
+ dependencies:
+ "@typescript-eslint/types" "7.6.0"
+ "@typescript-eslint/visitor-keys" "7.6.0"
+ debug "^4.3.4"
+ globby "^11.1.0"
+ is-glob "^4.0.3"
+ minimatch "^9.0.4"
+ semver "^7.6.0"
+ ts-api-utils "^1.3.0"
+
+"@typescript-eslint/utils@7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.6.0.tgz#e400d782280b6f724c8a1204269d984c79202282"
+ integrity sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.4.0"
+ "@types/json-schema" "^7.0.15"
+ "@types/semver" "^7.5.8"
+ "@typescript-eslint/scope-manager" "7.6.0"
+ "@typescript-eslint/types" "7.6.0"
+ "@typescript-eslint/typescript-estree" "7.6.0"
+ semver "^7.6.0"
+
+"@typescript-eslint/utils@^5.10.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
@@ -2618,6 +2685,14 @@
"@typescript-eslint/types" "5.62.0"
eslint-visitor-keys "^3.3.0"
+"@typescript-eslint/visitor-keys@7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz#d1ce13145844379021e1f9bd102c1d78946f4e76"
+ integrity sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==
+ dependencies:
+ "@typescript-eslint/types" "7.6.0"
+ eslint-visitor-keys "^3.4.3"
+
"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
@@ -2914,7 +2989,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@^4.1.0:
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
@@ -3274,6 +3349,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@@ -3585,6 +3667,15 @@ cli-width@^3.0.0:
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
+cliui@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+ integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.1"
+ wrap-ansi "^7.0.0"
+
clone-deep@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
@@ -3713,6 +3804,21 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+concurrently@8.2.2:
+ version "8.2.2"
+ resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.2.tgz#353141985c198cfa5e4a3ef90082c336b5851784"
+ integrity sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==
+ dependencies:
+ chalk "^4.1.2"
+ date-fns "^2.30.0"
+ lodash "^4.17.21"
+ rxjs "^7.8.1"
+ shell-quote "^1.8.1"
+ spawn-command "0.0.2"
+ supports-color "^8.1.1"
+ tree-kill "^1.2.2"
+ yargs "^17.7.2"
+
config-chain@^1.1.11:
version "1.1.13"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
@@ -4327,6 +4433,13 @@ damerau-levenshtein@^1.0.8:
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
+date-fns@^2.30.0:
+ version "2.30.0"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
+ integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
+ dependencies:
+ "@babel/runtime" "^7.21.0"
+
dayjs@^1.11.7:
version "1.11.10"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
@@ -4933,7 +5046,7 @@ eslint-import-resolver-node@^0.3.9:
is-core-module "^2.13.0"
resolve "^1.22.4"
-eslint-import-resolver-typescript@^3.5.5:
+eslint-import-resolver-typescript@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa"
integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==
@@ -4973,11 +5086,6 @@ eslint-plugin-eslint-comments@^3.2.0:
escape-string-regexp "^1.0.5"
ignore "^5.0.5"
-eslint-plugin-import-newlines@^1.3.1:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/eslint-plugin-import-newlines/-/eslint-plugin-import-newlines-1.3.4.tgz#c3917ae478b1dcce2a920637eaa8af001b8e1477"
- integrity sha512-Lmf/BbK+EQKUfjKPcZpslE/KTGYlgaI8ZJ/sYzdbb3BVTg5+GmLBLHBjsUKNEVRM1SEhDTF/didtOSYKi4tSnQ==
-
eslint-plugin-import@^2.27.5:
version "2.29.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643"
@@ -5121,6 +5229,13 @@ eslint-plugin-small-import@^1.0.0:
dependencies:
eslint "^6.7.2"
+eslint-plugin-sort-destructure-keys@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-sort-destructure-keys/-/eslint-plugin-sort-destructure-keys-1.5.0.tgz#dc45ff119b6886d4e72d3e0ff8a528af83b89388"
+ integrity sha512-xGLyqHtbFXZNXQSvAiQ4ISBYokrbUywEhmaA50fKtSKgceCv5y3zjoNuZwcnajdM6q29Nxj+oXC9KcqfMsAPrg==
+ dependencies:
+ natural-compare-lite "^1.4.0"
+
eslint-plugin-unicorn@^46.0.0:
version "46.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-46.0.1.tgz#222ff65b30b2d9ed6f90de908ceb6a05dd0514d9"
@@ -5846,6 +5961,11 @@ gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2:
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
get-east-asian-width@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e"
@@ -6463,7 +6583,7 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-ignore@^5.0.5, ignore@^5.2.0, ignore@^5.2.4:
+ignore@^5.0.5, ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
@@ -8471,6 +8591,13 @@ minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch
dependencies:
brace-expansion "^1.1.7"
+minimatch@^9.0.4:
+ version "9.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51"
+ integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==
+ dependencies:
+ brace-expansion "^2.0.1"
+
minimist@^1.2.0, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
@@ -10016,6 +10143,11 @@ repeat-string@^1.0.0, repeat-string@^1.5.4:
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -10165,6 +10297,13 @@ rxjs@^6.6.0:
dependencies:
tslib "^1.9.0"
+rxjs@^7.8.1:
+ version "7.8.1"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
+ integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
+ dependencies:
+ tslib "^2.1.0"
+
sade@^1.7.3:
version "1.8.1"
resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
@@ -10311,7 +10450,7 @@ semver@^6.1.2, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4:
+semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4, semver@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
@@ -10595,6 +10734,11 @@ space-separated-tokens@^2.0.0:
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f"
integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
+spawn-command@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
+ integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==
+
spdx-correct@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c"
@@ -10693,7 +10837,7 @@ string-width@^3.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
-string-width@^4.1.0, string-width@^4.2.0:
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -10901,7 +11045,7 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
-supports-color@^8.0.0:
+supports-color@^8.0.0, supports-color@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
@@ -11034,6 +11178,11 @@ totalist@^3.0.0:
resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8"
integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==
+tree-kill@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
+ integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
+
trim-lines@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338"
@@ -11059,6 +11208,11 @@ trough@^2.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f"
integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
+ts-api-utils@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
+ integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
+
ts-dedent@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
@@ -11079,7 +11233,7 @@ tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-tslib@^2.0.3, tslib@^2.3.1, tslib@^2.6.0:
+tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@@ -11870,6 +12024,15 @@ word-wrap@~1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
@@ -11937,6 +12100,11 @@ xtend@^4.0.0, xtend@^4.0.1:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
yallist@^3.0.2:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
@@ -11957,6 +12125,24 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
+yargs-parser@^21.1.1:
+ version "21.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+ integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
+yargs@^17.7.2:
+ version "17.7.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
+ integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
+ dependencies:
+ cliui "^8.0.1"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.1.1"
+
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"