masonite-framework-orm 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- masonite_framework_orm-3.0.1.dist-info/METADATA +87 -0
- masonite_framework_orm-3.0.1.dist-info/RECORD +116 -0
- masonite_framework_orm-3.0.1.dist-info/WHEEL +5 -0
- masonite_framework_orm-3.0.1.dist-info/entry_points.txt +3 -0
- masonite_framework_orm-3.0.1.dist-info/licenses/LICENSE +21 -0
- masonite_framework_orm-3.0.1.dist-info/top_level.txt +1 -0
- masoniteorm/__init__.py +1 -0
- masoniteorm/collection/Collection.py +605 -0
- masoniteorm/collection/__init__.py +1 -0
- masoniteorm/commands/CanOverrideConfig.py +16 -0
- masoniteorm/commands/CanOverrideOptionsDefault.py +22 -0
- masoniteorm/commands/Command.py +6 -0
- masoniteorm/commands/Entry.py +43 -0
- masoniteorm/commands/MakeMigrationCommand.py +57 -0
- masoniteorm/commands/MakeModelCommand.py +78 -0
- masoniteorm/commands/MakeModelDocstringCommand.py +37 -0
- masoniteorm/commands/MakeObserverCommand.py +54 -0
- masoniteorm/commands/MakeSeedCommand.py +54 -0
- masoniteorm/commands/MigrateCommand.py +46 -0
- masoniteorm/commands/MigrateFreshCommand.py +41 -0
- masoniteorm/commands/MigrateRefreshCommand.py +41 -0
- masoniteorm/commands/MigrateResetCommand.py +25 -0
- masoniteorm/commands/MigrateRollbackCommand.py +26 -0
- masoniteorm/commands/MigrateStatusCommand.py +51 -0
- masoniteorm/commands/SeedRunCommand.py +35 -0
- masoniteorm/commands/ShellCommand.py +205 -0
- masoniteorm/commands/__init__.py +18 -0
- masoniteorm/commands/stubs/create_migration.stub +20 -0
- masoniteorm/commands/stubs/create_seed.stub +9 -0
- masoniteorm/commands/stubs/model.stub +9 -0
- masoniteorm/commands/stubs/observer.stub +101 -0
- masoniteorm/commands/stubs/table_migration.stub +19 -0
- masoniteorm/config.py +123 -0
- masoniteorm/connections/BaseConnection.py +101 -0
- masoniteorm/connections/ConnectionFactory.py +59 -0
- masoniteorm/connections/ConnectionResolver.py +132 -0
- masoniteorm/connections/MSSQLConnection.py +176 -0
- masoniteorm/connections/MySQLConnection.py +232 -0
- masoniteorm/connections/PostgresConnection.py +225 -0
- masoniteorm/connections/SQLiteConnection.py +179 -0
- masoniteorm/connections/__init__.py +6 -0
- masoniteorm/exceptions.py +38 -0
- masoniteorm/expressions/__init__.py +1 -0
- masoniteorm/expressions/expressions.py +288 -0
- masoniteorm/factories/Factory.py +112 -0
- masoniteorm/factories/__init__.py +1 -0
- masoniteorm/helpers/__init__.py +0 -0
- masoniteorm/helpers/misc.py +22 -0
- masoniteorm/migrations/Migration.py +330 -0
- masoniteorm/migrations/__init__.py +1 -0
- masoniteorm/models/MigrationModel.py +9 -0
- masoniteorm/models/Model.py +1209 -0
- masoniteorm/models/Model.pyi +1366 -0
- masoniteorm/models/Pivot.py +5 -0
- masoniteorm/models/__init__.py +1 -0
- masoniteorm/observers/ObservesEvents.py +27 -0
- masoniteorm/observers/__init__.py +1 -0
- masoniteorm/pagination/BasePaginator.py +10 -0
- masoniteorm/pagination/LengthAwarePaginator.py +34 -0
- masoniteorm/pagination/SimplePaginator.py +28 -0
- masoniteorm/pagination/__init__.py +2 -0
- masoniteorm/providers/ORMProvider.py +39 -0
- masoniteorm/providers/__init__.py +1 -0
- masoniteorm/query/EagerRelation.py +42 -0
- masoniteorm/query/QueryBuilder.py +2486 -0
- masoniteorm/query/__init__.py +1 -0
- masoniteorm/query/grammars/BaseGrammar.py +1027 -0
- masoniteorm/query/grammars/MSSQLGrammar.py +194 -0
- masoniteorm/query/grammars/MySQLGrammar.py +238 -0
- masoniteorm/query/grammars/PostgresGrammar.py +213 -0
- masoniteorm/query/grammars/SQLiteGrammar.py +228 -0
- masoniteorm/query/grammars/__init__.py +4 -0
- masoniteorm/query/processors/MSSQLPostProcessor.py +58 -0
- masoniteorm/query/processors/MySQLPostProcessor.py +48 -0
- masoniteorm/query/processors/PostgresPostProcessor.py +49 -0
- masoniteorm/query/processors/SQLitePostProcessor.py +49 -0
- masoniteorm/query/processors/__init__.py +4 -0
- masoniteorm/relationships/BaseRelationship.py +161 -0
- masoniteorm/relationships/BelongsTo.py +124 -0
- masoniteorm/relationships/BelongsToMany.py +604 -0
- masoniteorm/relationships/HasMany.py +66 -0
- masoniteorm/relationships/HasManyThrough.py +269 -0
- masoniteorm/relationships/HasOne.py +111 -0
- masoniteorm/relationships/HasOneThrough.py +275 -0
- masoniteorm/relationships/MorphMany.py +152 -0
- masoniteorm/relationships/MorphOne.py +156 -0
- masoniteorm/relationships/MorphTo.py +111 -0
- masoniteorm/relationships/MorphToMany.py +108 -0
- masoniteorm/relationships/__init__.py +10 -0
- masoniteorm/schema/Blueprint.py +1161 -0
- masoniteorm/schema/Column.py +144 -0
- masoniteorm/schema/ColumnDiff.py +0 -0
- masoniteorm/schema/Constraint.py +5 -0
- masoniteorm/schema/ForeignKeyConstraint.py +28 -0
- masoniteorm/schema/Index.py +5 -0
- masoniteorm/schema/Schema.py +359 -0
- masoniteorm/schema/Table.py +94 -0
- masoniteorm/schema/TableDiff.py +86 -0
- masoniteorm/schema/__init__.py +3 -0
- masoniteorm/schema/platforms/MSSQLPlatform.py +367 -0
- masoniteorm/schema/platforms/MySQLPlatform.py +513 -0
- masoniteorm/schema/platforms/Platform.py +97 -0
- masoniteorm/schema/platforms/PostgresPlatform.py +551 -0
- masoniteorm/schema/platforms/SQLitePlatform.py +481 -0
- masoniteorm/schema/platforms/__init__.py +4 -0
- masoniteorm/scopes/BaseScope.py +6 -0
- masoniteorm/scopes/SoftDeleteScope.py +56 -0
- masoniteorm/scopes/SoftDeletesMixin.py +13 -0
- masoniteorm/scopes/TimeStampsMixin.py +12 -0
- masoniteorm/scopes/TimeStampsScope.py +47 -0
- masoniteorm/scopes/UUIDPrimaryKeyMixin.py +8 -0
- masoniteorm/scopes/UUIDPrimaryKeyScope.py +51 -0
- masoniteorm/scopes/__init__.py +8 -0
- masoniteorm/scopes/scope.py +15 -0
- masoniteorm/seeds/Seeder.py +42 -0
- masoniteorm/seeds/__init__.py +1 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from ..collection import Collection
|
|
2
|
+
from ..config import load_config
|
|
3
|
+
from .BaseRelationship import BaseRelationship
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MorphMany(BaseRelationship):
|
|
7
|
+
def __init__(self, fn, morph_key="record_type", morph_id="record_id"):
|
|
8
|
+
if isinstance(fn, str):
|
|
9
|
+
self.fn = None
|
|
10
|
+
self.morph_key = fn
|
|
11
|
+
self.morph_id = morph_key
|
|
12
|
+
else:
|
|
13
|
+
self.fn = fn
|
|
14
|
+
self.morph_id = morph_id
|
|
15
|
+
self.morph_key = morph_key
|
|
16
|
+
|
|
17
|
+
def get_builder(self):
|
|
18
|
+
return self._related_builder
|
|
19
|
+
|
|
20
|
+
def set_keys(self, owner, attribute):
|
|
21
|
+
self.morph_id = self.morph_id or "record_id"
|
|
22
|
+
self.morph_key = self.morph_key or "record_type"
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def __get__(self, instance, owner):
|
|
26
|
+
"""This method is called when the decorated method is accessed.
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
instance {object|None} -- The instance we called.
|
|
30
|
+
If we didn't call the attribute and only accessed it then this will be None.
|
|
31
|
+
|
|
32
|
+
owner {object} -- The current model that the property was accessed on.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
object -- Either returns a builder or a hydrated model.
|
|
36
|
+
"""
|
|
37
|
+
attribute = self.fn.__name__
|
|
38
|
+
self._related_builder = instance.builder
|
|
39
|
+
self.polymorphic_builder = self.fn(self)()
|
|
40
|
+
self.set_keys(owner, self.fn)
|
|
41
|
+
|
|
42
|
+
if not instance.is_loaded():
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
if attribute in instance._relationships:
|
|
46
|
+
return instance._relationships[attribute]
|
|
47
|
+
|
|
48
|
+
return self.apply_query(self._related_builder, instance)
|
|
49
|
+
|
|
50
|
+
def __getattr__(self, attribute):
|
|
51
|
+
relationship = self.fn(self)()
|
|
52
|
+
return getattr(relationship.builder, attribute)
|
|
53
|
+
|
|
54
|
+
def apply_query(self, builder, instance):
|
|
55
|
+
"""Apply the query and return a dictionary to be hydrated
|
|
56
|
+
|
|
57
|
+
Arguments:
|
|
58
|
+
builder {oject} -- The relationship object
|
|
59
|
+
instance {object} -- The current model oject.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
dict -- A dictionary of data which will be hydrated.
|
|
63
|
+
"""
|
|
64
|
+
polymorphic_key = self.get_record_key_lookup(builder._model)
|
|
65
|
+
polymorphic_builder = self.polymorphic_builder
|
|
66
|
+
return (
|
|
67
|
+
polymorphic_builder.where(self.morph_key, polymorphic_key)
|
|
68
|
+
.where(self.morph_id, instance.get_primary_key_value())
|
|
69
|
+
.get()
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def get_related(self, query, relation, eagers=None, callback=None):
|
|
73
|
+
"""Gets the relation needed between the relation and the related builder. If the relation is a collection
|
|
74
|
+
then will need to pluck out all the keys from the collection and fetch from the related builder. If
|
|
75
|
+
relation is just a Model then we can just call the model based on the value of the related
|
|
76
|
+
builders primary key.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
relation (Model|Collection):
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Model|Collection
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
if isinstance(relation, Collection):
|
|
86
|
+
record_type = self.get_record_key_lookup(relation.first())
|
|
87
|
+
if callback:
|
|
88
|
+
return callback(
|
|
89
|
+
self.polymorphic_builder.where(
|
|
90
|
+
f"{self.polymorphic_builder.get_table_name()}.{self.morph_key}",
|
|
91
|
+
record_type,
|
|
92
|
+
).where_in(
|
|
93
|
+
self.morph_id,
|
|
94
|
+
relation.pluck(
|
|
95
|
+
relation.first().get_primary_key(),
|
|
96
|
+
keep_nulls=False,
|
|
97
|
+
).unique(),
|
|
98
|
+
)
|
|
99
|
+
).get()
|
|
100
|
+
return (
|
|
101
|
+
self.polymorphic_builder.where(
|
|
102
|
+
f"{self.polymorphic_builder.get_table_name()}.{self.morph_key}",
|
|
103
|
+
record_type,
|
|
104
|
+
)
|
|
105
|
+
.where_in(
|
|
106
|
+
self.morph_id,
|
|
107
|
+
relation.pluck(
|
|
108
|
+
relation.first().get_primary_key(), keep_nulls=False
|
|
109
|
+
).unique(),
|
|
110
|
+
)
|
|
111
|
+
.get()
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
else:
|
|
115
|
+
record_type = self.get_record_key_lookup(relation)
|
|
116
|
+
|
|
117
|
+
if callback:
|
|
118
|
+
return callback(
|
|
119
|
+
self.polymorphic_builder.where(
|
|
120
|
+
self.morph_key, record_type
|
|
121
|
+
).where(self.morph_id, relation.get_primary_key_value())
|
|
122
|
+
).get()
|
|
123
|
+
return (
|
|
124
|
+
self.polymorphic_builder.where(self.morph_key, record_type)
|
|
125
|
+
.where(self.morph_id, relation.get_primary_key_value())
|
|
126
|
+
.get()
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def register_related(self, key, model, collection):
|
|
130
|
+
record_type = self.get_record_key_lookup(model)
|
|
131
|
+
related = collection.where(self.morph_key, record_type).where(
|
|
132
|
+
self.morph_id, model.get_primary_key_value()
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
model.add_relation({key: related})
|
|
136
|
+
|
|
137
|
+
def morph_map(self):
|
|
138
|
+
return load_config().DB._morph_map
|
|
139
|
+
|
|
140
|
+
def get_record_key_lookup(self, relation):
|
|
141
|
+
record_type = None
|
|
142
|
+
for record_type_loop, model in self.morph_map().items():
|
|
143
|
+
if model == relation.__class__:
|
|
144
|
+
record_type = record_type_loop
|
|
145
|
+
break
|
|
146
|
+
|
|
147
|
+
if not record_type:
|
|
148
|
+
raise ValueError(
|
|
149
|
+
f"Could not find the record type key for the {relation} class"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return record_type
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from ..collection import Collection
|
|
2
|
+
from ..config import load_config
|
|
3
|
+
from .BaseRelationship import BaseRelationship
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MorphOne(BaseRelationship):
|
|
7
|
+
def __init__(self, fn, morph_key="record_type", morph_id="record_id"):
|
|
8
|
+
if isinstance(fn, str):
|
|
9
|
+
self.fn = None
|
|
10
|
+
self.morph_key = fn
|
|
11
|
+
self.morph_id = morph_key
|
|
12
|
+
else:
|
|
13
|
+
self.fn = fn
|
|
14
|
+
self.morph_id = morph_id
|
|
15
|
+
self.morph_key = morph_key
|
|
16
|
+
|
|
17
|
+
def get_builder(self):
|
|
18
|
+
return self._related_builder
|
|
19
|
+
|
|
20
|
+
def set_keys(self, owner, attribute):
|
|
21
|
+
self.morph_id = self.morph_id or "record_id"
|
|
22
|
+
self.morph_key = self.morph_key or "record_type"
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def __get__(self, instance, owner):
|
|
26
|
+
"""This method is called when the decorated method is accessed.
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
instance {object|None} -- The instance we called.
|
|
30
|
+
If we didn't call the attribute and only accessed it then this will be None.
|
|
31
|
+
|
|
32
|
+
owner {object} -- The current model that the property was accessed on.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
object -- Either returns a builder or a hydrated model.
|
|
36
|
+
"""
|
|
37
|
+
attribute = self.fn.__name__
|
|
38
|
+
self._related_builder = instance.builder
|
|
39
|
+
self.polymorphic_builder = self.fn(self)()
|
|
40
|
+
self.set_keys(owner, self.fn)
|
|
41
|
+
|
|
42
|
+
if not instance.is_loaded():
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
if attribute in instance._relationships:
|
|
46
|
+
return instance._relationships[attribute]
|
|
47
|
+
|
|
48
|
+
return self.apply_query(self._related_builder, instance)
|
|
49
|
+
|
|
50
|
+
def __getattr__(self, attribute):
|
|
51
|
+
relationship = self.fn(self)()
|
|
52
|
+
return getattr(relationship.builder, attribute)
|
|
53
|
+
|
|
54
|
+
def apply_query(self, builder, instance):
|
|
55
|
+
"""Apply the query and return a dictionary to be hydrated
|
|
56
|
+
|
|
57
|
+
Arguments:
|
|
58
|
+
builder {oject} -- The relationship object
|
|
59
|
+
instance {object} -- The current model oject.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
dict -- A dictionary of data which will be hydrated.
|
|
63
|
+
"""
|
|
64
|
+
polymorphic_key = self.get_record_key_lookup(builder._model)
|
|
65
|
+
polymorphic_builder = self.polymorphic_builder
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
polymorphic_builder.where(self.morph_key, polymorphic_key)
|
|
69
|
+
.where(self.morph_id, instance.get_primary_key_value())
|
|
70
|
+
.first()
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def get_related(self, query, relation, eagers=None, callback=None):
|
|
74
|
+
"""Gets the relation needed between the relation and the related builder. If the relation is a collection
|
|
75
|
+
then will need to pluck out all the keys from the collection and fetch from the related builder. If
|
|
76
|
+
relation is just a Model then we can just call the model based on the value of the related
|
|
77
|
+
builders primary key.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
relation (Model|Collection):
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Model|Collection
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
if isinstance(relation, Collection):
|
|
87
|
+
record_type = self.get_record_key_lookup(relation.first())
|
|
88
|
+
if callback:
|
|
89
|
+
return callback(
|
|
90
|
+
self.polymorphic_builder.where(
|
|
91
|
+
f"{self.polymorphic_builder.get_table_name()}.{self.morph_key}",
|
|
92
|
+
record_type,
|
|
93
|
+
).where_in(
|
|
94
|
+
self.morph_id,
|
|
95
|
+
relation.pluck(
|
|
96
|
+
relation.first().get_primary_key(),
|
|
97
|
+
keep_nulls=False,
|
|
98
|
+
).unique(),
|
|
99
|
+
)
|
|
100
|
+
).get()
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
self.polymorphic_builder.where(
|
|
104
|
+
f"{self.polymorphic_builder.get_table_name()}.{self.morph_key}",
|
|
105
|
+
record_type,
|
|
106
|
+
)
|
|
107
|
+
.where_in(
|
|
108
|
+
self.morph_id,
|
|
109
|
+
relation.pluck(
|
|
110
|
+
relation.first().get_primary_key(), keep_nulls=False
|
|
111
|
+
).unique(),
|
|
112
|
+
)
|
|
113
|
+
.get()
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
else:
|
|
117
|
+
record_type = self.get_record_key_lookup(relation)
|
|
118
|
+
if callback:
|
|
119
|
+
return callback(
|
|
120
|
+
self.polymorphic_builder.where(
|
|
121
|
+
self.morph_key, record_type
|
|
122
|
+
).where(self.morph_id, relation.get_primary_key_value())
|
|
123
|
+
).first()
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
self.polymorphic_builder.where(self.morph_key, record_type)
|
|
127
|
+
.where(self.morph_id, relation.get_primary_key_value())
|
|
128
|
+
.first()
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def register_related(self, key, model, collection):
|
|
132
|
+
record_type = self.get_record_key_lookup(model)
|
|
133
|
+
related = (
|
|
134
|
+
collection.where(self.morph_key, record_type)
|
|
135
|
+
.where(self.morph_id, model.get_primary_key_value())
|
|
136
|
+
.first()
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
model.add_relation({key: related})
|
|
140
|
+
|
|
141
|
+
def morph_map(self):
|
|
142
|
+
return load_config().DB._morph_map
|
|
143
|
+
|
|
144
|
+
def get_record_key_lookup(self, relation):
|
|
145
|
+
record_type = None
|
|
146
|
+
for record_type_loop, model in self.morph_map().items():
|
|
147
|
+
if model == relation.__class__:
|
|
148
|
+
record_type = record_type_loop
|
|
149
|
+
break
|
|
150
|
+
|
|
151
|
+
if not record_type:
|
|
152
|
+
raise ValueError(
|
|
153
|
+
f"Could not find the record type key for the {relation} class"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return record_type
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from ..collection import Collection
|
|
2
|
+
from ..config import load_config
|
|
3
|
+
from .BaseRelationship import BaseRelationship
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MorphTo(BaseRelationship):
|
|
7
|
+
def __init__(self, fn, morph_key="record_type", morph_id="record_id"):
|
|
8
|
+
if isinstance(fn, str):
|
|
9
|
+
self.fn = None
|
|
10
|
+
self.morph_key = fn
|
|
11
|
+
self.morph_id = morph_key
|
|
12
|
+
else:
|
|
13
|
+
self.fn = fn
|
|
14
|
+
self.morph_id = morph_id
|
|
15
|
+
self.morph_key = morph_key
|
|
16
|
+
|
|
17
|
+
def get_builder(self):
|
|
18
|
+
return self._related_builder
|
|
19
|
+
|
|
20
|
+
def set_keys(self, owner, attribute):
|
|
21
|
+
self.morph_id = self.morph_id or "record_id"
|
|
22
|
+
self.morph_key = self.morph_key or "record_type"
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def __get__(self, instance, owner):
|
|
26
|
+
"""This method is called when the decorated method is accessed.
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
instance {object|None} -- The instance we called.
|
|
30
|
+
If we didn't call the attribute and only accessed it then this will be None.
|
|
31
|
+
|
|
32
|
+
owner {object} -- The current model that the property was accessed on.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
object -- Either returns a builder or a hydrated model.
|
|
36
|
+
"""
|
|
37
|
+
attribute = self.fn.__name__
|
|
38
|
+
self._related_builder = instance.builder
|
|
39
|
+
self.set_keys(owner, self.fn)
|
|
40
|
+
|
|
41
|
+
if not instance.is_loaded():
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
if attribute in instance._relationships:
|
|
45
|
+
return instance._relationships[attribute]
|
|
46
|
+
|
|
47
|
+
return self.apply_query(self._related_builder, instance)
|
|
48
|
+
|
|
49
|
+
def __getattr__(self, attribute):
|
|
50
|
+
relationship = self.fn(self)()
|
|
51
|
+
return getattr(relationship._related_builder, attribute)
|
|
52
|
+
|
|
53
|
+
def apply_query(self, builder, instance):
|
|
54
|
+
"""Apply the query and return a dictionary to be hydrated
|
|
55
|
+
|
|
56
|
+
Arguments:
|
|
57
|
+
builder {oject} -- The relationship object
|
|
58
|
+
instance {object} -- The current model oject.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
dict -- A dictionary of data which will be hydrated.
|
|
62
|
+
"""
|
|
63
|
+
model = self.morph_map().get(instance.__attributes__[self.morph_key])
|
|
64
|
+
record = instance.__attributes__[self.morph_id]
|
|
65
|
+
|
|
66
|
+
return model.where(model.get_primary_key(), record).first()
|
|
67
|
+
|
|
68
|
+
def get_related(self, query, relation, eagers=None, callback=None):
|
|
69
|
+
"""Gets the relation needed between the relation and the related builder. If the relation is a collection
|
|
70
|
+
then will need to pluck out all the keys from the collection and fetch from the related builder. If
|
|
71
|
+
relation is just a Model then we can just call the model based on the value of the related
|
|
72
|
+
builders primary key.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
relation (Model|Collection):
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Model|Collection
|
|
79
|
+
"""
|
|
80
|
+
if isinstance(relation, Collection):
|
|
81
|
+
relations = Collection()
|
|
82
|
+
for group, items in relation.group_by(self.morph_key).items():
|
|
83
|
+
morphed_model = self.morph_map().get(group)
|
|
84
|
+
relations.merge(
|
|
85
|
+
morphed_model.where_in(
|
|
86
|
+
f"{morphed_model.get_table_name()}.{morphed_model.get_primary_key()}",
|
|
87
|
+
Collection(items)
|
|
88
|
+
.pluck(self.morph_id, keep_nulls=False)
|
|
89
|
+
.unique(),
|
|
90
|
+
).get()
|
|
91
|
+
)
|
|
92
|
+
return relations
|
|
93
|
+
else:
|
|
94
|
+
model = self.morph_map().get(getattr(relation, self.morph_key))
|
|
95
|
+
if model:
|
|
96
|
+
return model.find(getattr(relation, self.morph_id))
|
|
97
|
+
|
|
98
|
+
def register_related(self, key, model, collection):
|
|
99
|
+
morphed_model = self.morph_map().get(getattr(model, self.morph_key))
|
|
100
|
+
|
|
101
|
+
related = collection.where(
|
|
102
|
+
morphed_model.get_primary_key(), getattr(model, self.morph_id)
|
|
103
|
+
).first()
|
|
104
|
+
|
|
105
|
+
model.add_relation({key: related})
|
|
106
|
+
|
|
107
|
+
def morph_map(self):
|
|
108
|
+
return load_config().DB._morph_map
|
|
109
|
+
|
|
110
|
+
def map_related(self, related_result):
|
|
111
|
+
return related_result
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from ..collection import Collection
|
|
2
|
+
from ..config import load_config
|
|
3
|
+
from .BaseRelationship import BaseRelationship
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MorphToMany(BaseRelationship):
|
|
7
|
+
def __init__(self, fn, morph_key="record_type", morph_id="record_id"):
|
|
8
|
+
if isinstance(fn, str):
|
|
9
|
+
self.fn = None
|
|
10
|
+
self.morph_key = fn
|
|
11
|
+
self.morph_id = morph_key
|
|
12
|
+
else:
|
|
13
|
+
self.fn = fn
|
|
14
|
+
self.morph_id = morph_id
|
|
15
|
+
self.morph_key = morph_key
|
|
16
|
+
|
|
17
|
+
def get_builder(self):
|
|
18
|
+
return self._related_builder
|
|
19
|
+
|
|
20
|
+
def set_keys(self, owner, attribute):
|
|
21
|
+
self.morph_id = self.morph_id or "record_id"
|
|
22
|
+
self.morph_key = self.morph_key or "record_type"
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def __get__(self, instance, owner):
|
|
26
|
+
"""This method is called when the decorated method is accessed.
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
instance {object|None} -- The instance we called.
|
|
30
|
+
If we didn't call the attribute and only accessed it then this will be None.
|
|
31
|
+
|
|
32
|
+
owner {object} -- The current model that the property was accessed on.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
object -- Either returns a builder or a hydrated model.
|
|
36
|
+
"""
|
|
37
|
+
attribute = self.fn.__name__
|
|
38
|
+
self._related_builder = instance.builder
|
|
39
|
+
self.set_keys(owner, self.fn)
|
|
40
|
+
|
|
41
|
+
if not instance.is_loaded():
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
if attribute in instance._relationships:
|
|
45
|
+
return instance._relationships[attribute]
|
|
46
|
+
|
|
47
|
+
return self.apply_query(self._related_builder, instance)
|
|
48
|
+
|
|
49
|
+
def __getattr__(self, attribute):
|
|
50
|
+
relationship = self.fn(self)()
|
|
51
|
+
return getattr(relationship.builder, attribute)
|
|
52
|
+
|
|
53
|
+
def apply_query(self, builder, instance):
|
|
54
|
+
"""Apply the query and return a dictionary to be hydrated
|
|
55
|
+
|
|
56
|
+
Arguments:
|
|
57
|
+
builder {oject} -- The relationship object
|
|
58
|
+
instance {object} -- The current model oject.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
dict -- A dictionary of data which will be hydrated.
|
|
62
|
+
"""
|
|
63
|
+
model = self.morph_map().get(instance.__attributes__[self.morph_key])
|
|
64
|
+
record = instance.__attributes__[self.morph_id]
|
|
65
|
+
|
|
66
|
+
return model.where(model.get_primary_key(), record).first()
|
|
67
|
+
|
|
68
|
+
def get_related(self, query, relation, eagers=None, callback=None):
|
|
69
|
+
"""Gets the relation needed between the relation and the related builder. If the relation is a collection
|
|
70
|
+
then will need to pluck out all the keys from the collection and fetch from the related builder. If
|
|
71
|
+
relation is just a Model then we can just call the model based on the value of the related
|
|
72
|
+
builders primary key.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
relation (Model|Collection):
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Model|Collection
|
|
79
|
+
"""
|
|
80
|
+
if isinstance(relation, Collection):
|
|
81
|
+
relations = Collection()
|
|
82
|
+
for group, items in relation.group_by(self.morph_key).items():
|
|
83
|
+
morphed_model = self.morph_map().get(group)
|
|
84
|
+
relations.merge(
|
|
85
|
+
morphed_model.where_in(
|
|
86
|
+
f"{morphed_model.get_table_name()}.{morphed_model.get_primary_key()}",
|
|
87
|
+
Collection(items)
|
|
88
|
+
.pluck(self.morph_id, keep_nulls=False)
|
|
89
|
+
.unique(),
|
|
90
|
+
).get()
|
|
91
|
+
)
|
|
92
|
+
return relations
|
|
93
|
+
else:
|
|
94
|
+
model = self.morph_map().get(getattr(relation, self.morph_key))
|
|
95
|
+
if model:
|
|
96
|
+
return model.find([getattr(relation, self.morph_id)])
|
|
97
|
+
|
|
98
|
+
def register_related(self, key, model, collection):
|
|
99
|
+
morphed_model = self.morph_map().get(getattr(model, self.morph_key))
|
|
100
|
+
|
|
101
|
+
related = collection.where(
|
|
102
|
+
morphed_model.get_primary_key(), getattr(model, self.morph_id)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
model.add_relation({key: related})
|
|
106
|
+
|
|
107
|
+
def morph_map(self):
|
|
108
|
+
return load_config().DB._morph_map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .BelongsTo import BelongsTo as belongs_to
|
|
2
|
+
from .BelongsToMany import BelongsToMany as belongs_to_many
|
|
3
|
+
from .HasMany import HasMany as has_many
|
|
4
|
+
from .HasManyThrough import HasManyThrough as has_many_through
|
|
5
|
+
from .HasOne import HasOne as has_one
|
|
6
|
+
from .HasOneThrough import HasOneThrough as has_one_through
|
|
7
|
+
from .MorphMany import MorphMany as morph_many
|
|
8
|
+
from .MorphOne import MorphOne as morph_one
|
|
9
|
+
from .MorphTo import MorphTo as morph_to
|
|
10
|
+
from .MorphToMany import MorphToMany as morph_to_many
|