Skip to content
Toggle navigation
Projects
Groups
Snippets
Help
public
/
sequelize
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
不要怂,就是干,撸起袖子干!
Commit a4c10f8c
authored
Dec 19, 2013
by
Mick Hansen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor how doubleLink is determined
1 parent
ea8ed2bb
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
150 additions
and
40 deletions
lib/associations/has-many.js
lib/associations/mixin.js
lib/sequelize.js
test/associations/has-many.test.js
test/support.js
lib/associations/has-many.js
View file @
a4c10f8
...
@@ -7,25 +7,32 @@ var HasManySingleLinked = require("./has-many-single-linked")
...
@@ -7,25 +7,32 @@ var HasManySingleLinked = require("./has-many-single-linked")
,
HasManyMultiLinked
=
require
(
"./has-many-double-linked"
)
,
HasManyMultiLinked
=
require
(
"./has-many-double-linked"
)
module
.
exports
=
(
function
()
{
module
.
exports
=
(
function
()
{
var
HasMany
=
function
(
srcDAO
,
targetDAO
,
options
)
{
var
HasMany
=
function
(
source
,
target
,
options
)
{
var
self
=
this
this
.
associationType
=
'HasMany'
this
.
associationType
=
'HasMany'
this
.
source
=
srcDAO
this
.
source
=
source
this
.
target
=
targetDAO
this
.
target
=
target
this
.
targetAssociation
=
null
this
.
options
=
options
this
.
options
=
options
this
.
sequelize
=
source
.
daoFactoryManager
.
sequelize
this
.
through
=
options
.
through
this
.
through
=
options
.
through
this
.
isSelfAssociation
=
(
this
.
source
.
tableName
===
this
.
target
.
tableName
)
this
.
isSelfAssociation
=
(
this
.
source
.
tableName
===
this
.
target
.
tableName
)
this
.
doubleLinked
=
false
this
.
combinedTableName
=
Utils
.
combineTableNames
(
this
.
source
.
tableName
,
this
.
isSelfAssociation
?
(
this
.
options
.
as
||
this
.
target
.
tableName
)
:
this
.
target
.
tableName
)
// Map to through for BC
/*
* Map joinTableModel/Name to through for BC
*/
if
(
this
.
through
===
undefined
)
{
if
(
this
.
through
===
undefined
)
{
this
.
through
=
this
.
options
.
joinTableModel
||
this
.
options
.
joinTableName
;
this
.
through
=
this
.
options
.
joinTableModel
||
this
.
options
.
joinTableName
;
}
if
(
typeof
this
.
through
===
"string"
)
{
this
.
through
=
this
.
source
.
daoFactoryManager
.
sequelize
.
define
(
this
.
through
,
{},
_
.
extend
(
this
.
options
,
{
tableName
:
this
.
through
}))
}
/*
* If both are undefined, see if useJunctionTable was false (for self associations) - else assume through to be true
*/
if
(
this
.
through
===
undefined
)
{
if
(
this
.
through
===
undefined
)
{
if
(
this
.
options
.
useJunctionTable
===
false
)
{
if
(
this
.
options
.
useJunctionTable
===
false
)
{
this
.
through
=
null
;
this
.
through
=
null
;
...
@@ -33,16 +40,59 @@ module.exports = (function() {
...
@@ -33,16 +40,59 @@ module.exports = (function() {
this
.
through
=
true
;
this
.
through
=
true
;
}
}
}
}
}
if
(
this
.
through
===
true
)
{
/*
this
.
through
=
Utils
.
combineTableNames
(
* Determine associationAccessor, especially for include options to identify the correct model
this
.
source
.
tableName
,
*/
this
.
isSelfAssociation
?
(
this
.
options
.
as
||
this
.
target
.
tableName
)
:
this
.
target
.
tableName
)
this
.
associationAccessor
=
this
.
options
.
as
if
(
!
this
.
associationAccessor
&&
(
typeof
this
.
through
===
"string"
||
Object
(
this
.
through
)
===
this
.
through
))
{
this
.
associationAccessor
=
this
.
through
.
tableName
||
this
.
through
}
else
if
(
!
this
.
associationAccessor
)
{
this
.
associationAccessor
=
this
.
combinedTableName
}
/*
* Find partner DAOFactory if present, to identify double linked association
*/
if
(
this
.
through
)
{
_
.
each
(
this
.
target
.
associations
,
function
(
association
,
accessor
)
{
if
(
self
.
source
===
association
.
target
)
{
var
paired
=
false
// If through is default, we determine pairing by the accesor value (i.e. DAOFactory's using as won't pair, but regular ones will)
if
(
self
.
through
===
true
&&
accessor
===
self
.
associationAccessor
)
paired
=
true
// If through is not default, determine pairing by through value (model/string)
if
(
self
.
through
!==
true
&&
self
.
options
.
through
===
association
.
options
.
through
)
paired
=
true
// If
if
(
paired
)
{
self
.
doubleLinked
=
true
association
.
doubleLinked
=
true
self
.
targetAssociation
=
association
association
.
targetAssociation
=
self
}
}
})
}
/*
* If we are double linked, and through is either default or a string, we create the through model and set it on both associations
*/
if
(
this
.
doubleLinked
)
{
if
(
this
.
through
===
true
)
this
.
through
=
this
.
combinedTableName
if
(
typeof
this
.
through
===
"string"
)
{
this
.
through
=
this
.
sequelize
.
define
(
this
.
through
,
{},
_
.
extend
(
this
.
options
,
{
tableName
:
this
.
through
}))
this
.
targetAssociation
.
through
=
this
.
through
}
}
}
this
.
options
.
tableName
=
this
.
combinedName
=
(
this
.
through
===
Object
(
this
.
through
)
?
this
.
through
.
tableName
:
this
.
through
)
this
.
options
.
tableName
=
this
.
combinedName
=
(
this
.
through
===
Object
(
this
.
through
)
?
this
.
through
.
tableName
:
this
.
through
)
this
.
associationAccessor
=
this
.
options
.
as
||
this
.
combinedName
var
as
=
(
this
.
options
.
as
||
Utils
.
pluralize
(
this
.
target
.
tableName
,
this
.
target
.
options
.
language
))
var
as
=
(
this
.
options
.
as
||
Utils
.
pluralize
(
this
.
target
.
tableName
,
this
.
target
.
options
.
language
))
...
@@ -59,27 +109,27 @@ module.exports = (function() {
...
@@ -59,27 +109,27 @@ module.exports = (function() {
// the id is in the target table
// the id is in the target table
// or in an extra table which connects two tables
// or in an extra table which connects two tables
HasMany
.
prototype
.
injectAttributes
=
function
()
{
HasMany
.
prototype
.
injectAttributes
=
function
()
{
var
multiAssociation
=
this
.
target
.
associations
.
hasOwnProperty
(
this
.
associationAccessor
)
var
doubleLinked
=
this
.
doubleLinked
,
self
=
this
,
self
=
this
this
.
identifier
=
this
.
options
.
foreignKey
||
Utils
.
_
.
underscoredIf
(
Utils
.
singularize
(
this
.
source
.
tableName
,
this
.
source
.
options
.
language
)
+
"Id"
,
this
.
options
.
underscored
)
this
.
identifier
=
this
.
options
.
foreignKey
||
Utils
.
_
.
underscoredIf
(
Utils
.
singularize
(
this
.
source
.
tableName
,
this
.
source
.
options
.
language
)
+
"Id"
,
this
.
options
.
underscored
)
// is there already a single sided association between the source and the target?
// is there already a single sided association between the source and the target?
// or is the association on the model itself?
// or is the association on the model itself?
if
((
this
.
isSelfAssociation
&&
this
.
through
)
||
multiAssociation
)
{
if
((
this
.
isSelfAssociation
&&
this
.
through
)
||
doubleLinked
)
{
// remove the obsolete association identifier from the source
// remove the obsolete association identifier from the source
if
(
this
.
isSelfAssociation
)
{
if
(
this
.
isSelfAssociation
)
{
this
.
foreignIdentifier
=
Utils
.
_
.
underscoredIf
((
this
.
options
.
as
||
this
.
target
.
tableName
)
+
'Id'
,
this
.
options
.
underscored
)
this
.
foreignIdentifier
=
Utils
.
_
.
underscoredIf
((
this
.
options
.
as
||
this
.
target
.
tableName
)
+
'Id'
,
this
.
options
.
underscored
)
}
else
{
}
else
{
this
.
foreignIdentifier
=
this
.
target
.
associations
[
this
.
associationAccessor
]
.
identifier
this
.
foreignIdentifier
=
this
.
target
Association
.
identifier
this
.
target
.
associations
[
this
.
associationAccessor
]
.
foreignIdentifier
=
this
.
identifier
this
.
target
Association
.
foreignIdentifier
=
this
.
identifier
if
(
isForeignKeyDeletionAllowedFor
.
call
(
this
,
this
.
source
,
this
.
foreignIdentifier
))
{
if
(
isForeignKeyDeletionAllowedFor
.
call
(
this
,
this
.
source
,
this
.
foreignIdentifier
))
{
delete
this
.
source
.
rawAttributes
[
this
.
foreignIdentifier
]
delete
this
.
source
.
rawAttributes
[
this
.
foreignIdentifier
]
}
}
if
(
isForeignKeyDeletionAllowedFor
.
call
(
this
,
this
.
target
,
this
.
identifier
))
{
if
(
isForeignKeyDeletionAllowedFor
.
call
(
this
,
this
.
target
,
this
.
identifier
))
{
delete
this
.
target
.
associations
[
this
.
associationAccessor
]
.
source
.
rawAttributes
[
this
.
identifier
]
delete
this
.
target
Association
.
source
.
rawAttributes
[
this
.
identifier
]
}
}
}
}
...
@@ -92,7 +142,6 @@ module.exports = (function() {
...
@@ -92,7 +142,6 @@ module.exports = (function() {
combinedTableAttributes
[
this
.
identifier
]
=
{
type
:
sourceKeyType
,
primaryKey
:
true
}
combinedTableAttributes
[
this
.
identifier
]
=
{
type
:
sourceKeyType
,
primaryKey
:
true
}
combinedTableAttributes
[
this
.
foreignIdentifier
]
=
{
type
:
targetKeyType
,
primaryKey
:
true
}
combinedTableAttributes
[
this
.
foreignIdentifier
]
=
{
type
:
targetKeyType
,
primaryKey
:
true
}
if
(
Object
(
this
.
through
)
===
this
.
through
)
{
// remove any previously defined PKs
// remove any previously defined PKs
Utils
.
_
.
each
(
this
.
through
.
attributes
,
function
(
dataTypeString
,
attributeName
)
{
Utils
.
_
.
each
(
this
.
through
.
attributes
,
function
(
dataTypeString
,
attributeName
)
{
if
(
dataTypeString
.
toString
().
indexOf
(
'PRIMARY KEY'
)
!==
-
1
)
{
if
(
dataTypeString
.
toString
().
indexOf
(
'PRIMARY KEY'
)
!==
-
1
)
{
...
@@ -102,13 +151,6 @@ module.exports = (function() {
...
@@ -102,13 +151,6 @@ module.exports = (function() {
this
.
through
.
rawAttributes
=
Utils
.
_
.
merge
(
this
.
through
.
rawAttributes
,
combinedTableAttributes
)
this
.
through
.
rawAttributes
=
Utils
.
_
.
merge
(
this
.
through
.
rawAttributes
,
combinedTableAttributes
)
this
.
through
.
init
(
this
.
through
.
daoFactoryManager
)
this
.
through
.
init
(
this
.
through
.
daoFactoryManager
)
}
else
{
this
.
through
=
this
.
source
.
daoFactoryManager
.
sequelize
.
define
(
this
.
combinedName
,
combinedTableAttributes
,
this
.
options
)
}
if
(
!
this
.
isSelfAssociation
)
{
this
.
target
.
associations
[
this
.
associationAccessor
].
through
=
this
.
through
}
if
(
this
.
options
.
syncOnAssociation
)
{
if
(
this
.
options
.
syncOnAssociation
)
{
this
.
through
.
sync
()
this
.
through
.
sync
()
...
...
lib/associations/mixin.js
View file @
a4c10f8
...
@@ -6,14 +6,14 @@ var Utils = require("./../utils")
...
@@ -6,14 +6,14 @@ var Utils = require("./../utils")
/* Defines Mixin for all models. */
/* Defines Mixin for all models. */
var
Mixin
=
module
.
exports
=
function
(){}
var
Mixin
=
module
.
exports
=
function
(){}
Mixin
.
hasOne
=
function
(
associatedDAO
,
options
)
{
Mixin
.
hasOne
=
function
(
associatedDAO
Factory
,
options
)
{
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options
=
options
||
{}
options
=
options
||
{}
options
.
hooks
=
options
.
hooks
===
undefined
?
false
:
Boolean
(
options
.
hooks
)
options
.
hooks
=
options
.
hooks
===
undefined
?
false
:
Boolean
(
options
.
hooks
)
options
.
useHooks
=
options
.
hooks
options
.
useHooks
=
options
.
hooks
// the id is in the foreign table
// the id is in the foreign table
var
association
=
new
HasOne
(
this
,
associatedDAO
,
Utils
.
_
.
extend
((
options
||
{}),
this
.
options
))
var
association
=
new
HasOne
(
this
,
associatedDAO
Factory
,
Utils
.
_
.
extend
((
options
||
{}),
this
.
options
))
this
.
associations
[
association
.
associationAccessor
]
=
association
.
injectAttributes
()
this
.
associations
[
association
.
associationAccessor
]
=
association
.
injectAttributes
()
association
.
injectGetter
(
this
.
DAO
.
prototype
);
association
.
injectGetter
(
this
.
DAO
.
prototype
);
...
@@ -22,14 +22,14 @@ Mixin.hasOne = function(associatedDAO, options) {
...
@@ -22,14 +22,14 @@ Mixin.hasOne = function(associatedDAO, options) {
return
this
return
this
}
}
Mixin
.
belongsTo
=
function
(
associatedDAO
,
options
)
{
Mixin
.
belongsTo
=
function
(
associatedDAO
Factory
,
options
)
{
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options
=
options
||
{}
options
=
options
||
{}
options
.
hooks
=
options
.
hooks
===
undefined
?
false
:
Boolean
(
options
.
hooks
)
options
.
hooks
=
options
.
hooks
===
undefined
?
false
:
Boolean
(
options
.
hooks
)
options
.
useHooks
=
options
.
hooks
options
.
useHooks
=
options
.
hooks
// the id is in this table
// the id is in this table
var
association
=
new
BelongsTo
(
this
,
associatedDAO
,
Utils
.
_
.
extend
(
options
,
this
.
options
))
var
association
=
new
BelongsTo
(
this
,
associatedDAO
Factory
,
Utils
.
_
.
extend
(
options
,
this
.
options
))
this
.
associations
[
association
.
associationAccessor
]
=
association
.
injectAttributes
()
this
.
associations
[
association
.
associationAccessor
]
=
association
.
injectAttributes
()
association
.
injectGetter
(
this
.
DAO
.
prototype
)
association
.
injectGetter
(
this
.
DAO
.
prototype
)
...
@@ -38,15 +38,16 @@ Mixin.belongsTo = function(associatedDAO, options) {
...
@@ -38,15 +38,16 @@ Mixin.belongsTo = function(associatedDAO, options) {
return
this
return
this
}
}
Mixin
.
hasMany
=
function
(
associatedDAO
,
options
)
{
Mixin
.
hasMany
=
function
(
associatedDAO
Factory
,
options
)
{
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
// Since this is a mixin, we'll need a unique variable name for hooks (since DAOFactory will override our hooks option)
options
=
options
||
{}
options
=
options
||
{}
options
.
hooks
=
options
.
hooks
===
undefined
?
false
:
Boolean
(
options
.
hooks
)
options
.
hooks
=
options
.
hooks
===
undefined
?
false
:
Boolean
(
options
.
hooks
)
options
.
useHooks
=
options
.
hooks
options
.
useHooks
=
options
.
hooks
// the id is in the foreign table or in a connecting table
// the id is in the foreign table or in a connecting table
var
association
=
new
HasMany
(
this
,
associatedDAO
,
Utils
.
_
.
extend
((
options
||
{}),
this
.
options
))
var
association
=
new
HasMany
(
this
,
associatedDAO
Factory
,
Utils
.
_
.
extend
((
options
||
{}),
this
.
options
))
this
.
associations
[
association
.
associationAccessor
]
=
association
.
injectAttributes
()
this
.
associations
[
association
.
associationAccessor
]
=
association
.
injectAttributes
()
associatedDAOFactory
.
daoFactoryManager
.
sequelize
.
associations
.
push
(
association
)
association
.
injectGetter
(
this
.
DAO
.
prototype
)
association
.
injectGetter
(
this
.
DAO
.
prototype
)
association
.
injectSetter
(
this
.
DAO
.
prototype
)
association
.
injectSetter
(
this
.
DAO
.
prototype
)
...
...
lib/sequelize.js
View file @
a4c10f8
...
@@ -114,6 +114,7 @@ module.exports = (function() {
...
@@ -114,6 +114,7 @@ module.exports = (function() {
this
.
daoFactoryManager
=
new
DAOFactoryManager
(
this
)
this
.
daoFactoryManager
=
new
DAOFactoryManager
(
this
)
this
.
transactionManager
=
new
TransactionManager
(
this
)
this
.
transactionManager
=
new
TransactionManager
(
this
)
this
.
associations
=
[]
this
.
importCache
=
{}
this
.
importCache
=
{}
}
}
...
...
test/associations/has-many.test.js
View file @
a4c10f8
...
@@ -676,10 +676,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
...
@@ -676,10 +676,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
{
tableName
:
'tasks'
}
{
tableName
:
'tasks'
}
)
)
this
.
User
.
hasMany
(
this
.
Task
,
this
.
User
.
hasMany
(
this
.
Task
,
{
joinTableName
:
'user_has_tasks'
})
{
joinTableName
:
'user_has_tasks'
}
this
.
Task
.
hasMany
(
this
.
User
,
{
joinTableName
:
'user_has_tasks'
})
)
this
.
Task
.
hasMany
(
this
.
User
)
this
.
User
.
sync
({
force
:
true
}).
success
(
function
()
{
this
.
User
.
sync
({
force
:
true
}).
success
(
function
()
{
self
.
Task
.
sync
({
force
:
true
}).
success
(
function
()
{
self
.
Task
.
sync
({
force
:
true
}).
success
(
function
()
{
...
@@ -900,6 +898,73 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
...
@@ -900,6 +898,73 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
})
})
})
})
})
})
describe
(
'as'
,
function
()
{
it
(
"creates the join table when through is a string"
,
function
(
done
)
{
var
self
=
this
,
User
=
this
.
sequelize
.
define
(
'User'
,
{})
,
Group
=
this
.
sequelize
.
define
(
'Group'
,
{})
User
.
hasMany
(
Group
,
{
as
:
'MyGroups'
,
through
:
'group_user'
})
Group
.
hasMany
(
User
,
{
as
:
'MyUsers'
,
through
:
'group_user'
})
this
.
sequelize
.
sync
({
force
:
true
}).
success
(
function
()
{
self
.
sequelize
.
query
(
"SHOW TABLES LIKE 'group_user'"
).
success
(
function
(
res
)
{
expect
(
res
).
to
.
deep
.
equal
([
'group_user'
])
done
()
})
})
})
it
(
"creates the join table when through is a model"
,
function
(
done
)
{
var
self
=
this
,
User
=
this
.
sequelize
.
define
(
'User'
,
{})
,
Group
=
this
.
sequelize
.
define
(
'Group'
,
{})
,
UserGroup
=
this
.
sequelize
.
define
(
'GroupUser'
,
{},
{
tableName
:
'user_groups'
})
User
.
hasMany
(
Group
,
{
as
:
'MyGroups'
,
through
:
UserGroup
})
Group
.
hasMany
(
User
,
{
as
:
'MyUsers'
,
through
:
UserGroup
})
this
.
sequelize
.
sync
({
force
:
true
}).
success
(
function
()
{
self
.
sequelize
.
query
(
"SHOW TABLES LIKE 'user_groups'"
).
success
(
function
(
res
)
{
expect
(
res
).
to
.
deep
.
equal
([
'user_groups'
])
done
()
})
})
})
it
(
"correctly identifies its counterpart when through is a string"
,
function
(
done
)
{
var
self
=
this
,
User
=
this
.
sequelize
.
define
(
'User'
,
{})
,
Group
=
this
.
sequelize
.
define
(
'Group'
,
{})
User
.
hasMany
(
Group
,
{
as
:
'MyGroups'
,
through
:
'group_user'
})
Group
.
hasMany
(
User
,
{
as
:
'MyUsers'
,
through
:
'group_user'
})
expect
(
Group
.
associations
.
MyUsers
.
through
===
User
.
associations
.
MyGroups
.
through
);
expect
(
Group
.
associations
.
MyUsers
.
through
.
rawAttributes
.
UserId
).
to
.
exist
;
expect
(
Group
.
associations
.
MyUsers
.
through
.
rawAttributes
.
GroupId
).
to
.
exist
;
done
();
})
it
(
"correctly identifies its counterpart when through is a model"
,
function
(
done
)
{
var
self
=
this
,
User
=
this
.
sequelize
.
define
(
'User'
,
{})
,
Group
=
this
.
sequelize
.
define
(
'Group'
,
{})
,
UserGroup
=
this
.
sequelize
.
define
(
'GroupUser'
,
{},
{
tableName
:
'user_groups'
})
User
.
hasMany
(
Group
,
{
as
:
'MyGroups'
,
through
:
UserGroup
})
Group
.
hasMany
(
User
,
{
as
:
'MyUsers'
,
through
:
UserGroup
})
expect
(
Group
.
associations
.
MyUsers
.
through
===
User
.
associations
.
MyGroups
.
through
);
expect
(
Group
.
associations
.
MyUsers
.
through
.
rawAttributes
.
UserId
).
to
.
exist
;
expect
(
Group
.
associations
.
MyUsers
.
through
.
rawAttributes
.
GroupId
).
to
.
exist
;
done
();
})
})
})
})
describe
(
"Foreign key constraints"
,
function
()
{
describe
(
"Foreign key constraints"
,
function
()
{
...
...
test/support.js
View file @
a4c10f8
...
@@ -83,6 +83,7 @@ var Support = {
...
@@ -83,6 +83,7 @@ var Support = {
.
dropAllTables
()
.
dropAllTables
()
.
success
(
function
()
{
.
success
(
function
()
{
sequelize
.
daoFactoryManager
.
daos
=
[]
sequelize
.
daoFactoryManager
.
daos
=
[]
sequelize
.
associations
=
[]
callback
&&
callback
()
callback
&&
callback
()
})
})
.
error
(
function
(
err
)
{
console
.
log
(
err
)
})
.
error
(
function
(
err
)
{
console
.
log
(
err
)
})
...
...
Write
Preview
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment