plain.models 0.49.2__py3-none-any.whl → 0.50.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.
- plain/models/CHANGELOG.md +13 -0
- plain/models/aggregates.py +42 -19
- plain/models/backends/base/base.py +125 -105
- plain/models/backends/base/client.py +11 -3
- plain/models/backends/base/creation.py +22 -12
- plain/models/backends/base/features.py +10 -4
- plain/models/backends/base/introspection.py +29 -16
- plain/models/backends/base/operations.py +187 -91
- plain/models/backends/base/schema.py +267 -165
- plain/models/backends/base/validation.py +12 -3
- plain/models/backends/ddl_references.py +85 -43
- plain/models/backends/mysql/base.py +29 -26
- plain/models/backends/mysql/client.py +7 -2
- plain/models/backends/mysql/compiler.py +12 -3
- plain/models/backends/mysql/creation.py +5 -2
- plain/models/backends/mysql/features.py +24 -22
- plain/models/backends/mysql/introspection.py +22 -13
- plain/models/backends/mysql/operations.py +106 -39
- plain/models/backends/mysql/schema.py +48 -24
- plain/models/backends/mysql/validation.py +13 -6
- plain/models/backends/postgresql/base.py +41 -34
- plain/models/backends/postgresql/client.py +7 -2
- plain/models/backends/postgresql/creation.py +10 -5
- plain/models/backends/postgresql/introspection.py +15 -8
- plain/models/backends/postgresql/operations.py +109 -42
- plain/models/backends/postgresql/schema.py +85 -46
- plain/models/backends/sqlite3/_functions.py +151 -115
- plain/models/backends/sqlite3/base.py +37 -23
- plain/models/backends/sqlite3/client.py +7 -1
- plain/models/backends/sqlite3/creation.py +9 -5
- plain/models/backends/sqlite3/features.py +5 -3
- plain/models/backends/sqlite3/introspection.py +32 -16
- plain/models/backends/sqlite3/operations.py +125 -42
- plain/models/backends/sqlite3/schema.py +82 -58
- plain/models/backends/utils.py +52 -29
- plain/models/backups/cli.py +8 -6
- plain/models/backups/clients.py +16 -7
- plain/models/backups/core.py +24 -13
- plain/models/base.py +113 -74
- plain/models/cli.py +94 -63
- plain/models/config.py +1 -1
- plain/models/connections.py +23 -7
- plain/models/constraints.py +65 -47
- plain/models/database_url.py +1 -1
- plain/models/db.py +6 -2
- plain/models/deletion.py +66 -43
- plain/models/entrypoints.py +1 -1
- plain/models/enums.py +22 -11
- plain/models/exceptions.py +23 -8
- plain/models/expressions.py +440 -257
- plain/models/fields/__init__.py +253 -202
- plain/models/fields/json.py +120 -54
- plain/models/fields/mixins.py +12 -8
- plain/models/fields/related.py +284 -252
- plain/models/fields/related_descriptors.py +31 -22
- plain/models/fields/related_lookups.py +23 -11
- plain/models/fields/related_managers.py +81 -47
- plain/models/fields/reverse_related.py +58 -55
- plain/models/forms.py +89 -63
- plain/models/functions/comparison.py +71 -18
- plain/models/functions/datetime.py +79 -29
- plain/models/functions/math.py +43 -10
- plain/models/functions/mixins.py +24 -7
- plain/models/functions/text.py +104 -25
- plain/models/functions/window.py +12 -6
- plain/models/indexes.py +52 -28
- plain/models/lookups.py +228 -153
- plain/models/migrations/autodetector.py +86 -43
- plain/models/migrations/exceptions.py +7 -3
- plain/models/migrations/executor.py +33 -7
- plain/models/migrations/graph.py +79 -50
- plain/models/migrations/loader.py +45 -22
- plain/models/migrations/migration.py +23 -18
- plain/models/migrations/operations/base.py +37 -19
- plain/models/migrations/operations/fields.py +89 -42
- plain/models/migrations/operations/models.py +245 -143
- plain/models/migrations/operations/special.py +82 -25
- plain/models/migrations/optimizer.py +7 -2
- plain/models/migrations/questioner.py +58 -31
- plain/models/migrations/recorder.py +18 -11
- plain/models/migrations/serializer.py +50 -39
- plain/models/migrations/state.py +220 -133
- plain/models/migrations/utils.py +29 -13
- plain/models/migrations/writer.py +17 -14
- plain/models/options.py +63 -56
- plain/models/otel.py +16 -6
- plain/models/preflight.py +35 -12
- plain/models/query.py +323 -228
- plain/models/query_utils.py +93 -58
- plain/models/registry.py +34 -16
- plain/models/sql/compiler.py +146 -97
- plain/models/sql/datastructures.py +38 -25
- plain/models/sql/query.py +255 -169
- plain/models/sql/subqueries.py +32 -21
- plain/models/sql/where.py +54 -29
- plain/models/test/pytest.py +15 -11
- plain/models/test/utils.py +4 -2
- plain/models/transaction.py +20 -7
- plain/models/utils.py +13 -5
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/METADATA +1 -1
- plain_models-0.50.0.dist-info/RECORD +122 -0
- plain_models-0.49.2.dist-info/RECORD +0 -122
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/WHEEL +0 -0
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.49.2.dist-info → plain_models-0.50.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,10 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import logging
|
2
4
|
import operator
|
5
|
+
from collections.abc import Generator
|
3
6
|
from datetime import datetime
|
7
|
+
from typing import TYPE_CHECKING, Any
|
4
8
|
|
5
9
|
from plain.models.backends.ddl_references import (
|
6
10
|
Columns,
|
@@ -17,10 +21,18 @@ from plain.models.sql import Query
|
|
17
21
|
from plain.models.transaction import TransactionManagementError, atomic
|
18
22
|
from plain.utils import timezone
|
19
23
|
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from collections.abc import Iterable
|
26
|
+
|
27
|
+
from plain.models.backends.base.base import BaseDatabaseWrapper
|
28
|
+
from plain.models.base import Model
|
29
|
+
from plain.models.constraints import BaseConstraint
|
30
|
+
from plain.models.fields import Field
|
31
|
+
|
20
32
|
logger = logging.getLogger("plain.models.backends.schema")
|
21
33
|
|
22
34
|
|
23
|
-
def _is_relevant_relation(relation, altered_field):
|
35
|
+
def _is_relevant_relation(relation: Any, altered_field: Field) -> bool:
|
24
36
|
"""
|
25
37
|
When altering the given field, must constraints on its model from the given
|
26
38
|
relation be temporarily dropped?
|
@@ -36,10 +48,10 @@ def _is_relevant_relation(relation, altered_field):
|
|
36
48
|
return altered_field.name == "id"
|
37
49
|
|
38
50
|
|
39
|
-
def _all_related_fields(model):
|
51
|
+
def _all_related_fields(model: type[Model]) -> list[Any]:
|
40
52
|
# Related fields must be returned in a deterministic order.
|
41
53
|
return sorted(
|
42
|
-
model._meta._get_fields(
|
54
|
+
model._meta._get_fields( # type: ignore[attr-defined]
|
43
55
|
forward=False,
|
44
56
|
reverse=True,
|
45
57
|
include_hidden=True,
|
@@ -48,7 +60,9 @@ def _all_related_fields(model):
|
|
48
60
|
)
|
49
61
|
|
50
62
|
|
51
|
-
def _related_non_m2m_objects(
|
63
|
+
def _related_non_m2m_objects(
|
64
|
+
old_field: Field, new_field: Field
|
65
|
+
) -> Generator[tuple[Any, Any], None, None]:
|
52
66
|
# Filter out m2m objects from reverse relations.
|
53
67
|
# Return (old_relation, new_relation) tuples.
|
54
68
|
related_fields = zip(
|
@@ -140,23 +154,28 @@ class BaseDatabaseSchemaEditor:
|
|
140
154
|
sql_alter_table_comment = "COMMENT ON TABLE %(table)s IS %(comment)s"
|
141
155
|
sql_alter_column_comment = "COMMENT ON COLUMN %(table)s.%(column)s IS %(comment)s"
|
142
156
|
|
143
|
-
def __init__(
|
157
|
+
def __init__(
|
158
|
+
self,
|
159
|
+
connection: BaseDatabaseWrapper,
|
160
|
+
collect_sql: bool = False,
|
161
|
+
atomic: bool = True,
|
162
|
+
):
|
144
163
|
self.connection = connection
|
145
164
|
self.collect_sql = collect_sql
|
146
165
|
if self.collect_sql:
|
147
|
-
self.collected_sql = []
|
166
|
+
self.collected_sql: list[str] = []
|
148
167
|
self.atomic_migration = self.connection.features.can_rollback_ddl and atomic
|
149
168
|
|
150
169
|
# State-managing methods
|
151
170
|
|
152
|
-
def __enter__(self):
|
153
|
-
self.deferred_sql = []
|
171
|
+
def __enter__(self) -> BaseDatabaseSchemaEditor:
|
172
|
+
self.deferred_sql: list[Any] = []
|
154
173
|
if self.atomic_migration:
|
155
174
|
self.atomic = atomic()
|
156
175
|
self.atomic.__enter__()
|
157
176
|
return self
|
158
177
|
|
159
|
-
def __exit__(self, exc_type, exc_value, traceback):
|
178
|
+
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
160
179
|
if exc_type is None:
|
161
180
|
for sql in self.deferred_sql:
|
162
181
|
self.execute(sql)
|
@@ -165,7 +184,9 @@ class BaseDatabaseSchemaEditor:
|
|
165
184
|
|
166
185
|
# Core utility functions
|
167
186
|
|
168
|
-
def execute(
|
187
|
+
def execute(
|
188
|
+
self, sql: str | Statement, params: tuple[Any, ...] | list[Any] | None = ()
|
189
|
+
) -> None:
|
169
190
|
"""Execute the given SQL statement, with optional parameters."""
|
170
191
|
# Don't perform the transactional DDL check if SQL is being collected
|
171
192
|
# as it's not going to be executed anyway.
|
@@ -196,10 +217,10 @@ class BaseDatabaseSchemaEditor:
|
|
196
217
|
with self.connection.cursor() as cursor:
|
197
218
|
cursor.execute(sql, params)
|
198
219
|
|
199
|
-
def quote_name(self, name):
|
220
|
+
def quote_name(self, name: str) -> str:
|
200
221
|
return self.connection.ops.quote_name(name)
|
201
222
|
|
202
|
-
def table_sql(self, model):
|
223
|
+
def table_sql(self, model: type[Model]) -> tuple[str, list[Any]]:
|
203
224
|
"""Take a model and return its table definition."""
|
204
225
|
# Create column SQL, add FK deferreds if needed.
|
205
226
|
column_sqls = []
|
@@ -219,7 +240,7 @@ class BaseDatabaseSchemaEditor:
|
|
219
240
|
definition += f" {col_type_suffix}"
|
220
241
|
params.extend(extra_params)
|
221
242
|
# FK.
|
222
|
-
if field.remote_field and field.db_constraint:
|
243
|
+
if field.remote_field and field.db_constraint: # type: ignore[attr-defined]
|
223
244
|
to_table = field.remote_field.model._meta.db_table
|
224
245
|
to_column = field.remote_field.model._meta.get_field(
|
225
246
|
field.remote_field.field_name
|
@@ -262,8 +283,14 @@ class BaseDatabaseSchemaEditor:
|
|
262
283
|
# Field <-> database mapping functions
|
263
284
|
|
264
285
|
def _iter_column_sql(
|
265
|
-
self,
|
266
|
-
|
286
|
+
self,
|
287
|
+
column_db_type: str,
|
288
|
+
params: list[Any],
|
289
|
+
model: type[Model],
|
290
|
+
field: Field,
|
291
|
+
field_db_params: dict[str, Any],
|
292
|
+
include_default: bool,
|
293
|
+
) -> Generator[str, None, None]:
|
267
294
|
yield column_db_type
|
268
295
|
if collation := field_db_params.get("collation"):
|
269
296
|
yield self._collate_sql(collation)
|
@@ -302,7 +329,9 @@ class BaseDatabaseSchemaEditor:
|
|
302
329
|
if field.primary_key:
|
303
330
|
yield "PRIMARY KEY"
|
304
331
|
|
305
|
-
def column_sql(
|
332
|
+
def column_sql(
|
333
|
+
self, model: type[Model], field: Field, include_default: bool = False
|
334
|
+
) -> tuple[str | None, list[Any] | None]:
|
306
335
|
"""
|
307
336
|
Return the column definition for a field. The field must already have
|
308
337
|
had set_attributes_from_name() called.
|
@@ -313,7 +342,7 @@ class BaseDatabaseSchemaEditor:
|
|
313
342
|
# Check for fields that aren't actually columns (e.g. M2M).
|
314
343
|
if column_db_type is None:
|
315
344
|
return None, None
|
316
|
-
params = []
|
345
|
+
params: list[Any] = []
|
317
346
|
return (
|
318
347
|
" ".join(
|
319
348
|
# This appends to the params being returned.
|
@@ -329,21 +358,21 @@ class BaseDatabaseSchemaEditor:
|
|
329
358
|
params,
|
330
359
|
)
|
331
360
|
|
332
|
-
def skip_default(self, field):
|
361
|
+
def skip_default(self, field: Field) -> bool:
|
333
362
|
"""
|
334
363
|
Some backends don't accept default values for certain columns types
|
335
364
|
(i.e. MySQL longtext and longblob).
|
336
365
|
"""
|
337
366
|
return False
|
338
367
|
|
339
|
-
def skip_default_on_alter(self, field):
|
368
|
+
def skip_default_on_alter(self, field: Field) -> bool:
|
340
369
|
"""
|
341
370
|
Some backends don't accept default values for certain columns types
|
342
371
|
(i.e. MySQL longtext and longblob) in the ALTER COLUMN statement.
|
343
372
|
"""
|
344
373
|
return False
|
345
374
|
|
346
|
-
def prepare_default(self, value):
|
375
|
+
def prepare_default(self, value: Any) -> str:
|
347
376
|
"""
|
348
377
|
Only used for backends which have requires_literal_defaults feature
|
349
378
|
"""
|
@@ -352,7 +381,7 @@ class BaseDatabaseSchemaEditor:
|
|
352
381
|
"requires_literal_defaults must provide a prepare_default() method"
|
353
382
|
)
|
354
383
|
|
355
|
-
def _column_default_sql(self, field):
|
384
|
+
def _column_default_sql(self, field: Field) -> str:
|
356
385
|
"""
|
357
386
|
Return the SQL to use in a DEFAULT clause. The resulting string should
|
358
387
|
contain a '%s' placeholder for a default value.
|
@@ -360,7 +389,7 @@ class BaseDatabaseSchemaEditor:
|
|
360
389
|
return "%s"
|
361
390
|
|
362
391
|
@staticmethod
|
363
|
-
def _effective_default(field):
|
392
|
+
def _effective_default(field: Field) -> Any:
|
364
393
|
# This method allows testing its logic without a connection.
|
365
394
|
if field.has_default():
|
366
395
|
default = field.get_default()
|
@@ -385,11 +414,11 @@ class BaseDatabaseSchemaEditor:
|
|
385
414
|
default = None
|
386
415
|
return default
|
387
416
|
|
388
|
-
def effective_default(self, field):
|
417
|
+
def effective_default(self, field: Field) -> Any:
|
389
418
|
"""Return a field's effective database default value."""
|
390
419
|
return field.get_db_prep_save(self._effective_default(field), self.connection)
|
391
420
|
|
392
|
-
def quote_value(self, value):
|
421
|
+
def quote_value(self, value: Any) -> str:
|
393
422
|
"""
|
394
423
|
Return a quoted version of the value so it's safe to use in an SQL
|
395
424
|
string. This is not safe against injection from user code; it is
|
@@ -401,7 +430,7 @@ class BaseDatabaseSchemaEditor:
|
|
401
430
|
|
402
431
|
# Actions
|
403
432
|
|
404
|
-
def create_model(self, model):
|
433
|
+
def create_model(self, model: type[Model]) -> None:
|
405
434
|
"""
|
406
435
|
Create a table and any accompanying indexes or unique constraints for
|
407
436
|
the given `model`.
|
@@ -431,7 +460,7 @@ class BaseDatabaseSchemaEditor:
|
|
431
460
|
# Add any field index (deferred as SQLite _remake_table needs it).
|
432
461
|
self.deferred_sql.extend(self._model_indexes_sql(model))
|
433
462
|
|
434
|
-
def delete_model(self, model):
|
463
|
+
def delete_model(self, model: type[Model]) -> None:
|
435
464
|
"""Delete a model from the database."""
|
436
465
|
|
437
466
|
# Delete the table
|
@@ -448,7 +477,7 @@ class BaseDatabaseSchemaEditor:
|
|
448
477
|
):
|
449
478
|
self.deferred_sql.remove(sql)
|
450
479
|
|
451
|
-
def add_index(self, model, index):
|
480
|
+
def add_index(self, model: type[Model], index: Index) -> None:
|
452
481
|
"""Add an index on a model."""
|
453
482
|
if (
|
454
483
|
index.contains_expressions
|
@@ -459,7 +488,7 @@ class BaseDatabaseSchemaEditor:
|
|
459
488
|
# necessity to avoid escaping attempts on execution.
|
460
489
|
self.execute(index.create_sql(model, self), params=None)
|
461
490
|
|
462
|
-
def remove_index(self, model, index):
|
491
|
+
def remove_index(self, model: type[Model], index: Index) -> None:
|
463
492
|
"""Remove an index from a model."""
|
464
493
|
if (
|
465
494
|
index.contains_expressions
|
@@ -468,7 +497,9 @@ class BaseDatabaseSchemaEditor:
|
|
468
497
|
return None
|
469
498
|
self.execute(index.remove_sql(model, self))
|
470
499
|
|
471
|
-
def rename_index(
|
500
|
+
def rename_index(
|
501
|
+
self, model: type[Model], old_index: Index, new_index: Index
|
502
|
+
) -> None:
|
472
503
|
if self.connection.features.can_rename_index:
|
473
504
|
self.execute(
|
474
505
|
self._rename_index_sql(model, old_index.name, new_index.name),
|
@@ -478,7 +509,7 @@ class BaseDatabaseSchemaEditor:
|
|
478
509
|
self.remove_index(model, old_index)
|
479
510
|
self.add_index(model, new_index)
|
480
511
|
|
481
|
-
def add_constraint(self, model, constraint):
|
512
|
+
def add_constraint(self, model: type[Model], constraint: BaseConstraint) -> None:
|
482
513
|
"""Add a constraint to a model."""
|
483
514
|
sql = constraint.create_sql(model, self)
|
484
515
|
if sql:
|
@@ -486,13 +517,15 @@ class BaseDatabaseSchemaEditor:
|
|
486
517
|
# params=None a necessity to avoid escaping attempts on execution.
|
487
518
|
self.execute(sql, params=None)
|
488
519
|
|
489
|
-
def remove_constraint(self, model, constraint):
|
520
|
+
def remove_constraint(self, model: type[Model], constraint: BaseConstraint) -> None:
|
490
521
|
"""Remove a constraint from a model."""
|
491
522
|
sql = constraint.remove_sql(model, self)
|
492
523
|
if sql:
|
493
524
|
self.execute(sql)
|
494
525
|
|
495
|
-
def alter_db_table(
|
526
|
+
def alter_db_table(
|
527
|
+
self, model: type[Model], old_db_table: str, new_db_table: str
|
528
|
+
) -> None:
|
496
529
|
"""Rename the table a model points to."""
|
497
530
|
if old_db_table == new_db_table or (
|
498
531
|
self.connection.features.ignores_table_name_case
|
@@ -511,7 +544,12 @@ class BaseDatabaseSchemaEditor:
|
|
511
544
|
if isinstance(sql, Statement):
|
512
545
|
sql.rename_table_references(old_db_table, new_db_table)
|
513
546
|
|
514
|
-
def alter_db_table_comment(
|
547
|
+
def alter_db_table_comment(
|
548
|
+
self,
|
549
|
+
model: type[Model],
|
550
|
+
old_db_table_comment: str | None,
|
551
|
+
new_db_table_comment: str | None,
|
552
|
+
) -> None:
|
515
553
|
self.execute(
|
516
554
|
self.sql_alter_table_comment
|
517
555
|
% {
|
@@ -520,7 +558,7 @@ class BaseDatabaseSchemaEditor:
|
|
520
558
|
}
|
521
559
|
)
|
522
560
|
|
523
|
-
def add_field(self, model, field):
|
561
|
+
def add_field(self, model: type[Model], field: Field) -> None:
|
524
562
|
"""
|
525
563
|
Create a field on a model. Usually involves adding a column, but may
|
526
564
|
involve adding a table instead (for M2M fields).
|
@@ -539,7 +577,7 @@ class BaseDatabaseSchemaEditor:
|
|
539
577
|
if (
|
540
578
|
field.remote_field
|
541
579
|
and self.connection.features.supports_foreign_keys
|
542
|
-
and field.db_constraint
|
580
|
+
and field.db_constraint # type: ignore[attr-defined]
|
543
581
|
):
|
544
582
|
constraint_suffix = "_fk_%(to_table)s_%(to_column)s"
|
545
583
|
# Add FK constraint inline, if supported.
|
@@ -601,7 +639,7 @@ class BaseDatabaseSchemaEditor:
|
|
601
639
|
if self.connection.features.connection_persists_old_columns:
|
602
640
|
self.connection.close()
|
603
641
|
|
604
|
-
def remove_field(self, model, field):
|
642
|
+
def remove_field(self, model: type[Model], field: Field) -> None:
|
605
643
|
"""
|
606
644
|
Remove a field from a model. Usually involves deleting a column,
|
607
645
|
but for M2Ms may involve deleting a table.
|
@@ -630,7 +668,13 @@ class BaseDatabaseSchemaEditor:
|
|
630
668
|
):
|
631
669
|
self.deferred_sql.remove(sql)
|
632
670
|
|
633
|
-
def alter_field(
|
671
|
+
def alter_field(
|
672
|
+
self,
|
673
|
+
model: type[Model],
|
674
|
+
old_field: Field,
|
675
|
+
new_field: Field,
|
676
|
+
strict: bool = False,
|
677
|
+
) -> None:
|
634
678
|
"""
|
635
679
|
Allow a field's type, uniqueness, nullability, default, column,
|
636
680
|
constraints, etc. to be modified.
|
@@ -655,7 +699,7 @@ class BaseDatabaseSchemaEditor:
|
|
655
699
|
elif (
|
656
700
|
old_type is None
|
657
701
|
and new_type is None
|
658
|
-
and (old_field.remote_field.through and new_field.remote_field.through)
|
702
|
+
and (old_field.remote_field.through and new_field.remote_field.through) # type: ignore[attr-defined]
|
659
703
|
):
|
660
704
|
# Both sides have through models; this is a no-op.
|
661
705
|
return
|
@@ -677,7 +721,9 @@ class BaseDatabaseSchemaEditor:
|
|
677
721
|
strict,
|
678
722
|
)
|
679
723
|
|
680
|
-
def _field_db_check(
|
724
|
+
def _field_db_check(
|
725
|
+
self, field: Field, field_db_params: dict[str, Any]
|
726
|
+
) -> str | None:
|
681
727
|
# Always check constraints with the same mocked column name to avoid
|
682
728
|
# recreating constrains when the column is renamed.
|
683
729
|
check_constraints = self.connection.data_type_check_constraints
|
@@ -690,22 +736,22 @@ class BaseDatabaseSchemaEditor:
|
|
690
736
|
|
691
737
|
def _alter_field(
|
692
738
|
self,
|
693
|
-
model,
|
694
|
-
old_field,
|
695
|
-
new_field,
|
696
|
-
old_type,
|
697
|
-
new_type,
|
698
|
-
old_db_params,
|
699
|
-
new_db_params,
|
700
|
-
strict=False,
|
701
|
-
):
|
739
|
+
model: type[Model],
|
740
|
+
old_field: Field,
|
741
|
+
new_field: Field,
|
742
|
+
old_type: str,
|
743
|
+
new_type: str,
|
744
|
+
old_db_params: dict[str, Any],
|
745
|
+
new_db_params: dict[str, Any],
|
746
|
+
strict: bool = False,
|
747
|
+
) -> None:
|
702
748
|
"""Perform a "physical" (non-ManyToMany) field update."""
|
703
749
|
# Drop any FK constraints, we'll remake them later
|
704
750
|
fks_dropped = set()
|
705
751
|
if (
|
706
752
|
self.connection.features.supports_foreign_keys
|
707
753
|
and old_field.remote_field
|
708
|
-
and old_field.db_constraint
|
754
|
+
and old_field.db_constraint # type: ignore[attr-defined]
|
709
755
|
and self._field_should_be_altered(
|
710
756
|
old_field,
|
711
757
|
new_field,
|
@@ -773,10 +819,10 @@ class BaseDatabaseSchemaEditor:
|
|
773
819
|
# True | False | False | True
|
774
820
|
# True | False | True | True
|
775
821
|
if (
|
776
|
-
(old_field.remote_field and old_field.db_index)
|
822
|
+
(old_field.remote_field and old_field.db_index) # type: ignore[attr-defined]
|
777
823
|
and not old_field.primary_key
|
778
824
|
and (
|
779
|
-
not (new_field.remote_field and new_field.db_index)
|
825
|
+
not (new_field.remote_field and new_field.db_index) # type: ignore[attr-defined]
|
780
826
|
or new_field.primary_key
|
781
827
|
)
|
782
828
|
):
|
@@ -938,10 +984,10 @@ class BaseDatabaseSchemaEditor:
|
|
938
984
|
# True | True | True | False
|
939
985
|
if (
|
940
986
|
(
|
941
|
-
not (old_field.remote_field and old_field.db_index)
|
987
|
+
not (old_field.remote_field and old_field.db_index) # type: ignore[attr-defined]
|
942
988
|
or old_field.primary_key
|
943
989
|
)
|
944
|
-
and (new_field.remote_field and new_field.db_index)
|
990
|
+
and (new_field.remote_field and new_field.db_index) # type: ignore[attr-defined]
|
945
991
|
and not new_field.primary_key
|
946
992
|
):
|
947
993
|
self.execute(self._create_index_sql(model, fields=[new_field]))
|
@@ -986,9 +1032,9 @@ class BaseDatabaseSchemaEditor:
|
|
986
1032
|
self.connection.features.supports_foreign_keys
|
987
1033
|
and new_field.remote_field
|
988
1034
|
and (
|
989
|
-
fks_dropped or not old_field.remote_field or not old_field.db_constraint
|
1035
|
+
fks_dropped or not old_field.remote_field or not old_field.db_constraint # type: ignore[attr-defined]
|
990
1036
|
)
|
991
|
-
and new_field.db_constraint
|
1037
|
+
and new_field.db_constraint # type: ignore[attr-defined]
|
992
1038
|
):
|
993
1039
|
self.execute(
|
994
1040
|
self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s")
|
@@ -1023,7 +1069,9 @@ class BaseDatabaseSchemaEditor:
|
|
1023
1069
|
if self.connection.features.connection_persists_old_columns:
|
1024
1070
|
self.connection.close()
|
1025
1071
|
|
1026
|
-
def _alter_column_null_sql(
|
1072
|
+
def _alter_column_null_sql(
|
1073
|
+
self, model: type[Model], old_field: Field, new_field: Field
|
1074
|
+
) -> tuple[str, list[Any]]:
|
1027
1075
|
"""
|
1028
1076
|
Hook to specialize column null alteration.
|
1029
1077
|
|
@@ -1045,7 +1093,13 @@ class BaseDatabaseSchemaEditor:
|
|
1045
1093
|
[],
|
1046
1094
|
)
|
1047
1095
|
|
1048
|
-
def _alter_column_default_sql(
|
1096
|
+
def _alter_column_default_sql(
|
1097
|
+
self,
|
1098
|
+
model: type[Model],
|
1099
|
+
old_field: Field | None,
|
1100
|
+
new_field: Field,
|
1101
|
+
drop: bool = False,
|
1102
|
+
) -> tuple[str, list[Any]]:
|
1049
1103
|
"""
|
1050
1104
|
Hook to specialize column default alteration.
|
1051
1105
|
|
@@ -1084,8 +1138,14 @@ class BaseDatabaseSchemaEditor:
|
|
1084
1138
|
)
|
1085
1139
|
|
1086
1140
|
def _alter_column_type_sql(
|
1087
|
-
self,
|
1088
|
-
|
1141
|
+
self,
|
1142
|
+
model: type[Model],
|
1143
|
+
old_field: Field,
|
1144
|
+
new_field: Field,
|
1145
|
+
new_type: str,
|
1146
|
+
old_collation: str | None,
|
1147
|
+
new_collation: str | None,
|
1148
|
+
) -> tuple[tuple[str, list[Any]], list[tuple[str, list[Any]]]]:
|
1089
1149
|
"""
|
1090
1150
|
Hook to specialize column type alteration for different backends,
|
1091
1151
|
for cases when a creation type is different to an alteration type
|
@@ -1129,7 +1189,13 @@ class BaseDatabaseSchemaEditor:
|
|
1129
1189
|
other_actions,
|
1130
1190
|
)
|
1131
1191
|
|
1132
|
-
def _alter_column_comment_sql(
|
1192
|
+
def _alter_column_comment_sql(
|
1193
|
+
self,
|
1194
|
+
model: type[Model],
|
1195
|
+
new_field: Field,
|
1196
|
+
new_type: str,
|
1197
|
+
new_db_comment: str | None,
|
1198
|
+
) -> tuple[str, list[Any]]:
|
1133
1199
|
return (
|
1134
1200
|
self.sql_alter_column_comment
|
1135
1201
|
% {
|
@@ -1140,42 +1206,46 @@ class BaseDatabaseSchemaEditor:
|
|
1140
1206
|
[],
|
1141
1207
|
)
|
1142
1208
|
|
1143
|
-
def _comment_sql(self, comment):
|
1209
|
+
def _comment_sql(self, comment: str | None) -> str:
|
1144
1210
|
return self.quote_value(comment or "")
|
1145
1211
|
|
1146
|
-
def _alter_many_to_many(
|
1212
|
+
def _alter_many_to_many(
|
1213
|
+
self, model: type[Model], old_field: Field, new_field: Field, strict: bool
|
1214
|
+
) -> None:
|
1147
1215
|
"""Alter M2Ms to repoint their to= endpoints."""
|
1148
1216
|
# Rename the through table
|
1149
1217
|
if (
|
1150
|
-
old_field.remote_field.through._meta.db_table
|
1151
|
-
!= new_field.remote_field.through._meta.db_table
|
1218
|
+
old_field.remote_field.through._meta.db_table # type: ignore[attr-defined]
|
1219
|
+
!= new_field.remote_field.through._meta.db_table # type: ignore[attr-defined]
|
1152
1220
|
):
|
1153
1221
|
self.alter_db_table(
|
1154
|
-
old_field.remote_field.through,
|
1155
|
-
old_field.remote_field.through._meta.db_table,
|
1156
|
-
new_field.remote_field.through._meta.db_table,
|
1222
|
+
old_field.remote_field.through, # type: ignore[attr-defined]
|
1223
|
+
old_field.remote_field.through._meta.db_table, # type: ignore[attr-defined]
|
1224
|
+
new_field.remote_field.through._meta.db_table, # type: ignore[attr-defined]
|
1157
1225
|
)
|
1158
1226
|
# Repoint the FK to the other side
|
1159
1227
|
self.alter_field(
|
1160
|
-
new_field.remote_field.through,
|
1228
|
+
new_field.remote_field.through, # type: ignore[attr-defined]
|
1161
1229
|
# The field that points to the target model is needed, so we can
|
1162
1230
|
# tell alter_field to change it - this is m2m_reverse_field_name()
|
1163
1231
|
# (as opposed to m2m_field_name(), which points to our model).
|
1164
|
-
old_field.remote_field.through._meta.get_field(
|
1165
|
-
old_field.m2m_reverse_field_name()
|
1232
|
+
old_field.remote_field.through._meta.get_field( # type: ignore[attr-defined]
|
1233
|
+
old_field.m2m_reverse_field_name() # type: ignore[attr-defined]
|
1166
1234
|
),
|
1167
|
-
new_field.remote_field.through._meta.get_field(
|
1168
|
-
new_field.m2m_reverse_field_name()
|
1235
|
+
new_field.remote_field.through._meta.get_field( # type: ignore[attr-defined]
|
1236
|
+
new_field.m2m_reverse_field_name() # type: ignore[attr-defined]
|
1169
1237
|
),
|
1170
1238
|
)
|
1171
1239
|
self.alter_field(
|
1172
|
-
new_field.remote_field.through,
|
1240
|
+
new_field.remote_field.through, # type: ignore[attr-defined]
|
1173
1241
|
# for self-referential models we need to alter field from the other end too
|
1174
|
-
old_field.remote_field.through._meta.get_field(old_field.m2m_field_name()),
|
1175
|
-
new_field.remote_field.through._meta.get_field(new_field.m2m_field_name()),
|
1242
|
+
old_field.remote_field.through._meta.get_field(old_field.m2m_field_name()), # type: ignore[attr-defined]
|
1243
|
+
new_field.remote_field.through._meta.get_field(new_field.m2m_field_name()), # type: ignore[attr-defined]
|
1176
1244
|
)
|
1177
1245
|
|
1178
|
-
def _create_index_name(
|
1246
|
+
def _create_index_name(
|
1247
|
+
self, table_name: str, column_names: list[str], suffix: str = ""
|
1248
|
+
) -> str:
|
1179
1249
|
"""
|
1180
1250
|
Generate a unique name for an index/unique constraint.
|
1181
1251
|
|
@@ -1208,12 +1278,14 @@ class BaseDatabaseSchemaEditor:
|
|
1208
1278
|
index_name = f"D{index_name[:-1]}"
|
1209
1279
|
return index_name
|
1210
1280
|
|
1211
|
-
def _index_condition_sql(self, condition):
|
1281
|
+
def _index_condition_sql(self, condition: str | None) -> str:
|
1212
1282
|
if condition:
|
1213
1283
|
return " WHERE " + condition
|
1214
1284
|
return ""
|
1215
1285
|
|
1216
|
-
def _index_include_sql(
|
1286
|
+
def _index_include_sql(
|
1287
|
+
self, model: type[Model], columns: list[str] | None
|
1288
|
+
) -> str | Statement:
|
1217
1289
|
if not columns or not self.connection.features.supports_covering_indexes:
|
1218
1290
|
return ""
|
1219
1291
|
return Statement(
|
@@ -1223,19 +1295,19 @@ class BaseDatabaseSchemaEditor:
|
|
1223
1295
|
|
1224
1296
|
def _create_index_sql(
|
1225
1297
|
self,
|
1226
|
-
model,
|
1298
|
+
model: type[Model],
|
1227
1299
|
*,
|
1228
|
-
fields=None,
|
1229
|
-
name=None,
|
1230
|
-
suffix="",
|
1231
|
-
using="",
|
1232
|
-
col_suffixes=(),
|
1233
|
-
sql=None,
|
1234
|
-
opclasses=(),
|
1235
|
-
condition=None,
|
1236
|
-
include=None,
|
1237
|
-
expressions=None,
|
1238
|
-
):
|
1300
|
+
fields: list[Field] | None = None,
|
1301
|
+
name: str | None = None,
|
1302
|
+
suffix: str = "",
|
1303
|
+
using: str = "",
|
1304
|
+
col_suffixes: tuple[str, ...] = (),
|
1305
|
+
sql: str | None = None,
|
1306
|
+
opclasses: tuple[str, ...] = (),
|
1307
|
+
condition: str | None = None,
|
1308
|
+
include: list[str] | None = None,
|
1309
|
+
expressions: Any = None,
|
1310
|
+
) -> Statement:
|
1239
1311
|
"""
|
1240
1312
|
Return the SQL statement to create the index for one or several fields
|
1241
1313
|
or expressions. `sql` can be specified if the syntax differs from the
|
@@ -1248,7 +1320,7 @@ class BaseDatabaseSchemaEditor:
|
|
1248
1320
|
sql_create_index = sql or self.sql_create_index
|
1249
1321
|
table = model._meta.db_table
|
1250
1322
|
|
1251
|
-
def create_index_name(*args, **kwargs):
|
1323
|
+
def create_index_name(*args: Any, **kwargs: Any) -> str:
|
1252
1324
|
nonlocal name
|
1253
1325
|
if name is None:
|
1254
1326
|
name = self._create_index_name(*args, **kwargs)
|
@@ -1269,14 +1341,18 @@ class BaseDatabaseSchemaEditor:
|
|
1269
1341
|
include=self._index_include_sql(model, include),
|
1270
1342
|
)
|
1271
1343
|
|
1272
|
-
def _delete_index_sql(
|
1344
|
+
def _delete_index_sql(
|
1345
|
+
self, model: type[Model], name: str, sql: str | None = None
|
1346
|
+
) -> Statement:
|
1273
1347
|
return Statement(
|
1274
1348
|
sql or self.sql_delete_index,
|
1275
1349
|
table=Table(model._meta.db_table, self.quote_name),
|
1276
1350
|
name=self.quote_name(name),
|
1277
1351
|
)
|
1278
1352
|
|
1279
|
-
def _rename_index_sql(
|
1353
|
+
def _rename_index_sql(
|
1354
|
+
self, model: type[Model], old_name: str, new_name: str
|
1355
|
+
) -> Statement:
|
1280
1356
|
return Statement(
|
1281
1357
|
self.sql_rename_index,
|
1282
1358
|
table=Table(model._meta.db_table, self.quote_name),
|
@@ -1284,14 +1360,20 @@ class BaseDatabaseSchemaEditor:
|
|
1284
1360
|
new_name=self.quote_name(new_name),
|
1285
1361
|
)
|
1286
1362
|
|
1287
|
-
def _index_columns(
|
1363
|
+
def _index_columns(
|
1364
|
+
self,
|
1365
|
+
table: str,
|
1366
|
+
columns: list[str],
|
1367
|
+
col_suffixes: tuple[str, ...],
|
1368
|
+
opclasses: tuple[str, ...],
|
1369
|
+
) -> Columns:
|
1288
1370
|
return Columns(table, columns, self.quote_name, col_suffixes=col_suffixes)
|
1289
1371
|
|
1290
|
-
def _model_indexes_sql(self, model):
|
1372
|
+
def _model_indexes_sql(self, model: type[Model]) -> list[Statement | None]:
|
1291
1373
|
"""
|
1292
1374
|
Return a list of all index SQL statements (field indexes, Meta.indexes) for the specified model.
|
1293
1375
|
"""
|
1294
|
-
output = []
|
1376
|
+
output: list[Statement | None] = []
|
1295
1377
|
for field in model._meta.local_fields:
|
1296
1378
|
output.extend(self._field_indexes_sql(model, field))
|
1297
1379
|
|
@@ -1303,16 +1385,18 @@ class BaseDatabaseSchemaEditor:
|
|
1303
1385
|
output.append(index.create_sql(model, self))
|
1304
1386
|
return output
|
1305
1387
|
|
1306
|
-
def _field_indexes_sql(self, model, field):
|
1388
|
+
def _field_indexes_sql(self, model: type[Model], field: Field) -> list[Statement]:
|
1307
1389
|
"""
|
1308
1390
|
Return a list of all index SQL statements for the specified field.
|
1309
1391
|
"""
|
1310
|
-
output = []
|
1392
|
+
output: list[Statement] = []
|
1311
1393
|
if self._field_should_be_indexed(model, field):
|
1312
1394
|
output.append(self._create_index_sql(model, fields=[field]))
|
1313
1395
|
return output
|
1314
1396
|
|
1315
|
-
def _field_should_be_altered(
|
1397
|
+
def _field_should_be_altered(
|
1398
|
+
self, old_field: Field, new_field: Field, ignore: set[str] | None = None
|
1399
|
+
) -> bool:
|
1316
1400
|
ignore = ignore or set()
|
1317
1401
|
_, old_path, old_args, old_kwargs = old_field.deconstruct()
|
1318
1402
|
_, new_path, new_args, new_kwargs = new_field.deconstruct()
|
@@ -1321,21 +1405,23 @@ class BaseDatabaseSchemaEditor:
|
|
1321
1405
|
# - changing an attribute that doesn't affect the schema
|
1322
1406
|
# - changing an attribute in the provided set of ignored attributes
|
1323
1407
|
# - adding only a db_column and the column name is not changed
|
1324
|
-
for attr in ignore.union(old_field.non_db_attrs):
|
1408
|
+
for attr in ignore.union(old_field.non_db_attrs): # type: ignore[attr-defined]
|
1325
1409
|
old_kwargs.pop(attr, None)
|
1326
|
-
for attr in ignore.union(new_field.non_db_attrs):
|
1410
|
+
for attr in ignore.union(new_field.non_db_attrs): # type: ignore[attr-defined]
|
1327
1411
|
new_kwargs.pop(attr, None)
|
1328
1412
|
return self.quote_name(old_field.column) != self.quote_name(
|
1329
1413
|
new_field.column
|
1330
1414
|
) or (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs)
|
1331
1415
|
|
1332
|
-
def _field_should_be_indexed(self, model, field):
|
1333
|
-
return (field.remote_field and field.db_index) and not field.primary_key
|
1416
|
+
def _field_should_be_indexed(self, model: type[Model], field: Field) -> bool:
|
1417
|
+
return (field.remote_field and field.db_index) and not field.primary_key # type: ignore[attr-defined]
|
1334
1418
|
|
1335
|
-
def _field_became_primary_key(self, old_field, new_field):
|
1419
|
+
def _field_became_primary_key(self, old_field: Field, new_field: Field) -> bool:
|
1336
1420
|
return not old_field.primary_key and new_field.primary_key
|
1337
1421
|
|
1338
|
-
def _rename_field_sql(
|
1422
|
+
def _rename_field_sql(
|
1423
|
+
self, table: str, old_field: Field, new_field: Field, new_type: str
|
1424
|
+
) -> str:
|
1339
1425
|
return self.sql_rename_column % {
|
1340
1426
|
"table": self.quote_name(table),
|
1341
1427
|
"old_column": self.quote_name(old_field.column),
|
@@ -1343,14 +1429,16 @@ class BaseDatabaseSchemaEditor:
|
|
1343
1429
|
"type": new_type,
|
1344
1430
|
}
|
1345
1431
|
|
1346
|
-
def _create_fk_sql(
|
1432
|
+
def _create_fk_sql(
|
1433
|
+
self, model: type[Model], field: Field, suffix: str
|
1434
|
+
) -> Statement:
|
1347
1435
|
table = Table(model._meta.db_table, self.quote_name)
|
1348
1436
|
name = self._fk_constraint_name(model, field, suffix)
|
1349
1437
|
column = Columns(model._meta.db_table, [field.column], self.quote_name)
|
1350
|
-
to_table = Table(field.target_field.model._meta.db_table, self.quote_name)
|
1438
|
+
to_table = Table(field.target_field.model._meta.db_table, self.quote_name) # type: ignore[attr-defined]
|
1351
1439
|
to_column = Columns(
|
1352
|
-
field.target_field.model._meta.db_table,
|
1353
|
-
[field.target_field.column],
|
1440
|
+
field.target_field.model._meta.db_table, # type: ignore[attr-defined]
|
1441
|
+
[field.target_field.column], # type: ignore[attr-defined]
|
1354
1442
|
self.quote_name,
|
1355
1443
|
)
|
1356
1444
|
deferrable = self.connection.ops.deferrable_sql()
|
@@ -1364,41 +1452,44 @@ class BaseDatabaseSchemaEditor:
|
|
1364
1452
|
deferrable=deferrable,
|
1365
1453
|
)
|
1366
1454
|
|
1367
|
-
def _fk_constraint_name(
|
1368
|
-
|
1455
|
+
def _fk_constraint_name(
|
1456
|
+
self, model: type[Model], field: Field, suffix: str
|
1457
|
+
) -> ForeignKeyName:
|
1458
|
+
def create_fk_name(*args: Any, **kwargs: Any) -> str:
|
1369
1459
|
return self.quote_name(self._create_index_name(*args, **kwargs))
|
1370
1460
|
|
1371
1461
|
return ForeignKeyName(
|
1372
1462
|
model._meta.db_table,
|
1373
1463
|
[field.column],
|
1374
|
-
split_identifier(field.target_field.model._meta.db_table)[1],
|
1375
|
-
[field.target_field.column],
|
1464
|
+
split_identifier(field.target_field.model._meta.db_table)[1], # type: ignore[attr-defined]
|
1465
|
+
[field.target_field.column], # type: ignore[attr-defined]
|
1376
1466
|
suffix,
|
1377
1467
|
create_fk_name,
|
1378
1468
|
)
|
1379
1469
|
|
1380
|
-
def _delete_fk_sql(self, model, name):
|
1470
|
+
def _delete_fk_sql(self, model: type[Model], name: str) -> Statement:
|
1381
1471
|
return self._delete_constraint_sql(self.sql_delete_fk, model, name)
|
1382
1472
|
|
1383
|
-
def _deferrable_constraint_sql(self, deferrable):
|
1473
|
+
def _deferrable_constraint_sql(self, deferrable: Deferrable | None) -> str:
|
1384
1474
|
if deferrable is None:
|
1385
1475
|
return ""
|
1386
1476
|
if deferrable == Deferrable.DEFERRED:
|
1387
1477
|
return " DEFERRABLE INITIALLY DEFERRED"
|
1388
1478
|
if deferrable == Deferrable.IMMEDIATE:
|
1389
1479
|
return " DEFERRABLE INITIALLY IMMEDIATE"
|
1480
|
+
return ""
|
1390
1481
|
|
1391
1482
|
def _unique_sql(
|
1392
1483
|
self,
|
1393
|
-
model,
|
1394
|
-
fields,
|
1395
|
-
name,
|
1396
|
-
condition=None,
|
1397
|
-
deferrable=None,
|
1398
|
-
include=None,
|
1399
|
-
opclasses=None,
|
1400
|
-
expressions=None,
|
1401
|
-
):
|
1484
|
+
model: type[Model],
|
1485
|
+
fields: Iterable[Field],
|
1486
|
+
name: str,
|
1487
|
+
condition: str | None = None,
|
1488
|
+
deferrable: Deferrable | None = None,
|
1489
|
+
include: list[str] | None = None,
|
1490
|
+
opclasses: tuple[str, ...] | None = None,
|
1491
|
+
expressions: Any = None,
|
1492
|
+
) -> str | None:
|
1402
1493
|
if (
|
1403
1494
|
deferrable
|
1404
1495
|
and not self.connection.features.supports_deferrable_unique_constraints
|
@@ -1430,15 +1521,15 @@ class BaseDatabaseSchemaEditor:
|
|
1430
1521
|
|
1431
1522
|
def _create_unique_sql(
|
1432
1523
|
self,
|
1433
|
-
model,
|
1434
|
-
fields,
|
1435
|
-
name=None,
|
1436
|
-
condition=None,
|
1437
|
-
deferrable=None,
|
1438
|
-
include=None,
|
1439
|
-
opclasses=None,
|
1440
|
-
expressions=None,
|
1441
|
-
):
|
1524
|
+
model: type[Model],
|
1525
|
+
fields: Iterable[Field],
|
1526
|
+
name: str | None = None,
|
1527
|
+
condition: str | None = None,
|
1528
|
+
deferrable: Deferrable | None = None,
|
1529
|
+
include: list[str] | None = None,
|
1530
|
+
opclasses: tuple[str, ...] | None = None,
|
1531
|
+
expressions: Any = None,
|
1532
|
+
) -> Statement | None:
|
1442
1533
|
if (
|
1443
1534
|
(
|
1444
1535
|
deferrable
|
@@ -1464,25 +1555,27 @@ class BaseDatabaseSchemaEditor:
|
|
1464
1555
|
else:
|
1465
1556
|
sql = self.sql_create_unique
|
1466
1557
|
if columns:
|
1467
|
-
|
1558
|
+
columns_obj: Columns | Expressions = self._index_columns(
|
1468
1559
|
table, columns, col_suffixes=(), opclasses=opclasses
|
1469
1560
|
)
|
1470
1561
|
else:
|
1471
|
-
|
1562
|
+
columns_obj = Expressions(table, expressions, compiler, self.quote_value)
|
1472
1563
|
return Statement(
|
1473
1564
|
sql,
|
1474
1565
|
table=Table(table, self.quote_name),
|
1475
1566
|
name=name,
|
1476
|
-
columns=
|
1567
|
+
columns=columns_obj,
|
1477
1568
|
condition=self._index_condition_sql(condition),
|
1478
1569
|
deferrable=self._deferrable_constraint_sql(deferrable),
|
1479
1570
|
include=self._index_include_sql(model, include),
|
1480
1571
|
)
|
1481
1572
|
|
1482
|
-
def _unique_constraint_name(
|
1573
|
+
def _unique_constraint_name(
|
1574
|
+
self, table: str, columns: list[str], quote: bool = True
|
1575
|
+
) -> IndexName | str:
|
1483
1576
|
if quote:
|
1484
1577
|
|
1485
|
-
def create_unique_name(*args, **kwargs):
|
1578
|
+
def create_unique_name(*args: Any, **kwargs: Any) -> str:
|
1486
1579
|
return self.quote_name(self._create_index_name(*args, **kwargs))
|
1487
1580
|
|
1488
1581
|
else:
|
@@ -1492,14 +1585,14 @@ class BaseDatabaseSchemaEditor:
|
|
1492
1585
|
|
1493
1586
|
def _delete_unique_sql(
|
1494
1587
|
self,
|
1495
|
-
model,
|
1496
|
-
name,
|
1497
|
-
condition=None,
|
1498
|
-
deferrable=None,
|
1499
|
-
include=None,
|
1500
|
-
opclasses=None,
|
1501
|
-
expressions=None,
|
1502
|
-
):
|
1588
|
+
model: type[Model],
|
1589
|
+
name: str,
|
1590
|
+
condition: str | None = None,
|
1591
|
+
deferrable: Deferrable | None = None,
|
1592
|
+
include: list[str] | None = None,
|
1593
|
+
opclasses: tuple[str, ...] | None = None,
|
1594
|
+
expressions: Any = None,
|
1595
|
+
) -> Statement | None:
|
1503
1596
|
if (
|
1504
1597
|
(
|
1505
1598
|
deferrable
|
@@ -1518,13 +1611,15 @@ class BaseDatabaseSchemaEditor:
|
|
1518
1611
|
sql = self.sql_delete_unique
|
1519
1612
|
return self._delete_constraint_sql(sql, model, name)
|
1520
1613
|
|
1521
|
-
def _check_sql(self, name, check):
|
1614
|
+
def _check_sql(self, name: str, check: str) -> str:
|
1522
1615
|
return self.sql_constraint % {
|
1523
1616
|
"name": self.quote_name(name),
|
1524
1617
|
"constraint": self.sql_check_constraint % {"check": check},
|
1525
1618
|
}
|
1526
1619
|
|
1527
|
-
def _create_check_sql(
|
1620
|
+
def _create_check_sql(
|
1621
|
+
self, model: type[Model], name: str, check: str
|
1622
|
+
) -> Statement | None:
|
1528
1623
|
if not self.connection.features.supports_table_check_constraints:
|
1529
1624
|
return None
|
1530
1625
|
return Statement(
|
@@ -1534,12 +1629,14 @@ class BaseDatabaseSchemaEditor:
|
|
1534
1629
|
check=check,
|
1535
1630
|
)
|
1536
1631
|
|
1537
|
-
def _delete_check_sql(self, model, name):
|
1632
|
+
def _delete_check_sql(self, model: type[Model], name: str) -> Statement | None:
|
1538
1633
|
if not self.connection.features.supports_table_check_constraints:
|
1539
1634
|
return None
|
1540
1635
|
return self._delete_constraint_sql(self.sql_delete_check, model, name)
|
1541
1636
|
|
1542
|
-
def _delete_constraint_sql(
|
1637
|
+
def _delete_constraint_sql(
|
1638
|
+
self, template: str, model: type[Model], name: str
|
1639
|
+
) -> Statement:
|
1543
1640
|
return Statement(
|
1544
1641
|
template,
|
1545
1642
|
table=Table(model._meta.db_table, self.quote_name),
|
@@ -1548,16 +1645,16 @@ class BaseDatabaseSchemaEditor:
|
|
1548
1645
|
|
1549
1646
|
def _constraint_names(
|
1550
1647
|
self,
|
1551
|
-
model,
|
1552
|
-
column_names=None,
|
1553
|
-
unique=None,
|
1554
|
-
primary_key=None,
|
1555
|
-
index=None,
|
1556
|
-
foreign_key=None,
|
1557
|
-
check=None,
|
1558
|
-
type_=None,
|
1559
|
-
exclude=None,
|
1560
|
-
):
|
1648
|
+
model: type[Model],
|
1649
|
+
column_names: list[str] | None = None,
|
1650
|
+
unique: bool | None = None,
|
1651
|
+
primary_key: bool | None = None,
|
1652
|
+
index: bool | None = None,
|
1653
|
+
foreign_key: bool | None = None,
|
1654
|
+
check: bool | None = None,
|
1655
|
+
type_: str | None = None,
|
1656
|
+
exclude: set[str] | None = None,
|
1657
|
+
) -> list[str]:
|
1561
1658
|
"""Return all constraint names matching the columns and conditions."""
|
1562
1659
|
if column_names is not None:
|
1563
1660
|
column_names = [
|
@@ -1572,7 +1669,7 @@ class BaseDatabaseSchemaEditor:
|
|
1572
1669
|
constraints = self.connection.introspection.get_constraints(
|
1573
1670
|
cursor, model._meta.db_table
|
1574
1671
|
)
|
1575
|
-
result = []
|
1672
|
+
result: list[str] = []
|
1576
1673
|
for name, infodict in constraints.items():
|
1577
1674
|
if column_names is None or column_names == infodict["columns"]:
|
1578
1675
|
if unique is not None and infodict["unique"] != unique:
|
@@ -1591,7 +1688,7 @@ class BaseDatabaseSchemaEditor:
|
|
1591
1688
|
result.append(name)
|
1592
1689
|
return result
|
1593
1690
|
|
1594
|
-
def _delete_primary_key(self, model, strict=False):
|
1691
|
+
def _delete_primary_key(self, model: type[Model], strict: bool = False) -> None:
|
1595
1692
|
constraint_names = self._constraint_names(model, primary_key=True)
|
1596
1693
|
if strict and len(constraint_names) != 1:
|
1597
1694
|
raise ValueError(
|
@@ -1600,7 +1697,7 @@ class BaseDatabaseSchemaEditor:
|
|
1600
1697
|
for constraint_name in constraint_names:
|
1601
1698
|
self.execute(self._delete_primary_key_sql(model, constraint_name))
|
1602
1699
|
|
1603
|
-
def _create_primary_key_sql(self, model, field):
|
1700
|
+
def _create_primary_key_sql(self, model: type[Model], field: Field) -> Statement:
|
1604
1701
|
return Statement(
|
1605
1702
|
self.sql_create_pk,
|
1606
1703
|
table=Table(model._meta.db_table, self.quote_name),
|
@@ -1612,8 +1709,13 @@ class BaseDatabaseSchemaEditor:
|
|
1612
1709
|
columns=Columns(model._meta.db_table, [field.column], self.quote_name),
|
1613
1710
|
)
|
1614
1711
|
|
1615
|
-
def _delete_primary_key_sql(self, model, name):
|
1712
|
+
def _delete_primary_key_sql(self, model: type[Model], name: str) -> Statement:
|
1616
1713
|
return self._delete_constraint_sql(self.sql_delete_pk, model, name)
|
1617
1714
|
|
1618
|
-
def _collate_sql(
|
1715
|
+
def _collate_sql(
|
1716
|
+
self,
|
1717
|
+
collation: str | None,
|
1718
|
+
old_collation: str | None = None,
|
1719
|
+
table_name: str | None = None,
|
1720
|
+
) -> str:
|
1619
1721
|
return "COLLATE " + self.quote_name(collation) if collation else ""
|