piccolo 1.25.0__py3-none-any.whl → 1.26.0__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.
piccolo/__init__.py CHANGED
@@ -1 +1 @@
1
- __VERSION__ = "1.25.0"
1
+ __VERSION__ = "1.26.0"
@@ -19,6 +19,7 @@ from piccolo.columns.column_types import ForeignKey, Serial
19
19
  from piccolo.engine import engine_finder
20
20
  from piccolo.query import Query
21
21
  from piccolo.query.base import DDL
22
+ from piccolo.query.constraints import get_fk_constraint_name
22
23
  from piccolo.schema import SchemaDDLBase
23
24
  from piccolo.table import Table, create_table_class, sort_table_classes
24
25
  from piccolo.utils.warnings import colored_warning
@@ -423,8 +424,8 @@ class MigrationManager:
423
424
 
424
425
  async def _run_query(self, query: t.Union[DDL, Query, SchemaDDLBase]):
425
426
  """
426
- If MigrationManager is not in the preview mode,
427
- executes the queries. else, prints the query.
427
+ If MigrationManager is in preview mode then it just print the query
428
+ instead of executing it.
428
429
  """
429
430
  if self.preview:
430
431
  await self._print_query(query)
@@ -534,6 +535,39 @@ class MigrationManager:
534
535
 
535
536
  ###############################################################
536
537
 
538
+ on_delete = params.get("on_delete")
539
+ on_update = params.get("on_update")
540
+ if on_delete is not None or on_update is not None:
541
+ existing_table = await self.get_table_from_snapshot(
542
+ table_class_name=table_class_name,
543
+ app_name=self.app_name,
544
+ )
545
+
546
+ fk_column = existing_table._meta.get_column_by_name(
547
+ alter_column.column_name
548
+ )
549
+
550
+ assert isinstance(fk_column, ForeignKey)
551
+
552
+ # First drop the existing foreign key constraint
553
+ constraint_name = await get_fk_constraint_name(
554
+ column=fk_column
555
+ )
556
+ await self._run_query(
557
+ _Table.alter().drop_constraint(
558
+ constraint_name=constraint_name
559
+ )
560
+ )
561
+
562
+ # Then add a new foreign key constraint
563
+ await self._run_query(
564
+ _Table.alter().add_foreign_key_constraint(
565
+ column=fk_column,
566
+ on_delete=on_delete,
567
+ on_update=on_update,
568
+ )
569
+ )
570
+
537
571
  null = params.get("null")
538
572
  if null is not None:
539
573
  await self._run_query(
@@ -501,6 +501,8 @@ async def get_fk_triggers(
501
501
  Any Table subclass - just used to execute raw queries on the database.
502
502
 
503
503
  """
504
+ # TODO - Move this query to `piccolo.query.constraints` or use:
505
+ # `piccolo.query.constraints.referential_constraints`
504
506
  triggers = await table_class.raw(
505
507
  (
506
508
  "SELECT tc.constraint_name, "
@@ -537,23 +539,6 @@ async def get_fk_triggers(
537
539
  )
538
540
 
539
541
 
540
- ONDELETE_MAP = {
541
- "NO ACTION": OnDelete.no_action,
542
- "RESTRICT": OnDelete.restrict,
543
- "CASCADE": OnDelete.cascade,
544
- "SET NULL": OnDelete.set_null,
545
- "SET DEFAULT": OnDelete.set_default,
546
- }
547
-
548
- ONUPDATE_MAP = {
549
- "NO ACTION": OnUpdate.no_action,
550
- "RESTRICT": OnUpdate.restrict,
551
- "CASCADE": OnUpdate.cascade,
552
- "SET NULL": OnUpdate.set_null,
553
- "SET DEFAULT": OnUpdate.set_default,
554
- }
555
-
556
-
557
542
  async def get_constraints(
558
543
  table_class: t.Type[Table], tablename: str, schema_name: str = "public"
559
544
  ) -> TableConstraints:
@@ -765,8 +750,8 @@ async def create_table_class_from_db(
765
750
  column_name, constraint_table.name
766
751
  )
767
752
  if trigger:
768
- kwargs["on_update"] = ONUPDATE_MAP[trigger.on_update]
769
- kwargs["on_delete"] = ONDELETE_MAP[trigger.on_delete]
753
+ kwargs["on_update"] = OnUpdate(trigger.on_update)
754
+ kwargs["on_delete"] = OnDelete(trigger.on_delete)
770
755
  else:
771
756
  output_schema.trigger_warnings.append(
772
757
  f"{tablename}.{column_name}"
@@ -0,0 +1,92 @@
1
+ from dataclasses import dataclass
2
+
3
+ from piccolo.columns import ForeignKey
4
+ from piccolo.columns.base import OnDelete, OnUpdate
5
+
6
+
7
+ async def get_fk_constraint_name(column: ForeignKey) -> str:
8
+ """
9
+ Checks what the foreign key constraint is called in the database.
10
+ """
11
+
12
+ table = column._meta.table
13
+
14
+ if table._meta.db.engine_type == "sqlite":
15
+ # TODO - add the query for SQLite
16
+ raise ValueError("SQLite isn't currently supported.")
17
+
18
+ schema = table._meta.schema or "public"
19
+ table_name = table._meta.tablename
20
+ column_name = column._meta.db_column_name
21
+
22
+ constraints = await table.raw(
23
+ """
24
+ SELECT
25
+ kcu.constraint_name AS fk_constraint_name
26
+ FROM
27
+ information_schema.referential_constraints AS rc
28
+ INNER JOIN
29
+ information_schema.key_column_usage AS kcu
30
+ ON kcu.constraint_catalog = rc.constraint_catalog
31
+ AND kcu.constraint_schema = rc.constraint_schema
32
+ AND kcu.constraint_name = rc.constraint_name
33
+ WHERE
34
+ kcu.table_schema = {} AND
35
+ kcu.table_name = {} AND
36
+ kcu.column_name = {}
37
+ """,
38
+ schema,
39
+ table_name,
40
+ column_name,
41
+ )
42
+
43
+ return constraints[0]["fk_constraint_name"]
44
+
45
+
46
+ @dataclass
47
+ class ConstraintRules:
48
+ on_delete: OnDelete
49
+ on_update: OnUpdate
50
+
51
+
52
+ async def get_fk_constraint_rules(column: ForeignKey) -> ConstraintRules:
53
+ """
54
+ Checks the constraint rules for this foreign key in the database.
55
+ """
56
+ table = column._meta.table
57
+
58
+ if table._meta.db.engine_type == "sqlite":
59
+ # TODO - add the query for SQLite
60
+ raise ValueError("SQLite isn't currently supported.")
61
+
62
+ schema = table._meta.schema or "public"
63
+ table_name = table._meta.tablename
64
+ column_name = column._meta.db_column_name
65
+
66
+ constraints = await table.raw(
67
+ """
68
+ SELECT
69
+ kcu.constraint_name,
70
+ kcu.table_name,
71
+ kcu.column_name,
72
+ rc.update_rule,
73
+ rc.delete_rule
74
+ FROM
75
+ information_schema.key_column_usage AS kcu
76
+ INNER JOIN
77
+ information_schema.referential_constraints AS rc
78
+ ON kcu.constraint_name = rc.constraint_name
79
+ WHERE
80
+ kcu.table_schema = {} AND
81
+ kcu.table_name = {} AND
82
+ kcu.column_name = {}
83
+ """,
84
+ schema,
85
+ table_name,
86
+ column_name,
87
+ )
88
+
89
+ return ConstraintRules(
90
+ on_delete=OnDelete(constraints[0]["delete_rule"]),
91
+ on_update=OnUpdate(constraints[0]["update_rule"]),
92
+ )
@@ -36,6 +36,18 @@ class RenameTable(AlterStatement):
36
36
  return f"RENAME TO {self.new_name}"
37
37
 
38
38
 
39
+ @dataclass
40
+ class RenameConstraint(AlterStatement):
41
+ __slots__ = ("old_name", "new_name")
42
+
43
+ old_name: str
44
+ new_name: str
45
+
46
+ @property
47
+ def ddl(self) -> str:
48
+ return f"RENAME CONSTRAINT {self.old_name} TO {self.new_name}"
49
+
50
+
39
51
  @dataclass
40
52
  class AlterColumnStatement(AlterStatement):
41
53
  __slots__ = ("column",)
@@ -194,6 +206,7 @@ class AddForeignKeyConstraint(AlterStatement):
194
206
  "constraint_name",
195
207
  "foreign_key_column_name",
196
208
  "referenced_table_name",
209
+ "referenced_column_name",
197
210
  "on_delete",
198
211
  "on_update",
199
212
  )
@@ -201,9 +214,9 @@ class AddForeignKeyConstraint(AlterStatement):
201
214
  constraint_name: str
202
215
  foreign_key_column_name: str
203
216
  referenced_table_name: str
217
+ referenced_column_name: str
204
218
  on_delete: t.Optional[OnDelete]
205
219
  on_update: t.Optional[OnUpdate]
206
- referenced_column_name: str = "id"
207
220
 
208
221
  @property
209
222
  def ddl(self) -> str:
@@ -273,8 +286,8 @@ class DropTable:
273
286
 
274
287
  class Alter(DDL):
275
288
  __slots__ = (
276
- "_add_foreign_key_constraint",
277
289
  "_add",
290
+ "_add_foreign_key_constraint",
278
291
  "_drop_constraint",
279
292
  "_drop_default",
280
293
  "_drop_table",
@@ -288,6 +301,7 @@ class Alter(DDL):
288
301
  "_set_null",
289
302
  "_set_schema",
290
303
  "_set_unique",
304
+ "_rename_constraint",
291
305
  )
292
306
 
293
307
  def __init__(self, table: t.Type[Table], **kwargs):
@@ -307,6 +321,7 @@ class Alter(DDL):
307
321
  self._set_null: t.List[SetNull] = []
308
322
  self._set_schema: t.List[SetSchema] = []
309
323
  self._set_unique: t.List[SetUnique] = []
324
+ self._rename_constraint: t.List[RenameConstraint] = []
310
325
 
311
326
  def add_column(self: Self, name: str, column: Column) -> Self:
312
327
  """
@@ -372,6 +387,24 @@ class Alter(DDL):
372
387
  self._rename_table = [RenameTable(new_name=new_name)]
373
388
  return self
374
389
 
390
+ def rename_constraint(self, old_name: str, new_name: str) -> Alter:
391
+ """
392
+ Rename a constraint on the table::
393
+
394
+ >>> await Band.alter().rename_constraint(
395
+ ... 'old_constraint_name',
396
+ ... 'new_constraint_name',
397
+ ... )
398
+
399
+ """
400
+ self._rename_constraint = [
401
+ RenameConstraint(
402
+ old_name=old_name,
403
+ new_name=new_name,
404
+ )
405
+ ]
406
+ return self
407
+
375
408
  def rename_column(
376
409
  self, column: t.Union[str, Column], new_name: str
377
410
  ) -> Alter:
@@ -488,7 +521,7 @@ class Alter(DDL):
488
521
  def _get_constraint_name(self, column: t.Union[str, ForeignKey]) -> str:
489
522
  column_name = AlterColumnStatement(column=column).column_name
490
523
  tablename = self.table._meta.tablename
491
- return f"{tablename}_{column_name}_fk"
524
+ return f"{tablename}_{column_name}_fkey"
492
525
 
493
526
  def drop_constraint(self, constraint_name: str) -> Alter:
494
527
  self._drop_constraint.append(
@@ -500,37 +533,58 @@ class Alter(DDL):
500
533
  self, column: t.Union[str, ForeignKey]
501
534
  ) -> Alter:
502
535
  constraint_name = self._get_constraint_name(column=column)
503
- return self.drop_constraint(constraint_name=constraint_name)
536
+ self._drop_constraint.append(
537
+ DropConstraint(constraint_name=constraint_name)
538
+ )
539
+ return self
504
540
 
505
541
  def add_foreign_key_constraint(
506
542
  self,
507
543
  column: t.Union[str, ForeignKey],
508
- referenced_table_name: str,
544
+ referenced_table_name: t.Optional[str] = None,
545
+ referenced_column_name: t.Optional[str] = None,
546
+ constraint_name: t.Optional[str] = None,
509
547
  on_delete: t.Optional[OnDelete] = None,
510
548
  on_update: t.Optional[OnUpdate] = None,
511
- referenced_column_name: str = "id",
512
549
  ) -> Alter:
513
550
  """
514
551
  Add a new foreign key constraint::
515
552
 
516
553
  >>> await Band.alter().add_foreign_key_constraint(
517
554
  ... Band.manager,
518
- ... referenced_table_name='manager',
519
555
  ... on_delete=OnDelete.cascade
520
556
  ... )
521
557
 
522
558
  """
523
- constraint_name = self._get_constraint_name(column=column)
559
+ constraint_name = constraint_name or self._get_constraint_name(
560
+ column=column
561
+ )
524
562
  column_name = AlterColumnStatement(column=column).column_name
525
563
 
564
+ if referenced_column_name is None:
565
+ if isinstance(column, ForeignKey):
566
+ referenced_column_name = (
567
+ column._foreign_key_meta.resolved_target_column._meta.db_column_name # noqa: E501
568
+ )
569
+ else:
570
+ raise ValueError("Please pass in `referenced_column_name`.")
571
+
572
+ if referenced_table_name is None:
573
+ if isinstance(column, ForeignKey):
574
+ referenced_table_name = (
575
+ column._foreign_key_meta.resolved_references._meta.tablename # noqa: E501
576
+ )
577
+ else:
578
+ raise ValueError("Please pass in `referenced_table_name`.")
579
+
526
580
  self._add_foreign_key_constraint.append(
527
581
  AddForeignKeyConstraint(
528
582
  constraint_name=constraint_name,
529
583
  foreign_key_column_name=column_name,
530
584
  referenced_table_name=referenced_table_name,
585
+ referenced_column_name=referenced_column_name,
531
586
  on_delete=on_delete,
532
587
  on_update=on_update,
533
- referenced_column_name=referenced_column_name,
534
588
  )
535
589
  )
536
590
  return self
@@ -579,9 +633,12 @@ class Alter(DDL):
579
633
  i.ddl
580
634
  for i in itertools.chain(
581
635
  self._add,
636
+ self._add_foreign_key_constraint,
582
637
  self._rename_columns,
583
638
  self._rename_table,
639
+ self._rename_constraint,
584
640
  self._drop,
641
+ self._drop_constraint,
585
642
  self._drop_default,
586
643
  self._set_column_type,
587
644
  self._set_unique,
piccolo/utils/sync.py CHANGED
@@ -2,10 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import typing as t
5
- from concurrent.futures import ThreadPoolExecutor
5
+ from concurrent.futures import Future, ThreadPoolExecutor
6
6
 
7
+ ReturnType = t.TypeVar("ReturnType")
7
8
 
8
- def run_sync(coroutine: t.Coroutine):
9
+
10
+ def run_sync(
11
+ coroutine: t.Coroutine[t.Any, t.Any, ReturnType],
12
+ ) -> ReturnType:
9
13
  """
10
14
  Run the coroutine synchronously - trying to accommodate as many edge cases
11
15
  as possible.
@@ -20,5 +24,5 @@ def run_sync(coroutine: t.Coroutine):
20
24
  except RuntimeError:
21
25
  # An event loop already exists.
22
26
  with ThreadPoolExecutor(max_workers=1) as executor:
23
- future = executor.submit(asyncio.run, coroutine)
27
+ future: Future = executor.submit(asyncio.run, coroutine)
24
28
  return future.result()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piccolo
3
- Version: 1.25.0
3
+ Version: 1.26.0
4
4
  Summary: A fast, user friendly ORM and query builder which supports asyncio.
5
5
  Home-page: https://github.com/piccolo-orm/piccolo
6
6
  Author: Daniel Townsend
@@ -1,4 +1,4 @@
1
- piccolo/__init__.py,sha256=BvmUclKrZZEa9RXkZ0XNwqYKihj1aFvSDrl7jJkQmPc,23
1
+ piccolo/__init__.py,sha256=ZVRfjDuz3xhQA4e78KxBcezkGjqcWBkBGlP0bm-NFD8,23
2
2
  piccolo/custom_types.py,sha256=7HMQAze-5mieNLfbQ5QgbRQgR2abR7ol0qehv2SqROY,604
3
3
  piccolo/main.py,sha256=1VsFV67FWTUikPTysp64Fmgd9QBVa_9wcwKfwj2UCEA,5117
4
4
  piccolo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -66,7 +66,7 @@ piccolo/apps/migrations/piccolo_app.py,sha256=1EcS2ComBPCaMCC2C3WaPR_GqLwt3XiIJN
66
66
  piccolo/apps/migrations/tables.py,sha256=jqBnK-Rk545v1Eu6GaLHTVz7-uwBTUnz2m58OA-mxTc,799
67
67
  piccolo/apps/migrations/auto/__init__.py,sha256=eYb1rZQaalumv_bhbcEe6x3dUglmpFtw7Egg6k7597U,316
68
68
  piccolo/apps/migrations/auto/diffable_table.py,sha256=1HdqGeWFUYVJ2cJg6DZWOCh67SbgCxFVc554uD7N71A,7405
69
- piccolo/apps/migrations/auto/migration_manager.py,sha256=tyAE1Xk6Xb58igbuHw3FGHhKkVjv_Vr5qBH4AbvVb8k,35856
69
+ piccolo/apps/migrations/auto/migration_manager.py,sha256=5M2_01_FsZj6zIGXLEwkyEAPF2e-ICNB2_ZcBWwifCQ,37267
70
70
  piccolo/apps/migrations/auto/operations.py,sha256=169IrCLR3FtTRxHsEHNg6dTG45lcEM7Aoyy3SwgX_hU,1329
71
71
  piccolo/apps/migrations/auto/schema_differ.py,sha256=VA1rK-_wNSdyZZgfA3ZOlpVGJCcvLyouKtT9k2YKhiA,26266
72
72
  piccolo/apps/migrations/auto/schema_snapshot.py,sha256=ZqUg4NpChOeoACKF2gkhqsz1BW3wOWFnzJCccq-CNNQ,4719
@@ -93,7 +93,7 @@ piccolo/apps/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
93
93
  piccolo/apps/schema/piccolo_app.py,sha256=De9eujzB6zWsP6J1gHYUk_f5_DpjvTZVXJsQ3eXBgnA,432
94
94
  piccolo/apps/schema/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
95
  piccolo/apps/schema/commands/exceptions.py,sha256=ZOGL3iV-xtWbWsImXObrXNaKtNPY_Qk1OmaOMOV6Ps0,236
96
- piccolo/apps/schema/commands/generate.py,sha256=_niW_UxZ-x29-xjDIVdjHdDyKAaXn4HUDVtVjsXgBD0,30700
96
+ piccolo/apps/schema/commands/generate.py,sha256=xYRz9P4cdahSHzAIiXhXBzg6eUqV0WB2THSRZH7uPhU,30415
97
97
  piccolo/apps/schema/commands/graph.py,sha256=FuQUPavUXpr-Y_11XRr11DbVLsgK8uG0IN8uBZIe5G4,3190
98
98
  piccolo/apps/schema/commands/templates/graphviz.dot.jinja,sha256=-legygtsueOC70aboX35ZJpbCAXcv3E8RXXvFDQTeIY,1443
99
99
  piccolo/apps/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -153,6 +153,7 @@ piccolo/engine/postgres.py,sha256=DekL3KafCdzSAEQ6_EgOiUB1ERXh2xpePYwI9QvmN-c,18
153
153
  piccolo/engine/sqlite.py,sha256=Oe0GBrIUSUkutvk5LoXGWC6HFQzKeusfql5-NMssH_s,25735
154
154
  piccolo/query/__init__.py,sha256=bcsMV4813rMRAIqGv4DxI4eyO4FmpXkDv9dfTk5pt3A,699
155
155
  piccolo/query/base.py,sha256=sO5VyicbWjgYaQukr6jqUqUUrOctL6QJ1MjcsgDKHXM,14912
156
+ piccolo/query/constraints.py,sha256=menFcqLKSM4697OSvMRZPsTgxHfR9GTvZZMrGDk2PrA,2601
156
157
  piccolo/query/mixins.py,sha256=X9HEYnj6uOjgTkGr4vgqTwN_dokJPzVagwbFx385atQ,24468
157
158
  piccolo/query/proxy.py,sha256=Yq4jNc7IWJvdeO3u7_7iPyRy2WhVj8KsIUcIYHBIi9Q,1839
158
159
  piccolo/query/functions/__init__.py,sha256=pZkzOIh7Sg9HPNOeegOwAS46Oxt31ATlSVmwn-lxCbc,605
@@ -163,7 +164,7 @@ piccolo/query/functions/math.py,sha256=2Wapq0lpXZh77z0uzXUhnOfmUkbkM0xjQ4tiyuCsb
163
164
  piccolo/query/functions/string.py,sha256=X3g_4qomJJCkYOcKcK-zZEqC6qJBrS4VTogPp9Xw4Cs,2506
164
165
  piccolo/query/functions/type_conversion.py,sha256=OYbZc6TEk6b5yTwCMw2rmZ-UiQiUiWZOyxwMLzUjXwE,2583
165
166
  piccolo/query/methods/__init__.py,sha256=tm4gLeV_obDqpgnouVjFbGubbaoJcqm_cbNd4LPo48Q,622
166
- piccolo/query/methods/alter.py,sha256=AI9YkJeip2EitrWJN_TDExXhA8HGAG3XuDz1NR-KirQ,16728
167
+ piccolo/query/methods/alter.py,sha256=tL8IIuEldBwegQOZM5N4IaoO2fH0tYLk8R7ra6PLD3s,18519
167
168
  piccolo/query/methods/count.py,sha256=Vxn_7Ry-rleC6OGRxh-cLbuEMsy1DNjAZJThGED-_do,1748
168
169
  piccolo/query/methods/create.py,sha256=hJ-6VVsWczzKDH6fQRN1WmYhcitixuXJ-eNOuCo_JgM,2742
169
170
  piccolo/query/methods/create_index.py,sha256=gip_cRXZkLfpJqCL7KHk2l_7HLptoa-Ae8qu6I5d5c8,2224
@@ -195,11 +196,11 @@ piccolo/utils/printing.py,sha256=5VWNSfOrIGPh1VM-7fd4K18RGCYk0FQ5o-D4aLhzXZE,174
195
196
  piccolo/utils/pydantic.py,sha256=RhoQZ7ddmFmepVcslHXMqmynbSVch7XLKUSgJkLuQS0,12327
196
197
  piccolo/utils/repr.py,sha256=K3w-TAP9WPx8tbAIB2XDab_C4PHsPrB9TzwWfOHa4cc,787
197
198
  piccolo/utils/sql_values.py,sha256=pGXmVTw6pWr8q7QA4xs7NiKSwjBzhN--3HXVjQv2SQQ,1749
198
- piccolo/utils/sync.py,sha256=j9Abkxn5HHS6HyvfpMzb1zV_teTkFHVhaIxu9rrSwSU,819
199
+ piccolo/utils/sync.py,sha256=irmdTsYtURZNEBmIP6i2v2PyjgE-gK4t6VSxnBZ75Qo,920
199
200
  piccolo/utils/warnings.py,sha256=ONrurw3HVCClUuHnpenMjg45dcFesrXqMgG9ifgP4_8,1247
200
201
  piccolo/utils/graphlib/__init__.py,sha256=SUJ5Yh7LiRun3nkBuLUSVmGNHF6fANrxSoYan0mtYB0,200
201
202
  piccolo/utils/graphlib/_graphlib.py,sha256=9FNGDSmTIEAk86FktniCe_J2yXjSE_sRZHDBAJJAUOw,9677
202
- piccolo-1.25.0.dist-info/licenses/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
203
+ piccolo-1.26.0.dist-info/licenses/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
203
204
  profiling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
204
205
  profiling/run_profile.py,sha256=264qsSFu93NTpExePnKQ9GkcN5fiuRBQ72WOSt0ZHck,829
205
206
  tests/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -221,7 +222,7 @@ tests/apps/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
221
222
  tests/apps/migrations/test_migration.py,sha256=JmPLtf2BCWX3Yofe0GQe40m8I_yWa_-3vk1lDfFDfIo,308
222
223
  tests/apps/migrations/auto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
223
224
  tests/apps/migrations/auto/test_diffable_table.py,sha256=p0cKDkfhmu96-rB9bonOlg5bmfQ7U9S2kRppOt4YxyU,3338
224
- tests/apps/migrations/auto/test_migration_manager.py,sha256=L2raSuhZybRyK-BEh-G-nDndAgl4sLynekHQFqyrteY,35719
225
+ tests/apps/migrations/auto/test_migration_manager.py,sha256=qzYHC8nKm4pRtsgIyPDSZdXEWLOl2UDgTv6Mv8QO49k,37362
225
226
  tests/apps/migrations/auto/test_schema_differ.py,sha256=UdsaZisA02j15wr1bXkXD6Cqu3p0A23NwFQLXsJdQL4,19391
226
227
  tests/apps/migrations/auto/test_schema_snapshot.py,sha256=ZyvGZqn3N3cwd-3S-FME5AJ8buDSHesw7yPIvY6mE5k,6196
227
228
  tests/apps/migrations/auto/test_serialisation.py,sha256=EFkhES1w9h51UCamWrhxs3mf4I718ggeP7Yl5J_UID4,13548
@@ -380,8 +381,8 @@ tests/utils/test_sql_values.py,sha256=vzxRmy16FfLZPH-sAQexBvsF9MXB8n4smr14qoEOS5
380
381
  tests/utils/test_sync.py,sha256=9ytVo56y2vPQePvTeIi9lHIouEhWJbodl1TmzkGFrSo,799
381
382
  tests/utils/test_table_reflection.py,sha256=SIzuat-IpcVj1GCFyOWKShI8YkhdOPPFH7qVrvfyPNE,3794
382
383
  tests/utils/test_warnings.py,sha256=NvSC_cvJ6uZcwAGf1m-hLzETXCqprXELL8zg3TNLVMw,269
383
- piccolo-1.25.0.dist-info/METADATA,sha256=zdbTbYmUa-EhWuXfpt53Hr1RBENaNyiXBk80Ii_bFK8,5531
384
- piccolo-1.25.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
385
- piccolo-1.25.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
386
- piccolo-1.25.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
387
- piccolo-1.25.0.dist-info/RECORD,,
384
+ piccolo-1.26.0.dist-info/METADATA,sha256=UE7oErMiVueu8PQJLKAzHWoVIaXJtA2r9d1VZsOzwbU,5531
385
+ piccolo-1.26.0.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
386
+ piccolo-1.26.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
387
+ piccolo-1.26.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
388
+ piccolo-1.26.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -12,8 +12,10 @@ from piccolo.columns.base import OnDelete, OnUpdate
12
12
  from piccolo.columns.column_types import ForeignKey
13
13
  from piccolo.conf.apps import AppConfig
14
14
  from piccolo.engine import engine_finder
15
+ from piccolo.query.constraints import get_fk_constraint_rules
15
16
  from piccolo.table import Table, sort_table_classes
16
17
  from piccolo.utils.lazy_loader import LazyLoader
18
+ from piccolo.utils.sync import run_sync
17
19
  from tests.base import AsyncMock, DBTestCase, engine_is, engines_only
18
20
  from tests.example_apps.music.tables import Band, Concert, Manager, Venue
19
21
 
@@ -618,6 +620,54 @@ class TestMigrationManager(DBTestCase):
618
620
  response = self.run_sync("SELECT * FROM manager;")
619
621
  self.assertEqual(response, [{"id": id[0]["id"], "name": "Dave"}])
620
622
 
623
+ @engines_only("postgres", "cockroach")
624
+ def test_alter_fk_on_delete_on_update(self):
625
+ """
626
+ Test altering OnDelete and OnUpdate with MigrationManager.
627
+ """
628
+ # before performing migrations - OnDelete.no_action
629
+ self.assertEqual(
630
+ run_sync(get_fk_constraint_rules(column=Band.manager)).on_delete,
631
+ OnDelete.no_action,
632
+ )
633
+
634
+ manager = MigrationManager(app_name="music")
635
+ manager.alter_column(
636
+ table_class_name="Band",
637
+ tablename="band",
638
+ column_name="manager",
639
+ db_column_name="manager",
640
+ params={
641
+ "on_delete": OnDelete.set_null,
642
+ "on_update": OnUpdate.set_null,
643
+ },
644
+ old_params={
645
+ "on_delete": OnDelete.no_action,
646
+ "on_update": OnUpdate.no_action,
647
+ },
648
+ column_class=ForeignKey,
649
+ old_column_class=ForeignKey,
650
+ schema=None,
651
+ )
652
+
653
+ asyncio.run(manager.run())
654
+
655
+ # after performing migrations - OnDelete.set_null
656
+ self.assertEqual(
657
+ run_sync(get_fk_constraint_rules(column=Band.manager)).on_delete,
658
+ OnDelete.set_null,
659
+ )
660
+
661
+ # Reverse
662
+ asyncio.run(manager.run(backwards=True))
663
+
664
+ # after performing reverse migrations we have
665
+ # OnDelete.no_action again
666
+ self.assertEqual(
667
+ run_sync(get_fk_constraint_rules(column=Band.manager)).on_delete,
668
+ OnDelete.no_action,
669
+ )
670
+
621
671
  @engines_only("postgres")
622
672
  def test_alter_column_unique(self):
623
673
  """