relations-sqlite3 0.6.6__tar.gz → 0.6.8__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.6
3
+ Version: 0.6.8
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.6
3
+ Version: 0.6.8
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
  """
@@ -128,7 +128,7 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
128
128
  Get query for what's being inserted
129
129
  """
130
130
 
131
- fields = [field.store for field in model._fields._order if not field.auto and not field.inject]
131
+ fields = [field.store for field in model._fields._order if not field.auto and not field.inject and field.store]
132
132
  query = self.INSERT(self.TABLE_NAME(model.STORE, schema=model.SCHEMA), *fields)
133
133
 
134
134
  if not model._bulk and model._id is not None and model._fields._names[model._id].auto:
@@ -157,6 +157,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
157
157
  Executes the create
158
158
  """
159
159
 
160
+ super().create(model)
161
+
160
162
  cursor = self.connection.cursor()
161
163
 
162
164
  if not model._bulk and model._id is not None and model._fields._names[model._id].auto:
@@ -173,6 +175,10 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
173
175
  if not model._bulk:
174
176
 
175
177
  for creating in model._each("create"):
178
+
179
+ if model._id:
180
+ self.create_ties(creating)
181
+
176
182
  for parent_child in creating.CHILDREN:
177
183
  if creating._children.get(parent_child):
178
184
  creating._children[parent_child].create()
@@ -217,10 +223,10 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
217
223
  parent = False
218
224
 
219
225
  for relation in model.PARENTS.values():
220
- if field.name == relation.child_field:
226
+ if field.name == relation.child_parent_ref:
221
227
  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]))
228
+ if parent[relation.parent_id]:
229
+ titles(self.IN(field.store, parent[relation.parent_id]))
224
230
  model.overflow = model.overflow or parent.overflow
225
231
  else:
226
232
  parent = True
@@ -268,6 +274,7 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
268
274
  query = self.SELECT(self.AS("total", self.SQL("COUNT(*)"))).FROM(self.TABLE_NAME(model.STORE, schema=model.SCHEMA))
269
275
 
270
276
  model._collate()
277
+ self.collate_ties_query(model, query)
271
278
  self.retrieve_record(model._record, query)
272
279
  self.like(model, query)
273
280
 
@@ -299,6 +306,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
299
306
  Executes the count
300
307
  """
301
308
 
309
+ super().count(model)
310
+
302
311
  cursor = self.connection.cursor()
303
312
 
304
313
  if query is None:
@@ -330,6 +339,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
330
339
  Executes the retrieve
331
340
  """
332
341
 
342
+ super().retrieve(model)
343
+
333
344
  cursor = self.connection.cursor()
334
345
 
335
346
  if query is None:
@@ -368,6 +379,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
368
379
 
369
380
  model._action = "update"
370
381
 
382
+ self.retrieve_ties(model)
383
+
371
384
  cursor.close()
372
385
 
373
386
  return model
@@ -428,6 +441,8 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
428
441
  Executes the update
429
442
  """
430
443
 
444
+ super().update(model)
445
+
431
446
  cursor = self.connection.cursor()
432
447
 
433
448
  updated = 0
@@ -438,9 +453,29 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
438
453
 
439
454
  update_query = query or self.update_query(model)
440
455
 
441
- update_query.generate()
442
- cursor.execute(update_query.sql, update_query.args)
443
- updated = cursor.rowcount
456
+ if update_query.SET.expressions:
457
+
458
+ update_query.generate()
459
+ cursor.execute(update_query.sql, update_query.args)
460
+ updated += cursor.rowcount
461
+
462
+ ties = model._record.tie({})
463
+
464
+ if ties:
465
+
466
+ store_id = model._fields._names[model._id].store
467
+
468
+ id_query = self.SELECT(store_id, WHERE=update_query.WHERE).FROM(self.TABLE_NAME(model.STORE, schema=model.SCHEMA))
469
+
470
+ id_query.generate()
471
+
472
+ cursor.execute(id_query.sql, id_query.args)
473
+ ids = [row[store_id] for row in cursor.fetchall()]
474
+
475
+ self.delete_ties(model, ids)
476
+ self.create_ties(model, ties, ids)
477
+
478
+ updated = len(ids)
444
479
 
445
480
  elif model._id:
446
481
 
@@ -448,17 +483,21 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
448
483
 
449
484
  update_query = query or self.update_query(updating)
450
485
 
451
- if update_query.SET:
486
+ if update_query.SET.expressions:
452
487
 
453
488
  update_query.generate()
454
489
  cursor.execute(update_query.sql, update_query.args)
490
+ updated += cursor.rowcount
491
+
492
+
493
+
494
+ self.delete_ties(updating)
495
+ self.create_ties(updating)
455
496
 
456
497
  for parent_child in updating.CHILDREN:
457
498
  if updating._children.get(parent_child):
458
499
  updating._children[parent_child].create().update()
459
500
 
460
- updated += cursor.rowcount
461
-
462
501
  else:
463
502
 
464
503
  raise relations.ModelError(model, "nothing to update from")
@@ -482,6 +521,7 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
482
521
  store = model._fields._names[model._id].store
483
522
  for deleting in model._each():
484
523
  ids.append(deleting[model._id])
524
+
485
525
  query.WHERE(**{f"{store}__in": ids})
486
526
 
487
527
  else:
@@ -495,9 +535,38 @@ class Source(relations.Source): # pylint: disable=too-many-public-methods
495
535
  Executes the delete
496
536
  """
497
537
 
538
+ super().delete(model)
539
+
498
540
  cursor = self.connection.cursor()
499
541
 
500
- delete_query = query or self.delete_query(model)
542
+ if model._action == "retrieve":
543
+
544
+ delete_query = query or self.delete_query(model)
545
+
546
+ if model._id:
547
+
548
+ store_id = model._fields._names[model._id].store
549
+
550
+ id_query = self.SELECT(store_id, WHERE=delete_query.WHERE).FROM(self.TABLE_NAME(model.STORE, schema=model.SCHEMA))
551
+
552
+ id_query.generate()
553
+
554
+ cursor.execute(id_query.sql, id_query.args)
555
+ ids = [row[store_id] for row in cursor.fetchall()]
556
+
557
+ self.delete_ties(model, ids)
558
+
559
+ elif model._id:
560
+
561
+ delete_query = self.delete_query(model)
562
+
563
+ for deleting in model._each():
564
+ if self.has_ties(deleting):
565
+ self.delete_ties(deleting)
566
+
567
+ else:
568
+
569
+ raise relations.ModelError(model, "nothing to delete from")
501
570
 
502
571
  delete_query.generate()
503
572
  cursor.execute(delete_query.sql, tuple(delete_query.args))
@@ -7,13 +7,14 @@ with open("README.md", "r") as readme_file:
7
7
 
8
8
  setup(
9
9
  name="relations-sqlite3",
10
- version="0.6.6",
10
+ version="0.6.8",
11
11
  package_dir = {'': 'lib'},
12
12
  py_modules = [
13
13
  'relations_sqlite3'
14
14
  ],
15
15
  install_requires=[
16
- 'relations-sqlite>=0.6.1'
16
+ 'relations-dil>=0.6.13',
17
+ 'relations-sqlite>=0.6.3'
17
18
  ],
18
19
  url="https://github.com/relations-dil/python-relations-sqlite3",
19
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,76 @@ 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
+
797
920
  def test_titles(self):
798
921
 
799
922
  self.source.execute(Unit.define())
@@ -801,6 +924,9 @@ LIMIT ?""")
801
924
  self.source.execute(Case.define())
802
925
  self.source.execute(Meta.define())
803
926
  self.source.execute(Net.define())
927
+ self.source.execute(Sis.define())
928
+ self.source.execute(Bro.define())
929
+ self.source.execute(SisBro.define())
804
930
 
805
931
  Unit("people").create().test.add("stuff").add("things").create()
806
932
 
@@ -838,6 +964,11 @@ LIMIT ?""")
838
964
  1: ["1.2.3.4"]
839
965
  })
840
966
 
967
+ tom = Bro("Tom").create()
968
+ Sis("Sally", bro_id=[tom.id]).create()
969
+
970
+ self.assertEqual(len(Sis.many(bro_id=[tom.id]).titles().ids), 1)
971
+
841
972
  def test_update_field(self):
842
973
 
843
974
  # Standard
@@ -901,6 +1032,9 @@ LIMIT ?""")
901
1032
  self.source.execute(Case.define())
902
1033
  self.source.execute(Meta.define())
903
1034
  self.source.execute(Net.define())
1035
+ self.source.execute(Sis.define())
1036
+ self.source.execute(Bro.define())
1037
+ self.source.execute(SisBro.define())
904
1038
 
905
1039
  Unit([["people"], ["stuff"]]).create()
906
1040
 
@@ -943,6 +1077,31 @@ LIMIT ?""")
943
1077
  self.assertEqual(Net.one(ping.id).ip.compressed, "13.14.15.16")
944
1078
  self.assertEqual(Net.one(pong.id).ip.compressed, "5.6.7.8")
945
1079
 
1080
+ Sis("Sally").create()
1081
+ bro = Bro("Harry").create()
1082
+ Sis.many(name="Sally").set(bro_id=[bro.id]).update()
1083
+
1084
+ sis = Sis.one(name="Sally")
1085
+
1086
+ self.assertEqual(Bro.one(name="Harry").sis.id, [sis.id])
1087
+
1088
+ tom = Bro("Tom").create()
1089
+ dick = Bro("Dick").create()
1090
+
1091
+ dot = Sis("Dot").create()
1092
+ nikki = Sis("Nikki").create()
1093
+
1094
+ tom.sis_id = [nikki.id, dot.id]
1095
+ dot.bro_id = [tom.id, dick.id]
1096
+
1097
+ tom.update()
1098
+ dot.update()
1099
+
1100
+ self.assertEqual(Bro.one(name="Tom").sis.id, [dot.id, nikki.id])
1101
+ self.assertEqual(Sis.one(name="Dot").bro.id, [dick.id, tom.id])
1102
+ self.assertEqual(Bro.one(name="Dick").sis.id, [dot.id])
1103
+ self.assertEqual(Sis.one(name="Nikki").bro.id, [tom.id])
1104
+
946
1105
  def test_delete_query(self):
947
1106
 
948
1107
  self.source.execute(Unit.define())
@@ -974,6 +1133,9 @@ LIMIT ?""")
974
1133
  self.source.execute(Test.define())
975
1134
  self.source.execute(Case.define())
976
1135
  self.source.execute(Plain.define())
1136
+ self.source.execute(Sis.define())
1137
+ self.source.execute(Bro.define())
1138
+ self.source.execute(SisBro.define())
977
1139
 
978
1140
  unit = Unit("people")
979
1141
  unit.test.add("stuff").add("things")
@@ -992,6 +1154,34 @@ LIMIT ?""")
992
1154
  plain = Plain(0, "nope").create()
993
1155
  self.assertRaisesRegex(relations.ModelError, "plain: nothing to delete from", plain.delete)
994
1156
 
1157
+ Sis("Sally").create()
1158
+ bro = Bro("Harry").create()
1159
+ Sis.many(name="Sally").set(bro_id=[bro.id]).update()
1160
+
1161
+ Sis.many(name="Sally").delete()
1162
+
1163
+ self.assertEqual(Bro.one(name="Harry").sis.id, [])
1164
+ self.assertEqual(SisBro.many().count(), 0)
1165
+
1166
+ tom = Bro("Tom").create()
1167
+ dick = Bro("Dick").create()
1168
+
1169
+ dot = Sis("Dot").create()
1170
+ nikki = Sis("Nikki").create()
1171
+
1172
+ tom.sis_id = [nikki.id, dot.id]
1173
+ dot.bro_id = [tom.id, dick.id]
1174
+
1175
+ tom.update()
1176
+ dot.update()
1177
+
1178
+ Bro.one(name="Dick").retrieve().delete()
1179
+ Sis.one(name="Nikki").retrieve().delete()
1180
+
1181
+ self.assertEqual(Bro.one(name="Tom").sis.id, [dot.id])
1182
+ self.assertEqual(Sis.one(name="Dot").bro.id, [tom.id])
1183
+ self.assertEqual(SisBro.many().count(), 1)
1184
+
995
1185
  def test_definition(self):
996
1186
 
997
1187
  with open("ddl/general.json", 'w') as ddl_file:
@@ -1 +0,0 @@
1
- relations-sqlite>=0.6.1