relations-sqlite3 0.6.7__tar.gz → 0.6.9__tar.gz
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.
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/PKG-INFO +3 -1
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/lib/relations_sqlite3.egg-info/PKG-INFO +3 -1
- relations_sqlite3-0.6.9/lib/relations_sqlite3.egg-info/requires.txt +2 -0
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/lib/relations_sqlite3.py +94 -13
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/setup.py +3 -3
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/test/test_relations_sqlite3.py +236 -0
- relations-sqlite3-0.6.7/lib/relations_sqlite3.egg-info/requires.txt +0 -2
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/LICENSE.txt +0 -0
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/README.md +0 -0
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/lib/relations_sqlite3.egg-info/SOURCES.txt +0 -0
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/lib/relations_sqlite3.egg-info/dependency_links.txt +0 -0
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/lib/relations_sqlite3.egg-info/top_level.txt +0 -0
- {relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: relations-sqlite3
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.9
|
|
4
4
|
Summary: DB Modeling for SQLite using the sqlite3 library
|
|
5
5
|
Home-page: https://github.com/relations-dil/python-relations-sqlite3
|
|
6
6
|
Author: Gaffer Fitch
|
|
@@ -9,6 +9,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE.txt
|
|
12
|
+
Requires-Dist: relations-dil>=0.6.13
|
|
13
|
+
Requires-Dist: relations-sqlite>=0.6.3
|
|
12
14
|
|
|
13
15
|
# relations-sqlite3
|
|
14
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: relations-sqlite3
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.9
|
|
4
4
|
Summary: DB Modeling for SQLite using the sqlite3 library
|
|
5
5
|
Home-page: https://github.com/relations-dil/python-relations-sqlite3
|
|
6
6
|
Author: Gaffer Fitch
|
|
@@ -9,6 +9,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE.txt
|
|
12
|
+
Requires-Dist: relations-dil>=0.6.13
|
|
13
|
+
Requires-Dist: relations-sqlite>=0.6.3
|
|
12
14
|
|
|
13
15
|
# relations-sqlite3
|
|
14
16
|
|
|
@@ -14,7 +14,7 @@ import relations
|
|
|
14
14
|
import relations_sql
|
|
15
15
|
import relations_sqlite
|
|
16
16
|
|
|
17
|
-
class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
17
|
+
class Source(relations_sql.SOURCE, relations.Source): # pylint: disable=too-many-public-methods
|
|
18
18
|
"""
|
|
19
19
|
sqlite3 Source
|
|
20
20
|
"""
|
|
@@ -29,7 +29,9 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
29
29
|
OP = relations_sqlite.OP
|
|
30
30
|
|
|
31
31
|
AS = relations_sqlite.AS
|
|
32
|
+
OPTIONS = relations_sqlite.OPTIONS
|
|
32
33
|
FIELDS = relations_sqlite.FIELDS
|
|
34
|
+
COLUMN_NAME = relations_sqlite.COLUMN_NAME
|
|
33
35
|
TABLE = relations_sqlite.TABLE
|
|
34
36
|
TABLE_NAME = relations_sqlite.TABLE_NAME
|
|
35
37
|
|
|
@@ -128,7 +130,7 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
128
130
|
Get query for what's being inserted
|
|
129
131
|
"""
|
|
130
132
|
|
|
131
|
-
fields = [field.store for field in model._fields._order if not field.auto and not field.inject]
|
|
133
|
+
fields = [field.store for field in model._fields._order if not field.auto and not field.inject and field.store]
|
|
132
134
|
query = self.INSERT(self.TABLE_NAME(model.STORE, schema=model.SCHEMA), *fields)
|
|
133
135
|
|
|
134
136
|
if not model._bulk and model._id is not None and model._fields._names[model._id].auto:
|
|
@@ -157,6 +159,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
157
159
|
Executes the create
|
|
158
160
|
"""
|
|
159
161
|
|
|
162
|
+
super().create(model)
|
|
163
|
+
|
|
160
164
|
cursor = self.connection.cursor()
|
|
161
165
|
|
|
162
166
|
if not model._bulk and model._id is not None and model._fields._names[model._id].auto:
|
|
@@ -173,6 +177,10 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
173
177
|
if not model._bulk:
|
|
174
178
|
|
|
175
179
|
for creating in model._each("create"):
|
|
180
|
+
|
|
181
|
+
if model._id:
|
|
182
|
+
self.create_ties(creating)
|
|
183
|
+
|
|
176
184
|
for parent_child in creating.CHILDREN:
|
|
177
185
|
if creating._children.get(parent_child):
|
|
178
186
|
creating._children[parent_child].create()
|
|
@@ -217,10 +225,10 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
217
225
|
parent = False
|
|
218
226
|
|
|
219
227
|
for relation in model.PARENTS.values():
|
|
220
|
-
if field.name == relation.
|
|
228
|
+
if field.name == relation.child_parent_ref:
|
|
221
229
|
parent = relation.Parent.many(like=model._like).limit(model._chunk)
|
|
222
|
-
if parent[relation.
|
|
223
|
-
titles(self.IN(field.store, parent[relation.
|
|
230
|
+
if parent[relation.parent_id]:
|
|
231
|
+
titles(self.IN(field.store, parent[relation.parent_id]))
|
|
224
232
|
model.overflow = model.overflow or parent.overflow
|
|
225
233
|
else:
|
|
226
234
|
parent = True
|
|
@@ -268,9 +276,15 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
268
276
|
query = self.SELECT(self.AS("total", self.SQL("COUNT(*)"))).FROM(self.TABLE_NAME(model.STORE, schema=model.SCHEMA))
|
|
269
277
|
|
|
270
278
|
model._collate()
|
|
279
|
+
self.collate_ties_query(model, query)
|
|
271
280
|
self.retrieve_record(model._record, query)
|
|
272
281
|
self.like(model, query)
|
|
273
282
|
|
|
283
|
+
# a sibling-attribute flat join repeats a model tied to several matching siblings, so
|
|
284
|
+
# count the distinct model id instead of the joined rows
|
|
285
|
+
if getattr(model, "_distinct", False):
|
|
286
|
+
query.FIELDS = self.FIELDS(self.AS("total", self.SQL(f"COUNT(DISTINCT {model.STORE}.{model._id})")))
|
|
287
|
+
|
|
274
288
|
return query
|
|
275
289
|
|
|
276
290
|
def retrieve_query(self, model):
|
|
@@ -280,7 +294,12 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
280
294
|
|
|
281
295
|
query = self.count_query(model)
|
|
282
296
|
|
|
283
|
-
|
|
297
|
+
# honor the flat-join DISTINCT marker: dedupe the model rows the join multiplied
|
|
298
|
+
if getattr(model, "_distinct", False):
|
|
299
|
+
query.OPTIONS = self.OPTIONS("DISTINCT")
|
|
300
|
+
query.FIELDS = self.FIELDS(self.COLUMN_NAME("*", table=model.STORE))
|
|
301
|
+
else:
|
|
302
|
+
query.FIELDS = self.FIELDS("*")
|
|
284
303
|
|
|
285
304
|
self.sort(model, query)
|
|
286
305
|
self.limit(model, query)
|
|
@@ -299,6 +318,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
299
318
|
Executes the count
|
|
300
319
|
"""
|
|
301
320
|
|
|
321
|
+
super().count(model)
|
|
322
|
+
|
|
302
323
|
cursor = self.connection.cursor()
|
|
303
324
|
|
|
304
325
|
if query is None:
|
|
@@ -330,6 +351,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
330
351
|
Executes the retrieve
|
|
331
352
|
"""
|
|
332
353
|
|
|
354
|
+
super().retrieve(model)
|
|
355
|
+
|
|
333
356
|
cursor = self.connection.cursor()
|
|
334
357
|
|
|
335
358
|
if query is None:
|
|
@@ -368,6 +391,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
368
391
|
|
|
369
392
|
model._action = "update"
|
|
370
393
|
|
|
394
|
+
self.retrieve_ties(model)
|
|
395
|
+
|
|
371
396
|
cursor.close()
|
|
372
397
|
|
|
373
398
|
return model
|
|
@@ -428,6 +453,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
428
453
|
Executes the update
|
|
429
454
|
"""
|
|
430
455
|
|
|
456
|
+
super().update(model)
|
|
457
|
+
|
|
431
458
|
cursor = self.connection.cursor()
|
|
432
459
|
|
|
433
460
|
updated = 0
|
|
@@ -438,9 +465,29 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
438
465
|
|
|
439
466
|
update_query = query or self.update_query(model)
|
|
440
467
|
|
|
441
|
-
update_query.
|
|
442
|
-
|
|
443
|
-
|
|
468
|
+
if update_query.SET.expressions:
|
|
469
|
+
|
|
470
|
+
update_query.generate()
|
|
471
|
+
cursor.execute(update_query.sql, update_query.args)
|
|
472
|
+
updated += cursor.rowcount
|
|
473
|
+
|
|
474
|
+
ties = model._record.tie({})
|
|
475
|
+
|
|
476
|
+
if ties:
|
|
477
|
+
|
|
478
|
+
store_id = model._fields._names[model._id].store
|
|
479
|
+
|
|
480
|
+
id_query = self.SELECT(store_id, WHERE=update_query.WHERE).FROM(self.TABLE_NAME(model.STORE, schema=model.SCHEMA))
|
|
481
|
+
|
|
482
|
+
id_query.generate()
|
|
483
|
+
|
|
484
|
+
cursor.execute(id_query.sql, id_query.args)
|
|
485
|
+
ids = [row[store_id] for row in cursor.fetchall()]
|
|
486
|
+
|
|
487
|
+
self.delete_ties(model, ids)
|
|
488
|
+
self.create_ties(model, ties, ids)
|
|
489
|
+
|
|
490
|
+
updated = len(ids)
|
|
444
491
|
|
|
445
492
|
elif model._id:
|
|
446
493
|
|
|
@@ -448,17 +495,21 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
448
495
|
|
|
449
496
|
update_query = query or self.update_query(updating)
|
|
450
497
|
|
|
451
|
-
if update_query.SET:
|
|
498
|
+
if update_query.SET.expressions:
|
|
452
499
|
|
|
453
500
|
update_query.generate()
|
|
454
501
|
cursor.execute(update_query.sql, update_query.args)
|
|
502
|
+
updated += cursor.rowcount
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
self.delete_ties(updating)
|
|
507
|
+
self.create_ties(updating)
|
|
455
508
|
|
|
456
509
|
for parent_child in updating.CHILDREN:
|
|
457
510
|
if updating._children.get(parent_child):
|
|
458
511
|
updating._children[parent_child].create().update()
|
|
459
512
|
|
|
460
|
-
updated += cursor.rowcount
|
|
461
|
-
|
|
462
513
|
else:
|
|
463
514
|
|
|
464
515
|
raise relations.ModelError(model, "nothing to update from")
|
|
@@ -482,6 +533,7 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
482
533
|
store = model._fields._names[model._id].store
|
|
483
534
|
for deleting in model._each():
|
|
484
535
|
ids.append(deleting[model._id])
|
|
536
|
+
|
|
485
537
|
query.WHERE(**{f"{store}__in": ids})
|
|
486
538
|
|
|
487
539
|
else:
|
|
@@ -495,9 +547,38 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
|
|
|
495
547
|
Executes the delete
|
|
496
548
|
"""
|
|
497
549
|
|
|
550
|
+
super().delete(model)
|
|
551
|
+
|
|
498
552
|
cursor = self.connection.cursor()
|
|
499
553
|
|
|
500
|
-
|
|
554
|
+
if model._action == "retrieve":
|
|
555
|
+
|
|
556
|
+
delete_query = query or self.delete_query(model)
|
|
557
|
+
|
|
558
|
+
if model._id:
|
|
559
|
+
|
|
560
|
+
store_id = model._fields._names[model._id].store
|
|
561
|
+
|
|
562
|
+
id_query = self.SELECT(store_id, WHERE=delete_query.WHERE).FROM(self.TABLE_NAME(model.STORE, schema=model.SCHEMA))
|
|
563
|
+
|
|
564
|
+
id_query.generate()
|
|
565
|
+
|
|
566
|
+
cursor.execute(id_query.sql, id_query.args)
|
|
567
|
+
ids = [row[store_id] for row in cursor.fetchall()]
|
|
568
|
+
|
|
569
|
+
self.delete_ties(model, ids)
|
|
570
|
+
|
|
571
|
+
elif model._id:
|
|
572
|
+
|
|
573
|
+
delete_query = self.delete_query(model)
|
|
574
|
+
|
|
575
|
+
for deleting in model._each():
|
|
576
|
+
if self.has_ties(deleting):
|
|
577
|
+
self.delete_ties(deleting)
|
|
578
|
+
|
|
579
|
+
else:
|
|
580
|
+
|
|
581
|
+
raise relations.ModelError(model, "nothing to delete from")
|
|
501
582
|
|
|
502
583
|
delete_query.generate()
|
|
503
584
|
cursor.execute(delete_query.sql, tuple(delete_query.args))
|
|
@@ -7,14 +7,14 @@ with open("README.md", "r") as readme_file:
|
|
|
7
7
|
|
|
8
8
|
setup(
|
|
9
9
|
name="relations-sqlite3",
|
|
10
|
-
version="0.6.
|
|
10
|
+
version="0.6.9",
|
|
11
11
|
package_dir = {'': 'lib'},
|
|
12
12
|
py_modules = [
|
|
13
13
|
'relations_sqlite3'
|
|
14
14
|
],
|
|
15
15
|
install_requires=[
|
|
16
|
-
'relations-dil>=0.6.
|
|
17
|
-
'relations-sqlite>=0.6.
|
|
16
|
+
'relations-dil>=0.6.13',
|
|
17
|
+
'relations-sqlite>=0.6.3'
|
|
18
18
|
],
|
|
19
19
|
url="https://github.com/relations-dil/python-relations-sqlite3",
|
|
20
20
|
author="Gaffer Fitch",
|
|
@@ -84,6 +84,23 @@ class Case(SourceModel):
|
|
|
84
84
|
relations.OneToMany(Unit, Test)
|
|
85
85
|
relations.OneToOne(Test, Case)
|
|
86
86
|
|
|
87
|
+
class Sis(SourceModel):
|
|
88
|
+
id = int
|
|
89
|
+
name = str
|
|
90
|
+
bro_id = set
|
|
91
|
+
|
|
92
|
+
class Bro(SourceModel):
|
|
93
|
+
id = int
|
|
94
|
+
name = str
|
|
95
|
+
sis_id = set
|
|
96
|
+
|
|
97
|
+
class SisBro(SourceModel):
|
|
98
|
+
ID = None
|
|
99
|
+
bro_id = int
|
|
100
|
+
sis_id = int
|
|
101
|
+
|
|
102
|
+
relations.ManyToMany(Sis, Bro, SisBro)
|
|
103
|
+
|
|
87
104
|
def dict_factory(cursor, row):
|
|
88
105
|
d = {}
|
|
89
106
|
for idx, col in enumerate(cursor.description):
|
|
@@ -218,6 +235,12 @@ CREATE UNIQUE INDEX `test_source`.`simple_name` ON `simple` (`name`);
|
|
|
218
235
|
model = Simple([["sure"], ["fine"]])
|
|
219
236
|
self.assertRaisesRegex(relations.ModelError, "only one create query at a time", model.query)
|
|
220
237
|
|
|
238
|
+
query = Sis("sure", bro_id=[1, 2, 3]).query()
|
|
239
|
+
query.generate()
|
|
240
|
+
|
|
241
|
+
self.assertEqual(query.sql, """INSERT INTO `test_source`.`sis` (`name`) VALUES (?)""")
|
|
242
|
+
self.assertEqual(query.args, ["sure"])
|
|
243
|
+
|
|
221
244
|
def test_create_id(self):
|
|
222
245
|
|
|
223
246
|
self.source.execute(Simple.define())
|
|
@@ -282,6 +305,22 @@ CREATE UNIQUE INDEX `test_source`.`simple_name` ON `simple` (`name`);
|
|
|
282
305
|
"things__for__0____1": "yep"
|
|
283
306
|
})
|
|
284
307
|
|
|
308
|
+
sis = Sis("Sally", bro_id=[2, 3, 4], _bulk=True)
|
|
309
|
+
self.assertRaisesRegex(relations.ModelError, "cannot create ties in bulk", sis.create)
|
|
310
|
+
|
|
311
|
+
self.source.execute(Sis.define())
|
|
312
|
+
self.source.execute(Bro.define())
|
|
313
|
+
self.source.execute(SisBro.define())
|
|
314
|
+
|
|
315
|
+
sis = Sis("Sally", bro_id=[2, 3, 4]).create()
|
|
316
|
+
|
|
317
|
+
cursor.execute("SELECT * FROM test_source.sis_bro ORDER BY bro_id")
|
|
318
|
+
self.assertEqual(cursor.fetchall(), [
|
|
319
|
+
{"sis_id": sis.id, "bro_id": 2},
|
|
320
|
+
{"sis_id": sis.id, "bro_id": 3},
|
|
321
|
+
{"sis_id": sis.id, "bro_id": 4}
|
|
322
|
+
])
|
|
323
|
+
|
|
285
324
|
cursor.close()
|
|
286
325
|
|
|
287
326
|
def test_retrieve_field(self):
|
|
@@ -588,6 +627,17 @@ LIMIT ?""")
|
|
|
588
627
|
|
|
589
628
|
self.assertEqual(Unit.many(like="p").count(), 1)
|
|
590
629
|
|
|
630
|
+
self.source.execute(Sis.define())
|
|
631
|
+
self.source.execute(Bro.define())
|
|
632
|
+
self.source.execute(SisBro.define())
|
|
633
|
+
|
|
634
|
+
tom = Bro("Tom").create()
|
|
635
|
+
Sis("Sally", bro_id=[tom.id]).create()
|
|
636
|
+
Sis("Mary").create()
|
|
637
|
+
|
|
638
|
+
self.assertEqual(Sis.many(bro_id=[tom.id]).count(), 1)
|
|
639
|
+
self.assertEqual(Sis.many(bro_id=[999]).count(), 0)
|
|
640
|
+
|
|
591
641
|
def test_values_retrieve(self):
|
|
592
642
|
|
|
593
643
|
model = unittest.mock.MagicMock()
|
|
@@ -636,6 +686,9 @@ LIMIT ?""")
|
|
|
636
686
|
self.source.execute(Case.define())
|
|
637
687
|
self.source.execute(Meta.define())
|
|
638
688
|
self.source.execute(Net.define())
|
|
689
|
+
self.source.execute(Sis.define())
|
|
690
|
+
self.source.execute(Bro.define())
|
|
691
|
+
self.source.execute(SisBro.define())
|
|
639
692
|
|
|
640
693
|
Unit([["stuff"], ["people"]]).create()
|
|
641
694
|
|
|
@@ -794,6 +847,122 @@ LIMIT ?""")
|
|
|
794
847
|
model = Net.many(subnet__max_value=int(ipaddress.IPv4Address('1.2.3.0')))
|
|
795
848
|
self.assertEqual(len(model), 0)
|
|
796
849
|
|
|
850
|
+
tom = Bro("Tom").create()
|
|
851
|
+
dick = Bro("Dick").create()
|
|
852
|
+
|
|
853
|
+
dot = Sis("Dot").create()
|
|
854
|
+
nikki = Sis("Nikki").create()
|
|
855
|
+
|
|
856
|
+
mary = Sis("Mary", bro_id=[tom.id, dick.id]).create()
|
|
857
|
+
harry = Bro("Harry", sis_id=[dot.id, nikki.id]).create()
|
|
858
|
+
|
|
859
|
+
self.assertEqual(mary.bro.id, [dick.id, tom.id])
|
|
860
|
+
self.assertEqual(harry.sis.id, [dot.id, nikki.id])
|
|
861
|
+
|
|
862
|
+
self.assertEqual(len(Sis.many(bro_id=[tom.id])), 1)
|
|
863
|
+
self.assertEqual(Sis.many(bro_id=[tom.id])[0].name, "Mary")
|
|
864
|
+
|
|
865
|
+
self.assertEqual(len(Bro.many(sis_id=[dot.id])), 1)
|
|
866
|
+
self.assertEqual(Bro.many(sis_id=[dot.id])[0].name, "Harry")
|
|
867
|
+
|
|
868
|
+
self.assertEqual(len(Sis.many(bro_id=[999])), 0)
|
|
869
|
+
|
|
870
|
+
def test_retrieve_ties_query(self):
|
|
871
|
+
|
|
872
|
+
self.source.execute(Sis.define())
|
|
873
|
+
self.source.execute(Bro.define())
|
|
874
|
+
self.source.execute(SisBro.define())
|
|
875
|
+
|
|
876
|
+
# The tie field is a set, so it's queried with set operators (has/any/all),
|
|
877
|
+
# resolved through the tie table. A sister has many brothers, so "any of" and
|
|
878
|
+
# "all of" are genuinely different queries.
|
|
879
|
+
|
|
880
|
+
tom = Bro("Tom").create()
|
|
881
|
+
dick = Bro("Dick").create()
|
|
882
|
+
harry = Bro("Harry").create()
|
|
883
|
+
|
|
884
|
+
Sis("Mary", bro_id=[tom.id, dick.id]).create() # tied to Tom, Dick
|
|
885
|
+
Sis("Sue", bro_id=[tom.id]).create() # tied to Tom
|
|
886
|
+
Sis("Ann", bro_id=[dick.id, harry.id]).create() # tied to Dick, Harry
|
|
887
|
+
|
|
888
|
+
# has: tied to that one brother
|
|
889
|
+
self.assertEqual(sorted(Sis.many(bro_id__has=tom.id).name), ["Mary", "Sue"])
|
|
890
|
+
self.assertEqual(Sis.many(bro_id__has=harry.id).name, ["Ann"])
|
|
891
|
+
self.assertEqual(len(Sis.many(bro_id__has=999)), 0)
|
|
892
|
+
|
|
893
|
+
# any: tied to at least one of them
|
|
894
|
+
self.assertEqual(sorted(Sis.many(bro_id__any=[tom.id, harry.id]).name), ["Ann", "Mary", "Sue"])
|
|
895
|
+
self.assertEqual(Sis.many(bro_id__any=[harry.id]).name, ["Ann"])
|
|
896
|
+
self.assertEqual(len(Sis.many(bro_id__any=[999])), 0)
|
|
897
|
+
|
|
898
|
+
# all: tied to every one of them
|
|
899
|
+
self.assertEqual(Sis.many(bro_id__all=[tom.id, dick.id]).name, ["Mary"])
|
|
900
|
+
self.assertEqual(sorted(Sis.many(bro_id__all=[dick.id]).name), ["Ann", "Mary"])
|
|
901
|
+
self.assertEqual(len(Sis.many(bro_id__all=[tom.id, harry.id])), 0)
|
|
902
|
+
|
|
903
|
+
# all must de-dupe the requested list (a repeated value can't change the result)
|
|
904
|
+
self.assertEqual(sorted(Sis.many(bro_id__all=[dick.id, dick.id]).name), ["Ann", "Mary"])
|
|
905
|
+
|
|
906
|
+
# negation
|
|
907
|
+
self.assertEqual(Sis.many(bro_id__not_has=tom.id).name, ["Ann"])
|
|
908
|
+
self.assertEqual(sorted(Sis.many(bro_id__not_any=[harry.id]).name), ["Mary", "Sue"])
|
|
909
|
+
|
|
910
|
+
# symmetric: brothers queried by their tied sisters
|
|
911
|
+
jane = Sis("Jane").create()
|
|
912
|
+
joan = Sis("Joan").create()
|
|
913
|
+
Bro("Bob", sis_id=[jane.id, joan.id]).create() # tied to Jane, Joan
|
|
914
|
+
Bro("Bill", sis_id=[jane.id]).create() # tied to Jane
|
|
915
|
+
|
|
916
|
+
self.assertEqual(sorted(Bro.many(sis_id__has=jane.id).name), ["Bill", "Bob"])
|
|
917
|
+
self.assertEqual(Bro.many(sis_id__all=[jane.id, joan.id]).name, ["Bob"])
|
|
918
|
+
self.assertEqual(Bro.many(sis_id__any=[joan.id]).name, ["Bob"])
|
|
919
|
+
|
|
920
|
+
def test_retrieve_ties_attr(self):
|
|
921
|
+
|
|
922
|
+
self.source.execute(Sis.define())
|
|
923
|
+
self.source.execute(Bro.define())
|
|
924
|
+
self.source.execute(SisBro.define())
|
|
925
|
+
|
|
926
|
+
# M2M attribute filtering: a flat join through the tie table filters on the sibling's
|
|
927
|
+
# own field. Existence semantics, with the sibling's normal field operators.
|
|
928
|
+
|
|
929
|
+
tom = Bro("Tom").create()
|
|
930
|
+
dick = Bro("Dick").create()
|
|
931
|
+
harry = Bro("Harry").create()
|
|
932
|
+
|
|
933
|
+
Sis("Mary", bro_id=[tom.id, dick.id]).create() # tied to Tom, Dick
|
|
934
|
+
Sis("Sue", bro_id=[tom.id]).create() # tied to Tom
|
|
935
|
+
Sis("Ann", bro_id=[dick.id, harry.id]).create() # tied to Dick, Harry
|
|
936
|
+
|
|
937
|
+
# tied to a brother with that name
|
|
938
|
+
self.assertEqual(sorted(Sis.many(bro__name="Tom").name), ["Mary", "Sue"])
|
|
939
|
+
self.assertEqual(Sis.many(bro__name="Harry").name, ["Ann"])
|
|
940
|
+
self.assertEqual(len(Sis.many(bro__name="Ghost")), 0)
|
|
941
|
+
|
|
942
|
+
# field operators land on the sibling: like
|
|
943
|
+
self.assertEqual(Sis.many(bro__name__like="arr").name, ["Ann"])
|
|
944
|
+
|
|
945
|
+
# DISTINCT: Mary is tied to BOTH Tom and Dick, so an "in" join would match her twice;
|
|
946
|
+
# she must appear once (retrieve) and count once
|
|
947
|
+
self.assertEqual(sorted(Sis.many(bro__name__in=["Tom", "Dick"]).name), ["Ann", "Mary", "Sue"])
|
|
948
|
+
self.assertEqual(Sis.many(bro__name__in=["Tom", "Dick"]).count(), 3)
|
|
949
|
+
|
|
950
|
+
# negation is field-level: a tied non-Tom exists (Mary stays via Dick)
|
|
951
|
+
self.assertEqual(sorted(Sis.many(bro__name__not_in=["Tom"]).name), ["Ann", "Mary"])
|
|
952
|
+
|
|
953
|
+
# criteria on the same relation filter the SAME tied brother
|
|
954
|
+
self.assertEqual(Sis.many(bro__name="Dick", bro__id=harry.id).name, [])
|
|
955
|
+
self.assertEqual(sorted(Sis.many(bro__name="Dick", bro__id=dick.id).name), ["Ann", "Mary"])
|
|
956
|
+
|
|
957
|
+
# symmetric: brothers filtered by a tied sister's name
|
|
958
|
+
jane = Sis("Jane").create()
|
|
959
|
+
joan = Sis("Joan").create()
|
|
960
|
+
Bro("Bab", sis_id=[jane.id, joan.id]).create() # tied to Jane, Joan
|
|
961
|
+
Bro("Bil", sis_id=[jane.id]).create() # tied to Jane
|
|
962
|
+
|
|
963
|
+
self.assertEqual(sorted(Bro.many(sis__name="Jane").name), ["Bab", "Bil"])
|
|
964
|
+
self.assertEqual(Bro.many(sis__name__in=["Joan"]).name, ["Bab"])
|
|
965
|
+
|
|
797
966
|
def test_titles(self):
|
|
798
967
|
|
|
799
968
|
self.source.execute(Unit.define())
|
|
@@ -801,6 +970,9 @@ LIMIT ?""")
|
|
|
801
970
|
self.source.execute(Case.define())
|
|
802
971
|
self.source.execute(Meta.define())
|
|
803
972
|
self.source.execute(Net.define())
|
|
973
|
+
self.source.execute(Sis.define())
|
|
974
|
+
self.source.execute(Bro.define())
|
|
975
|
+
self.source.execute(SisBro.define())
|
|
804
976
|
|
|
805
977
|
Unit("people").create().test.add("stuff").add("things").create()
|
|
806
978
|
|
|
@@ -838,6 +1010,11 @@ LIMIT ?""")
|
|
|
838
1010
|
1: ["1.2.3.4"]
|
|
839
1011
|
})
|
|
840
1012
|
|
|
1013
|
+
tom = Bro("Tom").create()
|
|
1014
|
+
Sis("Sally", bro_id=[tom.id]).create()
|
|
1015
|
+
|
|
1016
|
+
self.assertEqual(len(Sis.many(bro_id=[tom.id]).titles().ids), 1)
|
|
1017
|
+
|
|
841
1018
|
def test_update_field(self):
|
|
842
1019
|
|
|
843
1020
|
# Standard
|
|
@@ -901,6 +1078,9 @@ LIMIT ?""")
|
|
|
901
1078
|
self.source.execute(Case.define())
|
|
902
1079
|
self.source.execute(Meta.define())
|
|
903
1080
|
self.source.execute(Net.define())
|
|
1081
|
+
self.source.execute(Sis.define())
|
|
1082
|
+
self.source.execute(Bro.define())
|
|
1083
|
+
self.source.execute(SisBro.define())
|
|
904
1084
|
|
|
905
1085
|
Unit([["people"], ["stuff"]]).create()
|
|
906
1086
|
|
|
@@ -943,6 +1123,31 @@ LIMIT ?""")
|
|
|
943
1123
|
self.assertEqual(Net.one(ping.id).ip.compressed, "13.14.15.16")
|
|
944
1124
|
self.assertEqual(Net.one(pong.id).ip.compressed, "5.6.7.8")
|
|
945
1125
|
|
|
1126
|
+
Sis("Sally").create()
|
|
1127
|
+
bro = Bro("Harry").create()
|
|
1128
|
+
Sis.many(name="Sally").set(bro_id=[bro.id]).update()
|
|
1129
|
+
|
|
1130
|
+
sis = Sis.one(name="Sally")
|
|
1131
|
+
|
|
1132
|
+
self.assertEqual(Bro.one(name="Harry").sis.id, [sis.id])
|
|
1133
|
+
|
|
1134
|
+
tom = Bro("Tom").create()
|
|
1135
|
+
dick = Bro("Dick").create()
|
|
1136
|
+
|
|
1137
|
+
dot = Sis("Dot").create()
|
|
1138
|
+
nikki = Sis("Nikki").create()
|
|
1139
|
+
|
|
1140
|
+
tom.sis_id = [nikki.id, dot.id]
|
|
1141
|
+
dot.bro_id = [tom.id, dick.id]
|
|
1142
|
+
|
|
1143
|
+
tom.update()
|
|
1144
|
+
dot.update()
|
|
1145
|
+
|
|
1146
|
+
self.assertEqual(Bro.one(name="Tom").sis.id, [dot.id, nikki.id])
|
|
1147
|
+
self.assertEqual(Sis.one(name="Dot").bro.id, [dick.id, tom.id])
|
|
1148
|
+
self.assertEqual(Bro.one(name="Dick").sis.id, [dot.id])
|
|
1149
|
+
self.assertEqual(Sis.one(name="Nikki").bro.id, [tom.id])
|
|
1150
|
+
|
|
946
1151
|
def test_delete_query(self):
|
|
947
1152
|
|
|
948
1153
|
self.source.execute(Unit.define())
|
|
@@ -974,6 +1179,9 @@ LIMIT ?""")
|
|
|
974
1179
|
self.source.execute(Test.define())
|
|
975
1180
|
self.source.execute(Case.define())
|
|
976
1181
|
self.source.execute(Plain.define())
|
|
1182
|
+
self.source.execute(Sis.define())
|
|
1183
|
+
self.source.execute(Bro.define())
|
|
1184
|
+
self.source.execute(SisBro.define())
|
|
977
1185
|
|
|
978
1186
|
unit = Unit("people")
|
|
979
1187
|
unit.test.add("stuff").add("things")
|
|
@@ -992,6 +1200,34 @@ LIMIT ?""")
|
|
|
992
1200
|
plain = Plain(0, "nope").create()
|
|
993
1201
|
self.assertRaisesRegex(relations.ModelError, "plain: nothing to delete from", plain.delete)
|
|
994
1202
|
|
|
1203
|
+
Sis("Sally").create()
|
|
1204
|
+
bro = Bro("Harry").create()
|
|
1205
|
+
Sis.many(name="Sally").set(bro_id=[bro.id]).update()
|
|
1206
|
+
|
|
1207
|
+
Sis.many(name="Sally").delete()
|
|
1208
|
+
|
|
1209
|
+
self.assertEqual(Bro.one(name="Harry").sis.id, [])
|
|
1210
|
+
self.assertEqual(SisBro.many().count(), 0)
|
|
1211
|
+
|
|
1212
|
+
tom = Bro("Tom").create()
|
|
1213
|
+
dick = Bro("Dick").create()
|
|
1214
|
+
|
|
1215
|
+
dot = Sis("Dot").create()
|
|
1216
|
+
nikki = Sis("Nikki").create()
|
|
1217
|
+
|
|
1218
|
+
tom.sis_id = [nikki.id, dot.id]
|
|
1219
|
+
dot.bro_id = [tom.id, dick.id]
|
|
1220
|
+
|
|
1221
|
+
tom.update()
|
|
1222
|
+
dot.update()
|
|
1223
|
+
|
|
1224
|
+
Bro.one(name="Dick").retrieve().delete()
|
|
1225
|
+
Sis.one(name="Nikki").retrieve().delete()
|
|
1226
|
+
|
|
1227
|
+
self.assertEqual(Bro.one(name="Tom").sis.id, [dot.id])
|
|
1228
|
+
self.assertEqual(Sis.one(name="Dot").bro.id, [tom.id])
|
|
1229
|
+
self.assertEqual(SisBro.many().count(), 1)
|
|
1230
|
+
|
|
995
1231
|
def test_definition(self):
|
|
996
1232
|
|
|
997
1233
|
with open("ddl/general.json", 'w') as ddl_file:
|
|
File without changes
|
|
File without changes
|
{relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/lib/relations_sqlite3.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{relations-sqlite3-0.6.7 → relations_sqlite3-0.6.9}/lib/relations_sqlite3.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|