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.
Files changed (116) hide show
  1. masonite_framework_orm-3.0.1.dist-info/METADATA +87 -0
  2. masonite_framework_orm-3.0.1.dist-info/RECORD +116 -0
  3. masonite_framework_orm-3.0.1.dist-info/WHEEL +5 -0
  4. masonite_framework_orm-3.0.1.dist-info/entry_points.txt +3 -0
  5. masonite_framework_orm-3.0.1.dist-info/licenses/LICENSE +21 -0
  6. masonite_framework_orm-3.0.1.dist-info/top_level.txt +1 -0
  7. masoniteorm/__init__.py +1 -0
  8. masoniteorm/collection/Collection.py +605 -0
  9. masoniteorm/collection/__init__.py +1 -0
  10. masoniteorm/commands/CanOverrideConfig.py +16 -0
  11. masoniteorm/commands/CanOverrideOptionsDefault.py +22 -0
  12. masoniteorm/commands/Command.py +6 -0
  13. masoniteorm/commands/Entry.py +43 -0
  14. masoniteorm/commands/MakeMigrationCommand.py +57 -0
  15. masoniteorm/commands/MakeModelCommand.py +78 -0
  16. masoniteorm/commands/MakeModelDocstringCommand.py +37 -0
  17. masoniteorm/commands/MakeObserverCommand.py +54 -0
  18. masoniteorm/commands/MakeSeedCommand.py +54 -0
  19. masoniteorm/commands/MigrateCommand.py +46 -0
  20. masoniteorm/commands/MigrateFreshCommand.py +41 -0
  21. masoniteorm/commands/MigrateRefreshCommand.py +41 -0
  22. masoniteorm/commands/MigrateResetCommand.py +25 -0
  23. masoniteorm/commands/MigrateRollbackCommand.py +26 -0
  24. masoniteorm/commands/MigrateStatusCommand.py +51 -0
  25. masoniteorm/commands/SeedRunCommand.py +35 -0
  26. masoniteorm/commands/ShellCommand.py +205 -0
  27. masoniteorm/commands/__init__.py +18 -0
  28. masoniteorm/commands/stubs/create_migration.stub +20 -0
  29. masoniteorm/commands/stubs/create_seed.stub +9 -0
  30. masoniteorm/commands/stubs/model.stub +9 -0
  31. masoniteorm/commands/stubs/observer.stub +101 -0
  32. masoniteorm/commands/stubs/table_migration.stub +19 -0
  33. masoniteorm/config.py +123 -0
  34. masoniteorm/connections/BaseConnection.py +101 -0
  35. masoniteorm/connections/ConnectionFactory.py +59 -0
  36. masoniteorm/connections/ConnectionResolver.py +132 -0
  37. masoniteorm/connections/MSSQLConnection.py +176 -0
  38. masoniteorm/connections/MySQLConnection.py +232 -0
  39. masoniteorm/connections/PostgresConnection.py +225 -0
  40. masoniteorm/connections/SQLiteConnection.py +179 -0
  41. masoniteorm/connections/__init__.py +6 -0
  42. masoniteorm/exceptions.py +38 -0
  43. masoniteorm/expressions/__init__.py +1 -0
  44. masoniteorm/expressions/expressions.py +288 -0
  45. masoniteorm/factories/Factory.py +112 -0
  46. masoniteorm/factories/__init__.py +1 -0
  47. masoniteorm/helpers/__init__.py +0 -0
  48. masoniteorm/helpers/misc.py +22 -0
  49. masoniteorm/migrations/Migration.py +330 -0
  50. masoniteorm/migrations/__init__.py +1 -0
  51. masoniteorm/models/MigrationModel.py +9 -0
  52. masoniteorm/models/Model.py +1209 -0
  53. masoniteorm/models/Model.pyi +1366 -0
  54. masoniteorm/models/Pivot.py +5 -0
  55. masoniteorm/models/__init__.py +1 -0
  56. masoniteorm/observers/ObservesEvents.py +27 -0
  57. masoniteorm/observers/__init__.py +1 -0
  58. masoniteorm/pagination/BasePaginator.py +10 -0
  59. masoniteorm/pagination/LengthAwarePaginator.py +34 -0
  60. masoniteorm/pagination/SimplePaginator.py +28 -0
  61. masoniteorm/pagination/__init__.py +2 -0
  62. masoniteorm/providers/ORMProvider.py +39 -0
  63. masoniteorm/providers/__init__.py +1 -0
  64. masoniteorm/query/EagerRelation.py +42 -0
  65. masoniteorm/query/QueryBuilder.py +2486 -0
  66. masoniteorm/query/__init__.py +1 -0
  67. masoniteorm/query/grammars/BaseGrammar.py +1027 -0
  68. masoniteorm/query/grammars/MSSQLGrammar.py +194 -0
  69. masoniteorm/query/grammars/MySQLGrammar.py +238 -0
  70. masoniteorm/query/grammars/PostgresGrammar.py +213 -0
  71. masoniteorm/query/grammars/SQLiteGrammar.py +228 -0
  72. masoniteorm/query/grammars/__init__.py +4 -0
  73. masoniteorm/query/processors/MSSQLPostProcessor.py +58 -0
  74. masoniteorm/query/processors/MySQLPostProcessor.py +48 -0
  75. masoniteorm/query/processors/PostgresPostProcessor.py +49 -0
  76. masoniteorm/query/processors/SQLitePostProcessor.py +49 -0
  77. masoniteorm/query/processors/__init__.py +4 -0
  78. masoniteorm/relationships/BaseRelationship.py +161 -0
  79. masoniteorm/relationships/BelongsTo.py +124 -0
  80. masoniteorm/relationships/BelongsToMany.py +604 -0
  81. masoniteorm/relationships/HasMany.py +66 -0
  82. masoniteorm/relationships/HasManyThrough.py +269 -0
  83. masoniteorm/relationships/HasOne.py +111 -0
  84. masoniteorm/relationships/HasOneThrough.py +275 -0
  85. masoniteorm/relationships/MorphMany.py +152 -0
  86. masoniteorm/relationships/MorphOne.py +156 -0
  87. masoniteorm/relationships/MorphTo.py +111 -0
  88. masoniteorm/relationships/MorphToMany.py +108 -0
  89. masoniteorm/relationships/__init__.py +10 -0
  90. masoniteorm/schema/Blueprint.py +1161 -0
  91. masoniteorm/schema/Column.py +144 -0
  92. masoniteorm/schema/ColumnDiff.py +0 -0
  93. masoniteorm/schema/Constraint.py +5 -0
  94. masoniteorm/schema/ForeignKeyConstraint.py +28 -0
  95. masoniteorm/schema/Index.py +5 -0
  96. masoniteorm/schema/Schema.py +359 -0
  97. masoniteorm/schema/Table.py +94 -0
  98. masoniteorm/schema/TableDiff.py +86 -0
  99. masoniteorm/schema/__init__.py +3 -0
  100. masoniteorm/schema/platforms/MSSQLPlatform.py +367 -0
  101. masoniteorm/schema/platforms/MySQLPlatform.py +513 -0
  102. masoniteorm/schema/platforms/Platform.py +97 -0
  103. masoniteorm/schema/platforms/PostgresPlatform.py +551 -0
  104. masoniteorm/schema/platforms/SQLitePlatform.py +481 -0
  105. masoniteorm/schema/platforms/__init__.py +4 -0
  106. masoniteorm/scopes/BaseScope.py +6 -0
  107. masoniteorm/scopes/SoftDeleteScope.py +56 -0
  108. masoniteorm/scopes/SoftDeletesMixin.py +13 -0
  109. masoniteorm/scopes/TimeStampsMixin.py +12 -0
  110. masoniteorm/scopes/TimeStampsScope.py +47 -0
  111. masoniteorm/scopes/UUIDPrimaryKeyMixin.py +8 -0
  112. masoniteorm/scopes/UUIDPrimaryKeyScope.py +51 -0
  113. masoniteorm/scopes/__init__.py +8 -0
  114. masoniteorm/scopes/scope.py +15 -0
  115. masoniteorm/seeds/Seeder.py +42 -0
  116. masoniteorm/seeds/__init__.py +1 -0
@@ -0,0 +1,269 @@
1
+ from ..collection import Collection
2
+ from .BaseRelationship import BaseRelationship
3
+
4
+
5
+ class HasManyThrough(BaseRelationship):
6
+ """HasManyThrough Relationship Class."""
7
+
8
+ def __init__(
9
+ self,
10
+ fn=None,
11
+ local_foreign_key=None,
12
+ other_foreign_key=None,
13
+ local_owner_key=None,
14
+ other_owner_key=None,
15
+ ):
16
+ if isinstance(fn, str):
17
+ self.fn = None
18
+ self.local_key = fn
19
+ self.foreign_key = local_foreign_key
20
+ self.local_owner_key = other_foreign_key or "id"
21
+ self.other_owner_key = local_owner_key or "id"
22
+ else:
23
+ self.fn = fn
24
+ self.local_key = local_foreign_key
25
+ self.foreign_key = other_foreign_key
26
+ self.local_owner_key = local_owner_key or "id"
27
+ self.other_owner_key = other_owner_key or "id"
28
+
29
+ def set_keys(self, distant_builder, intermediary_builder, attribute):
30
+ self.local_key = self.local_key or "id"
31
+ self.foreign_key = self.foreign_key or f"{attribute}_id"
32
+ self.local_owner_key = self.local_owner_key or "id"
33
+ self.other_owner_key = self.other_owner_key or "id"
34
+ return self
35
+
36
+ def __get__(self, instance, owner):
37
+ """This method is called when the decorated method is accessed.
38
+
39
+ Arguments:
40
+ instance {object|None} -- The instance we called.
41
+ If we didn't call the attribute and only accessed it then this will be None.
42
+
43
+ owner {object} -- The current model that the property was accessed on.
44
+
45
+ Returns:
46
+ object -- Either returns a builder or a hydrated model.
47
+ """
48
+ attribute = self.fn.__name__
49
+ self.attribute = attribute
50
+ relationship1 = self.fn(self)[0]()
51
+ relationship2 = self.fn(self)[1]()
52
+ self.distant_builder = relationship1.builder
53
+ self.intermediary_builder = relationship2.builder
54
+ self.set_keys(
55
+ self.distant_builder, self.intermediary_builder, attribute
56
+ )
57
+
58
+ if not instance.is_loaded():
59
+ return self
60
+
61
+ if attribute in instance._relationships:
62
+ return instance._relationships[attribute]
63
+
64
+ return self.apply_related_query(
65
+ self.distant_builder, self.intermediary_builder, instance
66
+ )
67
+
68
+ def apply_related_query(
69
+ self, distant_builder, intermediary_builder, owner
70
+ ):
71
+ """
72
+ Apply the query to return a Collection of data for the distant models to be hydrated with.
73
+
74
+ Method is used when accessing a relationship on a model if its not
75
+ already eager loaded
76
+
77
+ Arguments
78
+ distant_builder (QueryBuilder): QueryBuilder attached to the distant table
79
+ intermediate_builder (QueryBuilder): QueryBuilder attached to the intermediate (linking) table
80
+ owner (Any): the model this relationship is starting from
81
+
82
+ Returns
83
+ Collection: Collection of dicts which will be used for hydrating models.
84
+ """
85
+
86
+ distant_table = distant_builder.get_table_name()
87
+ intermediate_table = intermediary_builder.get_table_name()
88
+
89
+ return (
90
+ self.distant_builder.select(
91
+ f"{distant_table}.*, {intermediate_table}.{self.local_key}"
92
+ )
93
+ .join(
94
+ f"{intermediate_table}",
95
+ f"{intermediate_table}.{self.foreign_key}",
96
+ "=",
97
+ f"{distant_table}.{self.other_owner_key}",
98
+ )
99
+ .where(
100
+ f"{intermediate_table}.{self.local_key}",
101
+ getattr(owner, self.local_owner_key),
102
+ )
103
+ .get()
104
+ )
105
+
106
+ def relate(self, related_model):
107
+ return self.distant_builder.join(
108
+ f"{self.intermediary_builder.get_table_name()}",
109
+ f"{self.intermediary_builder.get_table_name()}.{self.foreign_key}",
110
+ "=",
111
+ f"{self.distant_builder.get_table_name()}.{self.other_owner_key}",
112
+ ).where(
113
+ f"{self.intermediary_builder.get_table_name()}.{self.local_key}",
114
+ getattr(related_model, self.local_owner_key),
115
+ )
116
+
117
+ def get_builder(self):
118
+ return self.distant_builder
119
+
120
+ def make_builder(self, eagers=None):
121
+ builder = self.get_builder().with_(eagers)
122
+
123
+ return builder
124
+
125
+ def register_related(self, key, model, collection):
126
+ """
127
+ Attach the related model to source models attribute
128
+
129
+ Arguments
130
+ key (str): The attribute name
131
+ model (Any): The model instance
132
+ collection (Collection): The data for the related models
133
+
134
+ Returns
135
+ None
136
+ """
137
+ related = collection.get(getattr(model, self.local_owner_key), None)
138
+ if related and not isinstance(related, Collection):
139
+ related = Collection(related)
140
+
141
+ model.add_relation({key: related if related else None})
142
+
143
+ def get_related(
144
+ self, current_builder, relation, eagers=None, callback=None
145
+ ):
146
+ """
147
+ Get a Collection to hydrate the models for the distant table with
148
+ Used when eager loading the model attribute
149
+
150
+ Arguments
151
+ current_builder (QueryBuilder): The source models QueryBuilder object
152
+ relation (HasManyThrough): this relationship object
153
+ eagers (Any):
154
+ callback (Any):
155
+
156
+ Returns
157
+ Collection the collection of dicts to hydrate the distant models with
158
+ """
159
+
160
+ distant_table = self.distant_builder.get_table_name()
161
+ intermediate_table = self.intermediary_builder.get_table_name()
162
+
163
+ if callback:
164
+ callback(current_builder)
165
+
166
+ (
167
+ self.distant_builder.select(
168
+ f"{distant_table}.*, {intermediate_table}.{self.local_key}"
169
+ ).join(
170
+ f"{intermediate_table}",
171
+ f"{intermediate_table}.{self.foreign_key}",
172
+ "=",
173
+ f"{distant_table}.{self.other_owner_key}",
174
+ )
175
+ )
176
+
177
+ if isinstance(relation, Collection):
178
+ return self.distant_builder.where_in(
179
+ f"{intermediate_table}.{self.local_key}",
180
+ Collection(relation._get_value(self.local_owner_key)).unique(),
181
+ ).get()
182
+ else:
183
+ return self.distant_builder.where(
184
+ f"{intermediate_table}.{self.local_key}",
185
+ getattr(relation, self.local_owner_key),
186
+ ).get()
187
+
188
+ def query_has(self, current_builder, method="where_exists"):
189
+ distant_table = self.distant_builder.get_table_name()
190
+ intermediate_table = self.intermediary_builder.get_table_name()
191
+
192
+ getattr(current_builder, method)(
193
+ self.distant_builder.join(
194
+ f"{intermediate_table}",
195
+ f"{intermediate_table}.{self.foreign_key}",
196
+ "=",
197
+ f"{distant_table}.{self.other_owner_key}",
198
+ ).where_column(
199
+ f"{intermediate_table}.{self.local_key}",
200
+ f"{current_builder.get_table_name()}.{self.local_owner_key}",
201
+ )
202
+ )
203
+
204
+ return self.distant_builder
205
+
206
+ def query_where_exists(
207
+ self, current_builder, callback, method="where_exists"
208
+ ):
209
+ distant_table = self.distant_builder.get_table_name()
210
+ intermediate_table = self.intermediary_builder.get_table_name()
211
+
212
+ getattr(current_builder, method)(
213
+ self.distant_builder.join(
214
+ f"{intermediate_table}",
215
+ f"{intermediate_table}.{self.foreign_key}",
216
+ "=",
217
+ f"{distant_table}.{self.other_owner_key}",
218
+ )
219
+ .where_column(
220
+ f"{intermediate_table}.{self.local_key}",
221
+ f"{current_builder.get_table_name()}.{self.local_owner_key}",
222
+ )
223
+ .when(callback, lambda q: (callback(q)))
224
+ )
225
+
226
+ def get_with_count_query(self, current_builder, callback):
227
+ distant_table = self.distant_builder.get_table_name()
228
+ intermediate_table = self.intermediary_builder.get_table_name()
229
+
230
+ if not current_builder._columns:
231
+ current_builder.select("*")
232
+
233
+ return_query = current_builder.add_select(
234
+ f"{self.attribute}_count",
235
+ lambda q: (
236
+ (
237
+ q.count("*")
238
+ .join(
239
+ f"{intermediate_table}",
240
+ f"{intermediate_table}.{self.foreign_key}",
241
+ "=",
242
+ f"{distant_table}.{self.other_owner_key}",
243
+ )
244
+ .where_column(
245
+ f"{intermediate_table}.{self.local_key}",
246
+ f"{current_builder.get_table_name()}.{self.local_owner_key}",
247
+ )
248
+ .table(distant_table)
249
+ .when(
250
+ callback,
251
+ lambda q: (
252
+ q.where_in(
253
+ self.foreign_key,
254
+ callback(
255
+ self.distant_builder.select(
256
+ self.other_owner_key
257
+ )
258
+ ),
259
+ )
260
+ ),
261
+ )
262
+ )
263
+ ),
264
+ )
265
+
266
+ return return_query
267
+
268
+ def map_related(self, related_result):
269
+ return related_result.group_by(self.local_key)
@@ -0,0 +1,111 @@
1
+ from ..collection import Collection
2
+ from .BaseRelationship import BaseRelationship
3
+
4
+
5
+ class HasOne(BaseRelationship):
6
+ """Belongs To Relationship Class."""
7
+
8
+ def __init__(self, fn, foreign_key=None, local_key=None):
9
+ if isinstance(fn, str):
10
+ self.foreign_key = fn
11
+ self.local_key = foreign_key or "id"
12
+ else:
13
+ self.fn = fn
14
+ self.local_key = local_key or "id"
15
+ self.foreign_key = foreign_key
16
+
17
+ def set_keys(self, owner, attribute):
18
+ self.local_key = self.local_key or "id"
19
+ self.foreign_key = self.foreign_key or f"{attribute}_id"
20
+ return self
21
+
22
+ def apply_query(self, foreign, owner):
23
+ """Apply the query and return a dictionary to be hydrated
24
+
25
+ Arguments:
26
+ foreign {oject} -- The relationship object
27
+ owner {object} -- The current model oject.
28
+
29
+ Returns:
30
+ dict -- A dictionary of data which will be hydrated.
31
+ """
32
+
33
+ return foreign.where(
34
+ self.foreign_key, owner.__attributes__[self.local_key]
35
+ ).first()
36
+
37
+ def get_related(self, query, relation, eagers=(), callback=None):
38
+ """Gets the relation needed between the relation and the related builder. If the relation is a collection
39
+ then will need to pluck out all the keys from the collection and fetch from the related builder. If
40
+ relation is just a Model then we can just call the model based on the value of the related
41
+ builders primary key.
42
+
43
+ Args:
44
+ relation (Model|Collection):
45
+
46
+ Returns:
47
+ Model|Collection
48
+ """
49
+ builder = self.get_builder().with_(eagers)
50
+
51
+ if callback:
52
+ callback(builder)
53
+
54
+ if isinstance(relation, Collection):
55
+ return builder.where_in(
56
+ f"{builder.get_table_name()}.{self.foreign_key}",
57
+ Collection(relation._get_value(self.local_key)).unique(),
58
+ ).get()
59
+ else:
60
+ return builder.where(
61
+ f"{builder.get_table_name()}.{self.foreign_key}",
62
+ getattr(relation, self.local_key),
63
+ ).first()
64
+
65
+ def query_has(self, current_query_builder, method="where_exists"):
66
+ related_builder = self.get_builder()
67
+
68
+ getattr(current_query_builder, method)(
69
+ related_builder.where_column(
70
+ f"{related_builder.get_table_name()}.{self.foreign_key}",
71
+ f"{current_query_builder.get_table_name()}.{self.local_key}",
72
+ )
73
+ )
74
+
75
+ return related_builder
76
+
77
+ def query_where_exists(self, builder, callback, method="where_exists"):
78
+ query = self.get_builder()
79
+ getattr(builder, method)(
80
+ callback(
81
+ query.where_column(
82
+ f"{query.get_table_name()}.{self.foreign_key}",
83
+ f"{builder.get_table_name()}.{self.local_key}",
84
+ )
85
+ )
86
+ )
87
+ return query
88
+
89
+ def register_related(self, key, model, collection):
90
+ related = collection.where(
91
+ self.foreign_key, getattr(model, self.local_key)
92
+ ).first()
93
+
94
+ model.add_relation({key: related or None})
95
+
96
+ def map_related(self, related_result):
97
+ return related_result
98
+
99
+ def attach(self, current_model, related_record):
100
+ local_key_value = getattr(current_model, self.local_key)
101
+ if not related_record.is_created():
102
+ related_record.fill({self.foreign_key: local_key_value})
103
+ return related_record.create(
104
+ related_record.all_attributes(), cast=True
105
+ )
106
+
107
+ related_record.update({self.foreign_key: local_key_value})
108
+ return related_record
109
+
110
+ def detach(self, current_model, related_record):
111
+ return related_record.update({self.foreign_key: None})
@@ -0,0 +1,275 @@
1
+ from ..collection import Collection
2
+ from .BaseRelationship import BaseRelationship
3
+
4
+
5
+ class HasOneThrough(BaseRelationship):
6
+ """HasOneThrough Relationship Class."""
7
+
8
+ def __init__(
9
+ self,
10
+ fn=None,
11
+ local_foreign_key=None,
12
+ other_foreign_key=None,
13
+ local_owner_key=None,
14
+ other_owner_key=None,
15
+ ):
16
+ if isinstance(fn, str):
17
+ self.fn = None
18
+ self.local_key = fn
19
+ self.foreign_key = local_foreign_key
20
+ self.local_owner_key = other_foreign_key or "id"
21
+ self.other_owner_key = local_owner_key or "id"
22
+ else:
23
+ self.fn = fn
24
+ self.local_key = local_foreign_key
25
+ self.foreign_key = other_foreign_key
26
+ self.local_owner_key = local_owner_key or "id"
27
+ self.other_owner_key = other_owner_key or "id"
28
+
29
+ def __getattr__(self, attribute):
30
+ relationship = self.fn(self)[1]()
31
+ return getattr(relationship.builder, attribute)
32
+
33
+ def set_keys(self, distant_builder, intermediary_builder, attribute):
34
+ self.local_key = self.local_key or "id"
35
+ self.foreign_key = self.foreign_key or f"{attribute}_id"
36
+ self.local_owner_key = self.local_owner_key or "id"
37
+ self.other_owner_key = self.other_owner_key or "id"
38
+ return self
39
+
40
+ def __get__(self, instance, owner):
41
+ """
42
+ This method is called when the decorated method is accessed.
43
+
44
+ Arguments
45
+ instance (object|None): The instance we called.
46
+ If we didn't call the attribute and only accessed it then this will be None.
47
+ owner (object): The current model that the property was accessed on.
48
+
49
+ Returns
50
+ QueryBuilder|Model: Either returns a builder or a hydrated model.
51
+ """
52
+
53
+ attribute = self.fn.__name__
54
+ self.attribute = attribute
55
+ relationship1 = self.fn(self)[0]()
56
+ relationship2 = self.fn(self)[1]()
57
+ self.distant_builder = relationship1.builder
58
+ self.intermediary_builder = relationship2.builder
59
+ self.set_keys(
60
+ self.distant_builder, self.intermediary_builder, attribute
61
+ )
62
+
63
+ if instance.is_loaded():
64
+ if attribute in instance._relationships:
65
+ return instance._relationships[attribute]
66
+
67
+ return self.apply_relation_query(
68
+ self.distant_builder, self.intermediary_builder, instance
69
+ )
70
+ else:
71
+ return self
72
+
73
+ def apply_relation_query(
74
+ self, distant_builder, intermediary_builder, owner
75
+ ):
76
+ """
77
+ Apply the query and return a dict of data for the distant model to be hydrated with.
78
+
79
+ Method is used when accessing a relationship on a model if its not
80
+ already eager loaded
81
+
82
+ Arguments
83
+ distant_builder (QueryBuilder): QueryBuilder attached to the distant table
84
+ intermediate_builder (QueryBuilder): QueryBuilder attached to the intermediate (linking) table
85
+ owner (Any): the model this relationship is starting from
86
+
87
+ Returns
88
+ dict: A dictionary of data which will be hydrated.
89
+ """
90
+
91
+ dist_table = distant_builder.get_table_name()
92
+ int_table = intermediary_builder.get_table_name()
93
+
94
+ return (
95
+ distant_builder.select(
96
+ f"{dist_table}.*, {int_table}.{self.local_owner_key} as {self.local_key}"
97
+ )
98
+ .join(
99
+ f"{int_table}",
100
+ f"{int_table}.{self.foreign_key}",
101
+ "=",
102
+ f"{dist_table}.{self.other_owner_key}",
103
+ )
104
+ .where(
105
+ f"{int_table}.{self.local_owner_key}",
106
+ getattr(owner, self.local_key),
107
+ )
108
+ .first()
109
+ )
110
+
111
+ def relate(self, related_model):
112
+ dist_table = self.distant_builder.get_table_name()
113
+ int_table = self.intermediary_builder.get_table_name()
114
+
115
+ return self.distant_builder.join(
116
+ f"{int_table}",
117
+ f"{int_table}.{self.foreign_key}",
118
+ "=",
119
+ f"{dist_table}.{self.other_owner_key}",
120
+ ).where_column(
121
+ f"{int_table}.{self.local_owner_key}",
122
+ getattr(related_model, self.local_key),
123
+ )
124
+
125
+ def get_builder(self):
126
+ return self.distant_builder
127
+
128
+ def make_builder(self, eagers=None):
129
+ builder = self.get_builder().with_(eagers)
130
+
131
+ return builder
132
+
133
+ def register_related(self, key, model, collection):
134
+ """
135
+ Attach the related model to source models attribute
136
+
137
+ Arguments
138
+ key (str): The attribute name
139
+ model (Any): The model instance
140
+ collection (Collection): The data for the related models
141
+
142
+ Returns
143
+ None
144
+ """
145
+
146
+ related = collection.get(getattr(model, self.local_key), None)
147
+ model.add_relation({key: related[0] if related else None})
148
+
149
+ def get_related(
150
+ self, current_builder, relation, eagers=None, callback=None
151
+ ):
152
+ """
153
+ Get the data to hydrate the model for the distant table with
154
+ Used when eager loading the model attribute
155
+
156
+ Arguments
157
+ query (QueryBuilder): The source models QueryBuilder object
158
+ relation (HasOneThrough): this relationship object
159
+ eagers (Any):
160
+ callback (Any):
161
+
162
+ Returns
163
+ dict: the dict to hydrate the distant model with
164
+ """
165
+
166
+ dist_table = self.distant_builder.get_table_name()
167
+ int_table = self.intermediary_builder.get_table_name()
168
+
169
+ if callback:
170
+ callback(current_builder)
171
+
172
+ (
173
+ self.distant_builder.select(
174
+ f"{dist_table}.*, {int_table}.{self.local_owner_key} as {self.local_key}"
175
+ ).join(
176
+ f"{int_table}",
177
+ f"{int_table}.{self.foreign_key}",
178
+ "=",
179
+ f"{dist_table}.{self.other_owner_key}",
180
+ )
181
+ )
182
+
183
+ if isinstance(relation, Collection):
184
+ return self.distant_builder.where_in(
185
+ f"{int_table}.{self.local_owner_key}",
186
+ Collection(relation._get_value(self.local_key)).unique(),
187
+ ).get()
188
+ else:
189
+ return self.distant_builder.where(
190
+ f"{int_table}.{self.local_owner_key}",
191
+ getattr(relation, self.local_key),
192
+ ).first()
193
+
194
+ def query_has(self, current_builder, method="where_exists"):
195
+ dist_table = self.distant_builder.get_table_name()
196
+ int_table = self.intermediary_builder.get_table_name()
197
+
198
+ getattr(current_builder, method)(
199
+ self.distant_builder.join(
200
+ f"{int_table}",
201
+ f"{int_table}.{self.foreign_key}",
202
+ "=",
203
+ f"{dist_table}.{self.other_owner_key}",
204
+ ).where_column(
205
+ f"{int_table}.{self.local_owner_key}",
206
+ f"{current_builder.get_table_name()}.{self.local_key}",
207
+ )
208
+ )
209
+
210
+ return self.distant_builder
211
+
212
+ def query_where_exists(
213
+ self, current_builder, callback, method="where_exists"
214
+ ):
215
+ dist_table = self.distant_builder.get_table_name()
216
+ int_table = self.intermediary_builder.get_table_name()
217
+
218
+ getattr(current_builder, method)(
219
+ self.distant_builder.join(
220
+ f"{int_table}",
221
+ f"{int_table}.{self.foreign_key}",
222
+ "=",
223
+ f"{dist_table}.{self.other_owner_key}",
224
+ )
225
+ .where_column(
226
+ f"{int_table}.{self.local_owner_key}",
227
+ f"{current_builder.get_table_name()}.{self.local_key}",
228
+ )
229
+ .when(callback, lambda q: (callback(q)))
230
+ )
231
+
232
+ def get_with_count_query(self, current_builder, callback):
233
+ dist_table = self.distant_builder.get_table_name()
234
+ int_table = self.intermediary_builder.get_table_name()
235
+
236
+ if not current_builder._columns:
237
+ current_builder.select("*")
238
+
239
+ return_query = current_builder.add_select(
240
+ f"{self.attribute}_count",
241
+ lambda q: (
242
+ (
243
+ q.count("*")
244
+ .join(
245
+ f"{int_table}",
246
+ f"{int_table}.{self.foreign_key}",
247
+ "=",
248
+ f"{dist_table}.{self.other_owner_key}",
249
+ )
250
+ .where_column(
251
+ f"{int_table}.{self.local_owner_key}",
252
+ f"{current_builder.get_table_name()}.{self.local_key}",
253
+ )
254
+ .table(dist_table)
255
+ .when(
256
+ callback,
257
+ lambda q: (
258
+ q.where_in(
259
+ self.foreign_key,
260
+ callback(
261
+ self.distant_builder.select(
262
+ self.other_owner_key
263
+ )
264
+ ),
265
+ )
266
+ ),
267
+ )
268
+ )
269
+ ),
270
+ )
271
+
272
+ return return_query
273
+
274
+ def map_related(self, related_result):
275
+ return related_result.group_by(self.local_key)