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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: relations-sqlite3
3
- Version: 0.6.7
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.7
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
 
@@ -0,0 +1,2 @@
1
+ relations-dil>=0.6.13
2
+ relations-sqlite>=0.6.3
@@ -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.child_field:
228
+ if field.name == relation.child_parent_ref:
221
229
  parent = relation.Parent.many(like=model._like).limit(model._chunk)
222
- if parent[relation.parent_field]:
223
- titles(self.IN(field.store, parent[relation.parent_field]))
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
- query.FIELDS = self.FIELDS("*")
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.generate()
442
- cursor.execute(update_query.sql, update_query.args)
443
- updated = cursor.rowcount
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
- delete_query = query or self.delete_query(model)
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.7",
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.12',
17
- 'relations-sqlite>=0.6.2'
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:
@@ -1,2 +0,0 @@
1
- relations-dil>=0.6.12
2
- relations-sqlite>=0.6.2