relations-sqlite3 0.6.9__tar.gz → 0.6.10__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.9
3
+ Version: 0.6.10
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,7 +9,7 @@ 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
12
+ Requires-Dist: relations-dil>=0.6.16
13
13
  Requires-Dist: relations-sqlite>=0.6.3
14
14
 
15
15
  # relations-sqlite3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: relations-sqlite3
3
- Version: 0.6.9
3
+ Version: 0.6.10
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,7 +9,7 @@ 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
12
+ Requires-Dist: relations-dil>=0.6.16
13
13
  Requires-Dist: relations-sqlite>=0.6.3
14
14
 
15
15
  # relations-sqlite3
@@ -1,2 +1,2 @@
1
- relations-dil>=0.6.13
1
+ relations-dil>=0.6.16
2
2
  relations-sqlite>=0.6.3
@@ -195,14 +195,30 @@ class Source(relations_sql.SOURCE, relations.Source): # pylint: disable=too-many
195
195
 
196
196
  return model
197
197
 
198
- def retrieve_field(self, field, query):
198
+ def retrieve_record(self, record, query):
199
199
  """
200
- Adds where caluse to query
200
+ Adds where clauses to query for all the fields in the record
201
+ """
202
+
203
+ for field in record._order:
204
+ self.retrieve_field(field, query, record)
205
+
206
+ def retrieve_field(self, field, query, record=None):
207
+ """
208
+ Adds where caluse to query, an injected field is looked up in the field it's stored in
201
209
  """
202
210
 
203
211
  for operator, value in (field.criteria or {}).items():
204
- name = f"{field.store}__{operator}"
205
- extracted = operator.rsplit("__", 1)[0] in (field.extract or {})
212
+
213
+ if field.inject:
214
+ stored, path = field.inject.split("__", 1)
215
+ stored = record._names[stored]
216
+ name = f"{stored.store}__{path}__{operator}"
217
+ extracted = path in (stored.extract or {})
218
+ else:
219
+ name = f"{field.store}__{operator}"
220
+ extracted = operator.rsplit("__", 1)[0] in (field.extract or {})
221
+
206
222
  query.WHERE(self.OP(name, value, EXTRACTED=extracted))
207
223
 
208
224
  def like(self, model, query):
@@ -252,7 +268,17 @@ class Source(relations_sql.SOURCE, relations.Source): # pylint: disable=too-many
252
268
  """
253
269
 
254
270
  for field in (model._sort or model._order or []):
255
- query.ORDER_BY(**{field[1:]: (self.ASC if field[0] == "+" else self.DESC)})
271
+
272
+ name = field[1:]
273
+ field_name, path = (name.split("__", 1) + [None])[:2]
274
+
275
+ # an injected field is sorted by its path in the field it's stored in
276
+
277
+ if model._fields._names[field_name].inject:
278
+ stored, injected = model._fields._names[field_name].inject.split("__", 1)
279
+ name = f"{model._fields._names[stored].store}__{injected}" + (f"__{path}" if path else "")
280
+
281
+ query.ORDER_BY(**{name: (self.ASC if field[0] == "+" else self.DESC)})
256
282
 
257
283
  model._sort = None
258
284
 
@@ -1,19 +1,26 @@
1
1
  #!/usr/bin/env python
2
2
 
3
+ import os
3
4
  from setuptools import setup
4
5
 
5
6
  with open("README.md", "r") as readme_file:
6
7
  long_description = readme_file.read()
7
8
 
9
+ version = os.environ.get("BUILD_VERSION")
10
+
11
+ if version is None:
12
+ with open("VERSION", "r") as version_file:
13
+ version = version_file.read().strip()
14
+
8
15
  setup(
9
16
  name="relations-sqlite3",
10
- version="0.6.9",
17
+ version=version,
11
18
  package_dir = {'': 'lib'},
12
19
  py_modules = [
13
20
  'relations_sqlite3'
14
21
  ],
15
22
  install_requires=[
16
- 'relations-dil>=0.6.13',
23
+ 'relations-dil>=0.6.16',
17
24
  'relations-sqlite>=0.6.3'
18
25
  ],
19
26
  url="https://github.com/relations-dil/python-relations-sqlite3",
@@ -101,6 +101,17 @@ class SisBro(SourceModel):
101
101
 
102
102
  relations.ManyToMany(Sis, Bro, SisBro)
103
103
 
104
+ class Owner(SourceModel):
105
+ id = int
106
+ name = str
107
+
108
+ class Pet(SourceModel):
109
+ id = int
110
+ name = str
111
+ what = dict
112
+
113
+ relations.OneToMany(Owner, Pet, child_inject="what")
114
+
104
115
  def dict_factory(cursor, row):
105
116
  d = {}
106
117
  for idx, col in enumerate(cursor.description):
@@ -218,6 +229,16 @@ CREATE UNIQUE INDEX `test_source`.`simple_name` ON `simple` (`name`);
218
229
 
219
230
  self.source.execute(self.source.define(Simple.thy().define()))
220
231
 
232
+ # an injected key isn't a column, it's stored in the dict field
233
+
234
+ self.source.execute(Owner.define())
235
+ self.source.execute(Pet.define())
236
+
237
+ cursor = self.source.connection.cursor()
238
+ cursor.execute("PRAGMA test_source.table_info(pet)")
239
+ self.assertEqual([column["name"] for column in cursor.fetchall()], ["id", "name", "what"])
240
+ cursor.close()
241
+
221
242
  def test_create_query(self):
222
243
 
223
244
  query = Simple("sure").query()
@@ -323,6 +344,27 @@ CREATE UNIQUE INDEX `test_source`.`simple_name` ON `simple` (`name`);
323
344
 
324
345
  cursor.close()
325
346
 
347
+ # an injected key is stored in the dict field, whether there's a parent or not
348
+
349
+ self.source.execute(Owner.define())
350
+ self.source.execute(Pet.define())
351
+
352
+ owner = Owner("pat")
353
+ owner.pet.add("rex")
354
+ owner.create()
355
+
356
+ stray = Pet("stray").create()
357
+
358
+ cursor = self.source.connection.cursor()
359
+ cursor.execute("SELECT * FROM test_source.pet ORDER BY name")
360
+ rows = [self.source.values_retrieve(stray, row) for row in cursor.fetchall()]
361
+ cursor.close()
362
+
363
+ self.assertEqual(rows, [
364
+ {"id": 1, "name": "rex", "what": {"relations": {"owner": {"id": owner.id}}}},
365
+ {"id": 2, "name": "stray", "what": {}}
366
+ ])
367
+
326
368
  def test_retrieve_field(self):
327
369
 
328
370
  field = relations.Field(int, name="id")
@@ -361,6 +403,37 @@ CREATE UNIQUE INDEX `test_source`.`simple_name` ON `simple` (`name`);
361
403
  self.assertEqual(query.sql, """SELECT WHERE `things__for__0____1`=?""")
362
404
  self.assertEqual(query.args, ['yes'])
363
405
 
406
+ # an injected key is looked up by its path in the field it's stored in
407
+
408
+ record = copy.deepcopy(Pet.thy()._fields)
409
+ field = record._names["owner_id"]
410
+ field.filter(1)
411
+ query = self.source.SELECT()
412
+ self.source.retrieve_field(field, query, record)
413
+ query.generate()
414
+ self.assertEqual(query.sql, """SELECT WHERE json_extract(`what`,?)=?""")
415
+ self.assertEqual(query.args, ['$.relations.owner.id', 1])
416
+
417
+ record = copy.deepcopy(Pet.thy()._fields)
418
+ field = record._names["owner_id"]
419
+ field.filter([1, 2], "in")
420
+ query = self.source.SELECT()
421
+ self.source.retrieve_field(field, query, record)
422
+ query.generate()
423
+ self.assertEqual(query.sql, """SELECT WHERE json_extract(`what`,?) IN (?,?)""")
424
+ self.assertEqual(query.args, ['$.relations.owner.id', 1, 2])
425
+
426
+ def test_retrieve_record(self):
427
+
428
+ record = copy.deepcopy(Pet.thy()._fields)
429
+ record._names["name"].filter("rex")
430
+ record._names["owner_id"].filter(1)
431
+ query = self.source.SELECT()
432
+ self.source.retrieve_record(record, query)
433
+ query.generate()
434
+ self.assertEqual(query.sql, """SELECT WHERE `name`=? AND json_extract(`what`,?)=?""")
435
+ self.assertEqual(query.args, ["rex", "$.relations.owner.id", 1])
436
+
364
437
  def test_like(self):
365
438
 
366
439
  self.source.execute(Unit.define())
@@ -448,6 +521,16 @@ CREATE UNIQUE INDEX `test_source`.`simple_name` ON `simple` (`name`);
448
521
  self.assertEqual(query.args, [])
449
522
  self.assertIsNone(unit._sort)
450
523
 
524
+ # an injected key sorts by its path in the field it's stored in
525
+
526
+ pet = Pet.many()
527
+ pet._sort = ['-owner_id']
528
+ query = self.source.SELECT()
529
+ self.source.sort(pet, query)
530
+ query.generate()
531
+ self.assertEqual(query.sql, """SELECT ORDER BY json_extract(`what`,?) DESC""")
532
+ self.assertEqual(query.args, ['$.relations.owner.id'])
533
+
451
534
  def test_limit(self):
452
535
 
453
536
  unit = Unit.one()
@@ -638,6 +721,19 @@ LIMIT ?""")
638
721
  self.assertEqual(Sis.many(bro_id=[tom.id]).count(), 1)
639
722
  self.assertEqual(Sis.many(bro_id=[999]).count(), 0)
640
723
 
724
+ # an injected key counts like any other
725
+
726
+ self.source.execute(Owner.define())
727
+ self.source.execute(Pet.define())
728
+
729
+ pat = Owner("pat").create()
730
+
731
+ Pet([{"name": "rex", "owner_id": pat.id}, {"name": "stray"}]).create()
732
+
733
+ self.assertEqual(Pet.many().count(), 2)
734
+ self.assertEqual(Pet.many(owner_id=pat.id).count(), 1)
735
+ self.assertEqual(Pet.many(owner_id=99).count(), 0)
736
+
641
737
  def test_values_retrieve(self):
642
738
 
643
739
  model = unittest.mock.MagicMock()
@@ -867,6 +963,37 @@ LIMIT ?""")
867
963
 
868
964
  self.assertEqual(len(Sis.many(bro_id=[999])), 0)
869
965
 
966
+ # an injected key retrieves, filters and relates like a column
967
+
968
+ self.source.execute(Owner.define())
969
+ self.source.execute(Pet.define())
970
+
971
+ pat = Owner("pat").create()
972
+ sam = Owner("sam").create()
973
+
974
+ Pet([
975
+ {"name": "rex", "owner_id": pat.id},
976
+ {"name": "fido", "owner_id": sam.id},
977
+ {"name": "spot", "owner_id": pat.id},
978
+ {"name": "stray"}
979
+ ]).create()
980
+
981
+ self.assertEqual(Pet.many(owner_id=pat.id).name, ["rex", "spot"])
982
+ self.assertEqual(Pet.many(owner_id__in=[pat.id, sam.id]).name, ["fido", "rex", "spot"])
983
+ self.assertEqual(Pet.many(owner_id__null=True).name, ["stray"])
984
+ self.assertEqual(Pet.many(owner_id=99).name, [])
985
+
986
+ self.assertEqual(Pet.one(name="rex").owner.name, "pat")
987
+ self.assertIsNone(Pet.one(name="stray").owner)
988
+
989
+ self.assertEqual(Owner.one(pat.id).pet.name, ["rex", "spot"])
990
+ self.assertEqual(Owner.one(name="pat").pet.name, ["rex", "spot"])
991
+
992
+ self.assertEqual(Pet.many(owner__name="pat").name, ["rex", "spot"])
993
+ self.assertEqual(Owner.many(pet__name="fido").name, ["sam"])
994
+
995
+ self.assertEqual(Pet.many(like="re").name, ["rex"])
996
+
870
997
  def test_retrieve_ties_query(self):
871
998
 
872
999
  self.source.execute(Sis.define())
@@ -1148,6 +1275,34 @@ LIMIT ?""")
1148
1275
  self.assertEqual(Bro.one(name="Dick").sis.id, [dot.id])
1149
1276
  self.assertEqual(Sis.one(name="Nikki").bro.id, [tom.id])
1150
1277
 
1278
+ # an injected key updates like a column, but not in mass
1279
+
1280
+ self.source.execute(Owner.define())
1281
+ self.source.execute(Pet.define())
1282
+
1283
+ pat = Owner("pat").create()
1284
+ sam = Owner("sam").create()
1285
+
1286
+ Pet([{"name": "rex", "owner_id": pat.id}, {"name": "fido", "owner_id": sam.id}]).create()
1287
+
1288
+ pet = Pet.one(name="rex").retrieve()
1289
+ pet.owner_id = sam.id
1290
+
1291
+ self.assertEqual(pet.update(), 1)
1292
+ self.assertEqual(Pet.one(name="rex").owner_id, sam.id)
1293
+ self.assertEqual(Pet.many(owner_id=sam.id).name, ["fido", "rex"])
1294
+
1295
+ pet.owner_id = None
1296
+
1297
+ self.assertEqual(pet.update(), 1)
1298
+ self.assertIsNone(Pet.one(name="rex").owner_id)
1299
+ self.assertEqual(Pet.many(owner_id__null=True).name, ["rex"])
1300
+
1301
+ self.assertEqual(Pet.one(name="rex").set(owner_id=pat.id).update(), 1)
1302
+ self.assertEqual(Pet.one(name="rex").owner_id, pat.id)
1303
+
1304
+ self.assertRaisesRegex(relations.FieldError, "no mass update with inject", Pet.many(name="rex").set(owner_id=sam.id).update)
1305
+
1151
1306
  def test_delete_query(self):
1152
1307
 
1153
1308
  self.source.execute(Unit.define())
@@ -1228,6 +1383,18 @@ LIMIT ?""")
1228
1383
  self.assertEqual(Sis.one(name="Dot").bro.id, [tom.id])
1229
1384
  self.assertEqual(SisBro.many().count(), 1)
1230
1385
 
1386
+ # an injected key deletes like any other
1387
+
1388
+ self.source.execute(Owner.define())
1389
+ self.source.execute(Pet.define())
1390
+
1391
+ pat = Owner("pat").create()
1392
+
1393
+ Pet([{"name": "rex", "owner_id": pat.id}, {"name": "spot", "owner_id": pat.id}, {"name": "stray"}]).create()
1394
+
1395
+ self.assertEqual(Pet.many(owner_id=pat.id).delete(), 2)
1396
+ self.assertEqual(Pet.many().name, ["stray"])
1397
+
1231
1398
  def test_definition(self):
1232
1399
 
1233
1400
  with open("ddl/general.json", 'w') as ddl_file: