plain.models 0.38.0__tar.gz → 0.39.0__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.
- {plain_models-0.38.0 → plain_models-0.39.0}/PKG-INFO +4 -1
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/CHANGELOG.md +19 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/README.md +3 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/__init__.py +2 -2
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/creation.py +1 -1
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/operations.py +1 -3
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/schema.py +4 -8
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/base.py +1 -3
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/introspection.py +2 -6
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/operations.py +2 -4
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/base.py +2 -6
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/introspection.py +2 -6
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/operations.py +1 -3
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/schema.py +2 -10
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/base.py +2 -6
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/introspection.py +2 -8
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/base.py +46 -74
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/constraints.py +3 -3
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/deletion.py +9 -9
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/fields/__init__.py +30 -104
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/fields/related.py +90 -343
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/fields/related_descriptors.py +14 -14
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/fields/related_lookups.py +2 -2
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/fields/reverse_related.py +6 -14
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/forms.py +14 -76
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/lookups.py +2 -2
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/autodetector.py +2 -25
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/operations/fields.py +0 -6
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/state.py +2 -26
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/utils.py +4 -14
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/options.py +4 -12
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/query.py +46 -54
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/query_utils.py +3 -5
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/sql/compiler.py +16 -18
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/sql/query.py +12 -11
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/sql/subqueries.py +10 -10
- {plain_models-0.38.0 → plain_models-0.39.0}/pyproject.toml +1 -1
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/app/examples/migrations/0001_initial.py +1 -1
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +7 -7
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/app/examples/models.py +3 -3
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/test_delete_behaviors.py +3 -3
- {plain_models-0.38.0 → plain_models-0.39.0}/.gitignore +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/LICENSE +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/README.md +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/aggregates.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/base.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/client.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/features.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/introspection.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/base/validation.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/ddl_references.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/client.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/compiler.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/creation.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/features.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/schema.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/mysql/validation.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/client.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/creation.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/features.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/_functions.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/client.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/creation.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/features.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/operations.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/sqlite3/schema.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/utils.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backups/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backups/cli.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backups/clients.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backups/core.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/cli.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/config.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/connections.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/constants.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/database_url.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/db.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/default_settings.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/entrypoints.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/enums.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/exceptions.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/expressions.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/fields/json.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/fields/mixins.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/functions/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/functions/comparison.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/functions/datetime.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/functions/math.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/functions/mixins.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/functions/text.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/functions/window.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/indexes.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/manager.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/exceptions.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/executor.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/graph.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/loader.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/migration.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/operations/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/operations/base.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/operations/models.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/operations/special.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/optimizer.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/questioner.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/recorder.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/serializer.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/migrations/writer.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/otel.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/preflight.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/registry.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/sql/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/sql/constants.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/sql/datastructures.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/sql/where.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/test/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/test/pytest.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/test/utils.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/transaction.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/plain/models/utils.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/app/examples/migrations/__init__.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/app/settings.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/app/urls.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/test_database_url.py +0 -0
- {plain_models-0.38.0 → plain_models-0.39.0}/tests/test_models.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: plain.models
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.39.0
|
4
4
|
Summary: Database models for Plain.
|
5
5
|
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
6
6
|
License-File: LICENSE
|
@@ -30,6 +30,9 @@ class User(models.Model):
|
|
30
30
|
return self.email
|
31
31
|
```
|
32
32
|
|
33
|
+
Every model automatically includes an `id` field which serves as the primary
|
34
|
+
key. The name `id` is reserved and can't be used for other fields.
|
35
|
+
|
33
36
|
Create, update, and delete instances of your models:
|
34
37
|
|
35
38
|
```python
|
@@ -1,5 +1,24 @@
|
|
1
1
|
# plain-models changelog
|
2
2
|
|
3
|
+
## [0.39.0](https://github.com/dropseed/plain/releases/plain-models@0.39.0) (2025-07-22)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- Models now use a single automatic `id` field as the primary key, replacing the previous `pk` alias and automatic field system ([4b8fa6a](https://github.com/dropseed/plain/commit/4b8fa6a))
|
8
|
+
- Removed the `to_field` option for ForeignKey - foreign keys now always reference the primary key of the related model ([7fc3c88](https://github.com/dropseed/plain/commit/7fc3c88))
|
9
|
+
- Removed the internal `from_fields` and `to_fields` system used for multi-column foreign keys ([0e9eda3](https://github.com/dropseed/plain/commit/0e9eda3))
|
10
|
+
- Removed the `parent_link` parameter on ForeignKey and ForeignObject ([6658647](https://github.com/dropseed/plain/commit/6658647))
|
11
|
+
- Removed `InlineForeignKeyField` from forms ([ede6265](https://github.com/dropseed/plain/commit/ede6265))
|
12
|
+
- Merged ForeignObject functionality into ForeignKey, simplifying the foreign key implementation ([e6d9aaa](https://github.com/dropseed/plain/commit/e6d9aaa))
|
13
|
+
- Cleaned up unused code in ForeignKey and fixed ForeignObjectRel imports ([b656ee6](https://github.com/dropseed/plain/commit/b656ee6))
|
14
|
+
|
15
|
+
### Upgrade instructions
|
16
|
+
|
17
|
+
- Replace any direct references to `pk` with `id` in your models and queries (e.g., `user.pk` becomes `user.id`)
|
18
|
+
- Remove any `to_field` arguments from ForeignKey definitions - they are no longer supported
|
19
|
+
- Remove any `parent_link=True` arguments from ForeignKey definitions - they are no longer supported
|
20
|
+
- Replace any usage of `InlineForeignKeyField` in forms with standard form fields
|
21
|
+
|
3
22
|
## [0.38.0](https://github.com/dropseed/plain/releases/plain-models@0.38.0) (2025-07-21)
|
4
23
|
|
5
24
|
### What's changed
|
@@ -19,6 +19,9 @@ class User(models.Model):
|
|
19
19
|
return self.email
|
20
20
|
```
|
21
21
|
|
22
|
+
Every model automatically includes an `id` field which serves as the primary
|
23
|
+
key. The name `id` is reserved and can't be used for other fields.
|
24
|
+
|
22
25
|
Create, update, and delete instances of your models:
|
23
26
|
|
24
27
|
```python
|
@@ -68,8 +68,9 @@ from .registry import models_registry, register_model
|
|
68
68
|
from .base import DEFERRED, Model # isort:skip
|
69
69
|
from .fields.related import ( # isort:skip
|
70
70
|
ForeignKey,
|
71
|
-
ForeignObject,
|
72
71
|
ManyToManyField,
|
72
|
+
)
|
73
|
+
from .fields.reverse_related import ( # isort:skip
|
73
74
|
ForeignObjectRel,
|
74
75
|
ManyToOneRel,
|
75
76
|
ManyToManyRel,
|
@@ -116,7 +117,6 @@ __all__ += [
|
|
116
117
|
"Model",
|
117
118
|
"FilteredRelation",
|
118
119
|
"ForeignKey",
|
119
|
-
"ForeignObject",
|
120
120
|
"ManyToManyField",
|
121
121
|
"ForeignObjectRel",
|
122
122
|
"ManyToOneRel",
|
@@ -96,7 +96,7 @@ class BaseDatabaseCreation:
|
|
96
96
|
# ) and router.allow_migrate_model(self.connection.alias, model):
|
97
97
|
# queryset = model._base_manager.using(
|
98
98
|
# self.connection.alias,
|
99
|
-
# ).order_by(
|
99
|
+
# ).order_by("id")
|
100
100
|
# yield from queryset.iterator()
|
101
101
|
|
102
102
|
# # Serialize to a string
|
@@ -28,9 +28,7 @@ class BaseDatabaseOperations:
|
|
28
28
|
"PositiveBigIntegerField": (0, 9223372036854775807),
|
29
29
|
"PositiveSmallIntegerField": (0, 32767),
|
30
30
|
"PositiveIntegerField": (0, 2147483647),
|
31
|
-
"
|
32
|
-
"AutoField": (-2147483648, 2147483647),
|
33
|
-
"BigAutoField": (-9223372036854775808, 9223372036854775807),
|
31
|
+
"PrimaryKeyField": (-9223372036854775808, 9223372036854775807),
|
34
32
|
}
|
35
33
|
set_operators = {
|
36
34
|
"union": "UNION",
|
@@ -29,11 +29,11 @@ def _is_relevant_relation(relation, altered_field):
|
|
29
29
|
if field.many_to_many:
|
30
30
|
# M2M reverse field
|
31
31
|
return False
|
32
|
-
if altered_field.primary_key
|
32
|
+
if altered_field.primary_key:
|
33
33
|
# Foreign key constraint on the primary key, which is being altered.
|
34
34
|
return True
|
35
|
-
#
|
36
|
-
return altered_field.name
|
35
|
+
# ForeignKey always targets 'id'
|
36
|
+
return altered_field.name == "id"
|
37
37
|
|
38
38
|
|
39
39
|
def _all_related_fields(model):
|
@@ -239,11 +239,7 @@ class BaseDatabaseSchemaEditor:
|
|
239
239
|
column_sqls.append(f"{self.quote_name(field.column)} {definition}")
|
240
240
|
# Autoincrement SQL (for backends with post table definition
|
241
241
|
# variant).
|
242
|
-
if field.get_internal_type() in (
|
243
|
-
"AutoField",
|
244
|
-
"BigAutoField",
|
245
|
-
"SmallAutoField",
|
246
|
-
):
|
242
|
+
if field.get_internal_type() in ("PrimaryKeyField",):
|
247
243
|
autoinc_sql = self.connection.ops.autoinc_sql(
|
248
244
|
model._meta.db_table, field.column
|
249
245
|
)
|
@@ -91,8 +91,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
91
91
|
# be interpolated against the values of Field.__dict__ before being output.
|
92
92
|
# If a column type is set to None, it won't be included in the output.
|
93
93
|
data_types = {
|
94
|
-
"
|
95
|
-
"BigAutoField": "bigint AUTO_INCREMENT",
|
94
|
+
"PrimaryKeyField": "bigint AUTO_INCREMENT",
|
96
95
|
"BinaryField": "longblob",
|
97
96
|
"BooleanField": "bool",
|
98
97
|
"CharField": "varchar(%(max_length)s)",
|
@@ -109,7 +108,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
109
108
|
"PositiveBigIntegerField": "bigint UNSIGNED",
|
110
109
|
"PositiveIntegerField": "integer UNSIGNED",
|
111
110
|
"PositiveSmallIntegerField": "smallint UNSIGNED",
|
112
|
-
"SmallAutoField": "smallint AUTO_INCREMENT",
|
113
111
|
"SmallIntegerField": "smallint",
|
114
112
|
"TextField": "longtext",
|
115
113
|
"TimeField": "time(6)",
|
@@ -49,12 +49,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|
49
49
|
def get_field_type(self, data_type, description):
|
50
50
|
field_type = super().get_field_type(data_type, description)
|
51
51
|
if "auto_increment" in description.extra:
|
52
|
-
if field_type == "
|
53
|
-
return "
|
54
|
-
elif field_type == "BigIntegerField":
|
55
|
-
return "BigAutoField"
|
56
|
-
elif field_type == "SmallIntegerField":
|
57
|
-
return "SmallAutoField"
|
52
|
+
if field_type == "BigIntegerField":
|
53
|
+
return "PrimaryKeyField"
|
58
54
|
if description.is_unsigned:
|
59
55
|
if field_type == "BigIntegerField":
|
60
56
|
return "PositiveBigIntegerField"
|
@@ -21,9 +21,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|
21
21
|
"PositiveBigIntegerField": (0, 18446744073709551615),
|
22
22
|
}
|
23
23
|
cast_data_types = {
|
24
|
-
"
|
25
|
-
"BigAutoField": "signed integer",
|
26
|
-
"SmallAutoField": "signed integer",
|
24
|
+
"PrimaryKeyField": "signed integer",
|
27
25
|
"CharField": "char(%(max_length)s)",
|
28
26
|
"DecimalField": "decimal(%(max_digits)s, %(decimal_places)s)",
|
29
27
|
"TextField": "char",
|
@@ -201,7 +199,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|
201
199
|
# NO_AUTO_VALUE_ON_ZERO SQL mode.
|
202
200
|
if value == 0 and not self.connection.features.allows_auto_pk_0:
|
203
201
|
raise ValueError(
|
204
|
-
"The database backend does not accept 0 as a value for
|
202
|
+
"The database backend does not accept 0 as a value for PrimaryKeyField."
|
205
203
|
)
|
206
204
|
return value
|
207
205
|
|
@@ -89,8 +89,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
89
89
|
# be interpolated against the values of Field.__dict__ before being output.
|
90
90
|
# If a column type is set to None, it won't be included in the output.
|
91
91
|
data_types = {
|
92
|
-
"
|
93
|
-
"BigAutoField": "bigint",
|
92
|
+
"PrimaryKeyField": "bigint",
|
94
93
|
"BinaryField": "bytea",
|
95
94
|
"BooleanField": "boolean",
|
96
95
|
"CharField": _get_varchar_column,
|
@@ -107,7 +106,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
107
106
|
"PositiveBigIntegerField": "bigint",
|
108
107
|
"PositiveIntegerField": "integer",
|
109
108
|
"PositiveSmallIntegerField": "smallint",
|
110
|
-
"SmallAutoField": "smallint",
|
111
109
|
"SmallIntegerField": "smallint",
|
112
110
|
"TextField": "text",
|
113
111
|
"TimeField": "time",
|
@@ -119,9 +117,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
119
117
|
"PositiveSmallIntegerField": '"%(column)s" >= 0',
|
120
118
|
}
|
121
119
|
data_types_suffix = {
|
122
|
-
"
|
123
|
-
"BigAutoField": "GENERATED BY DEFAULT AS IDENTITY",
|
124
|
-
"SmallAutoField": "GENERATED BY DEFAULT AS IDENTITY",
|
120
|
+
"PrimaryKeyField": "GENERATED BY DEFAULT AS IDENTITY",
|
125
121
|
}
|
126
122
|
operators = {
|
127
123
|
"exact": "= %s",
|
{plain_models-0.38.0 → plain_models-0.39.0}/plain/models/backends/postgresql/introspection.py
RENAMED
@@ -44,12 +44,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|
44
44
|
# Required for pre-Plain 4.1 serial columns.
|
45
45
|
description.default and "nextval" in description.default
|
46
46
|
):
|
47
|
-
if field_type == "
|
48
|
-
return "
|
49
|
-
elif field_type == "BigIntegerField":
|
50
|
-
return "BigAutoField"
|
51
|
-
elif field_type == "SmallIntegerField":
|
52
|
-
return "SmallAutoField"
|
47
|
+
if field_type == "BigIntegerField":
|
48
|
+
return "PrimaryKeyField"
|
53
49
|
return field_type
|
54
50
|
|
55
51
|
def get_table_list(self, cursor):
|
@@ -166,11 +166,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
166
166
|
old_internal_type = old_field.get_internal_type()
|
167
167
|
# Make ALTER TYPE with IDENTITY make sense.
|
168
168
|
table = strip_quotes(model._meta.db_table)
|
169
|
-
auto_field_types = {
|
170
|
-
"AutoField",
|
171
|
-
"BigAutoField",
|
172
|
-
"SmallAutoField",
|
173
|
-
}
|
169
|
+
auto_field_types = {"PrimaryKeyField"}
|
174
170
|
old_is_auto = old_internal_type in auto_field_types
|
175
171
|
new_is_auto = new_internal_type in auto_field_types
|
176
172
|
if new_is_auto and not old_is_auto:
|
@@ -229,11 +225,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
229
225
|
model, old_field, new_field, new_type, old_collation, new_collation
|
230
226
|
)
|
231
227
|
column = strip_quotes(new_field.column)
|
232
|
-
db_types = {
|
233
|
-
"AutoField": "integer",
|
234
|
-
"BigAutoField": "bigint",
|
235
|
-
"SmallAutoField": "smallint",
|
236
|
-
}
|
228
|
+
db_types = {"PrimaryKeyField": "bigint"}
|
237
229
|
# Alter the sequence type if exists (Plain 4.1+ identity columns
|
238
230
|
# don't have it).
|
239
231
|
other_actions = []
|
@@ -57,8 +57,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
57
57
|
# thing" given more verbose field definitions, so leave them as is so that
|
58
58
|
# schema inspection is more useful.
|
59
59
|
data_types = {
|
60
|
-
"
|
61
|
-
"BigAutoField": "integer",
|
60
|
+
"PrimaryKeyField": "integer",
|
62
61
|
"BinaryField": "BLOB",
|
63
62
|
"BooleanField": "bool",
|
64
63
|
"CharField": "varchar(%(max_length)s)",
|
@@ -75,7 +74,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
75
74
|
"PositiveBigIntegerField": "bigint unsigned",
|
76
75
|
"PositiveIntegerField": "integer unsigned",
|
77
76
|
"PositiveSmallIntegerField": "smallint unsigned",
|
78
|
-
"SmallAutoField": "integer",
|
79
77
|
"SmallIntegerField": "smallint",
|
80
78
|
"TextField": "text",
|
81
79
|
"TimeField": "time",
|
@@ -88,9 +86,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
88
86
|
"PositiveSmallIntegerField": '"%(column)s" >= 0',
|
89
87
|
}
|
90
88
|
data_types_suffix = {
|
91
|
-
"
|
92
|
-
"BigAutoField": "AUTOINCREMENT",
|
93
|
-
"SmallAutoField": "AUTOINCREMENT",
|
89
|
+
"PrimaryKeyField": "AUTOINCREMENT",
|
94
90
|
}
|
95
91
|
# SQLite requires LIKE statements to include an ESCAPE clause if the value
|
96
92
|
# being escaped has a percent or underscore in it.
|
@@ -63,14 +63,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|
63
63
|
|
64
64
|
def get_field_type(self, data_type, description):
|
65
65
|
field_type = super().get_field_type(data_type, description)
|
66
|
-
if description.pk and field_type
|
67
|
-
"
|
68
|
-
"IntegerField",
|
69
|
-
"SmallIntegerField",
|
70
|
-
}:
|
71
|
-
# No support for BigAutoField or SmallAutoField as SQLite treats
|
72
|
-
# all integer primary keys as signed 64-bit integers.
|
73
|
-
return "AutoField"
|
66
|
+
if description.pk and field_type == "BigIntegerField":
|
67
|
+
return "PrimaryKeyField"
|
74
68
|
if description.has_json_constraint:
|
75
69
|
return "JSONField"
|
76
70
|
return field_type
|
@@ -23,9 +23,7 @@ from plain.models.db import (
|
|
23
23
|
from plain.models.deletion import Collector
|
24
24
|
from plain.models.expressions import RawSQL, Value
|
25
25
|
from plain.models.fields import NOT_PROVIDED
|
26
|
-
from plain.models.fields.
|
27
|
-
ForeignObjectRel,
|
28
|
-
)
|
26
|
+
from plain.models.fields.reverse_related import ForeignObjectRel
|
29
27
|
from plain.models.manager import Manager
|
30
28
|
from plain.models.options import Options
|
31
29
|
from plain.models.query import F, Q
|
@@ -334,22 +332,22 @@ class Model(metaclass=ModelBase):
|
|
334
332
|
return f"<{self.__class__.__name__}: {self}>"
|
335
333
|
|
336
334
|
def __str__(self):
|
337
|
-
return f"{self.__class__.__name__} object ({self.
|
335
|
+
return f"{self.__class__.__name__} object ({self.id})"
|
338
336
|
|
339
337
|
def __eq__(self, other):
|
340
338
|
if not isinstance(other, Model):
|
341
339
|
return NotImplemented
|
342
340
|
if self._meta.concrete_model != other._meta.concrete_model:
|
343
341
|
return False
|
344
|
-
|
345
|
-
if
|
342
|
+
my_id = self.id
|
343
|
+
if my_id is None:
|
346
344
|
return self is other
|
347
|
-
return
|
345
|
+
return my_id == other.id
|
348
346
|
|
349
347
|
def __hash__(self):
|
350
|
-
if self.
|
348
|
+
if self.id is None:
|
351
349
|
raise TypeError("Model instances without primary key value are unhashable")
|
352
|
-
return hash(self.
|
350
|
+
return hash(self.id)
|
353
351
|
|
354
352
|
def __reduce__(self):
|
355
353
|
data = self.__getstate__()
|
@@ -395,15 +393,6 @@ class Model(metaclass=ModelBase):
|
|
395
393
|
state[attr] = memoryview(value)
|
396
394
|
self.__dict__.update(state)
|
397
395
|
|
398
|
-
def _get_pk_val(self, meta=None):
|
399
|
-
meta = meta or self._meta
|
400
|
-
return getattr(self, meta.pk.attname)
|
401
|
-
|
402
|
-
def _set_pk_val(self, value):
|
403
|
-
return setattr(self, self._meta.pk.attname, value)
|
404
|
-
|
405
|
-
pk = property(_get_pk_val, _set_pk_val)
|
406
|
-
|
407
396
|
def get_deferred_fields(self):
|
408
397
|
"""
|
409
398
|
Return a set containing names of deferred fields for this instance.
|
@@ -445,7 +434,7 @@ class Model(metaclass=ModelBase):
|
|
445
434
|
"are not allowed in fields."
|
446
435
|
)
|
447
436
|
|
448
|
-
db_instance_qs = self.__class__._base_manager.get_queryset().filter(
|
437
|
+
db_instance_qs = self.__class__._base_manager.get_queryset().filter(id=self.id)
|
449
438
|
|
450
439
|
# Use provided fields, if not set then reload all non-deferred fields.
|
451
440
|
deferred_fields = self.get_deferred_fields()
|
@@ -608,12 +597,13 @@ class Model(metaclass=ModelBase):
|
|
608
597
|
if f.name in update_fields or f.attname in update_fields
|
609
598
|
]
|
610
599
|
|
611
|
-
|
612
|
-
if
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
600
|
+
id_val = self.id
|
601
|
+
if id_val is None:
|
602
|
+
id_field = meta.get_field("id")
|
603
|
+
id_val = id_field.get_id_value_on_save(self)
|
604
|
+
setattr(self, id_field.attname, id_val)
|
605
|
+
id_set = id_val is not None
|
606
|
+
if not id_set and (force_update or update_fields):
|
617
607
|
raise ValueError("Cannot force an update in save() with no primary key.")
|
618
608
|
updated = False
|
619
609
|
# Skip an UPDATE when adding an instance and primary key has a default.
|
@@ -621,12 +611,12 @@ class Model(metaclass=ModelBase):
|
|
621
611
|
not raw
|
622
612
|
and not force_insert
|
623
613
|
and self._state.adding
|
624
|
-
and meta.
|
625
|
-
and meta.
|
614
|
+
and meta.get_field("id").default
|
615
|
+
and meta.get_field("id").default is not NOT_PROVIDED
|
626
616
|
):
|
627
617
|
force_insert = True
|
628
618
|
# If possible, try an UPDATE. If that doesn't update anything, do an INSERT.
|
629
|
-
if
|
619
|
+
if id_set and not force_insert:
|
630
620
|
base_qs = cls._base_manager
|
631
621
|
values = [
|
632
622
|
(
|
@@ -638,7 +628,7 @@ class Model(metaclass=ModelBase):
|
|
638
628
|
]
|
639
629
|
forced_update = update_fields or force_update
|
640
630
|
updated = self._do_update(
|
641
|
-
base_qs,
|
631
|
+
base_qs, id_val, values, update_fields, forced_update
|
642
632
|
)
|
643
633
|
if force_update and not updated:
|
644
634
|
raise DatabaseError("Forced update did not affect any rows.")
|
@@ -646,8 +636,9 @@ class Model(metaclass=ModelBase):
|
|
646
636
|
raise DatabaseError("Save with update_fields did not affect any rows.")
|
647
637
|
if not updated:
|
648
638
|
fields = meta.local_concrete_fields
|
649
|
-
if not
|
650
|
-
|
639
|
+
if not id_set:
|
640
|
+
id_field = meta.get_field("id")
|
641
|
+
fields = [f for f in fields if f is not id_field]
|
651
642
|
|
652
643
|
returning_fields = meta.db_returning_fields
|
653
644
|
results = self._do_insert(cls._base_manager, fields, returning_fields, raw)
|
@@ -656,12 +647,12 @@ class Model(metaclass=ModelBase):
|
|
656
647
|
setattr(self, field.attname, value)
|
657
648
|
return updated
|
658
649
|
|
659
|
-
def _do_update(self, base_qs,
|
650
|
+
def _do_update(self, base_qs, id_val, values, update_fields, forced_update):
|
660
651
|
"""
|
661
652
|
Try to update the model. Return True if the model was updated (if an
|
662
653
|
update query was done and a matching row was found in the DB).
|
663
654
|
"""
|
664
|
-
filtered = base_qs.filter(
|
655
|
+
filtered = base_qs.filter(id=id_val)
|
665
656
|
if not values:
|
666
657
|
# We can end up here when saving a model in inheritance chain where
|
667
658
|
# update_fields doesn't target any field in current model. In that
|
@@ -701,7 +692,7 @@ class Model(metaclass=ModelBase):
|
|
701
692
|
# database to raise an IntegrityError if applicable. If
|
702
693
|
# constraints aren't supported by the database, there's the
|
703
694
|
# unavoidable risk of data corruption.
|
704
|
-
if obj.
|
695
|
+
if obj.id is None:
|
705
696
|
# Remove the object from a related instance cache.
|
706
697
|
if not field.remote_field.multiple:
|
707
698
|
field.remote_field.delete_cached_value(obj)
|
@@ -721,9 +712,9 @@ class Model(metaclass=ModelBase):
|
|
721
712
|
field.delete_cached_value(self)
|
722
713
|
|
723
714
|
def delete(self):
|
724
|
-
if self.
|
715
|
+
if self.id is None:
|
725
716
|
raise ValueError(
|
726
|
-
f"{self._meta.object_name} object can't be deleted because its
|
717
|
+
f"{self._meta.object_name} object can't be deleted because its id attribute is set "
|
727
718
|
"to None."
|
728
719
|
)
|
729
720
|
collector = Collector(origin=self)
|
@@ -739,17 +730,17 @@ class Model(metaclass=ModelBase):
|
|
739
730
|
)
|
740
731
|
|
741
732
|
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
|
742
|
-
if not self.
|
733
|
+
if not self.id:
|
743
734
|
raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
|
744
735
|
op = "gt" if is_next else "lt"
|
745
736
|
order = "" if is_next else "-"
|
746
737
|
param = getattr(self, field.attname)
|
747
|
-
q = Q.create([(field.name, param), (f"
|
738
|
+
q = Q.create([(field.name, param), (f"id__{op}", self.id)], connector=Q.AND)
|
748
739
|
q = Q.create([q, (f"{field.name}__{op}", param)], connector=Q.OR)
|
749
740
|
qs = (
|
750
741
|
self.__class__._default_manager.filter(**kwargs)
|
751
742
|
.filter(q)
|
752
|
-
.order_by(f"{order}{field.name}", f"{order}
|
743
|
+
.order_by(f"{order}{field.name}", f"{order}id")
|
753
744
|
)
|
754
745
|
try:
|
755
746
|
return qs[0]
|
@@ -769,7 +760,7 @@ class Model(metaclass=ModelBase):
|
|
769
760
|
}
|
770
761
|
|
771
762
|
def prepare_database_save(self, field):
|
772
|
-
if self.
|
763
|
+
if self.id is None:
|
773
764
|
raise ValueError(
|
774
765
|
f"Unsaved model instance {self!r} cannot be used in an ORM query."
|
775
766
|
)
|
@@ -849,13 +840,11 @@ class Model(metaclass=ModelBase):
|
|
849
840
|
|
850
841
|
# Exclude the current object from the query if we are editing an
|
851
842
|
# instance (as opposed to creating a new one)
|
852
|
-
#
|
853
|
-
#
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
if not self._state.adding and model_class_pk is not None:
|
858
|
-
qs = qs.exclude(pk=model_class_pk)
|
843
|
+
# Use the primary key defined by model_class. In previous versions
|
844
|
+
# this could differ from `self.id` due to model inheritance.
|
845
|
+
model_class_id = getattr(self, "id")
|
846
|
+
if not self._state.adding and model_class_id is not None:
|
847
|
+
qs = qs.exclude(id=model_class_id)
|
859
848
|
if qs.exists():
|
860
849
|
if len(unique_check) == 1:
|
861
850
|
key = unique_check[0]
|
@@ -1111,22 +1100,18 @@ class Model(metaclass=ModelBase):
|
|
1111
1100
|
|
1112
1101
|
@classmethod
|
1113
1102
|
def _check_id_field(cls):
|
1114
|
-
"""
|
1115
|
-
|
1116
|
-
f for f in cls._meta.local_fields if f.name == "id" and f
|
1117
|
-
|
1118
|
-
# fields is empty or consists of the invalid "id" field
|
1119
|
-
if fields and not fields[0].primary_key and cls._meta.pk.name == "id":
|
1103
|
+
"""Disallow user-defined fields named ``id``."""
|
1104
|
+
if any(
|
1105
|
+
f for f in cls._meta.local_fields if f.name == "id" and not f.auto_created
|
1106
|
+
):
|
1120
1107
|
return [
|
1121
1108
|
preflight.Error(
|
1122
|
-
"'id'
|
1123
|
-
"sets 'primary_key=True'.",
|
1109
|
+
"'id' is a reserved word that cannot be used as a field name.",
|
1124
1110
|
obj=cls,
|
1125
1111
|
id="models.E004",
|
1126
1112
|
)
|
1127
1113
|
]
|
1128
|
-
|
1129
|
-
return []
|
1114
|
+
return []
|
1130
1115
|
|
1131
1116
|
@classmethod
|
1132
1117
|
def _check_field_name_clashes(cls):
|
@@ -1428,11 +1413,7 @@ class Model(metaclass=ModelBase):
|
|
1428
1413
|
fld = None
|
1429
1414
|
for part in field.split(LOOKUP_SEP):
|
1430
1415
|
try:
|
1431
|
-
|
1432
|
-
if part == "pk":
|
1433
|
-
fld = _cls._meta.pk
|
1434
|
-
else:
|
1435
|
-
fld = _cls._meta.get_field(part)
|
1416
|
+
fld = _cls._meta.get_field(part)
|
1436
1417
|
if fld.is_relation:
|
1437
1418
|
_cls = fld.path_infos[-1].to_opts.model
|
1438
1419
|
else:
|
@@ -1450,10 +1431,6 @@ class Model(metaclass=ModelBase):
|
|
1450
1431
|
)
|
1451
1432
|
)
|
1452
1433
|
|
1453
|
-
# Skip ordering on pk. This is always a valid order_by field
|
1454
|
-
# but is an alias and therefore won't be found by opts.get_field.
|
1455
|
-
fields = {f for f in fields if f != "pk"}
|
1456
|
-
|
1457
1434
|
# Check for invalid or nonexistent fields in ordering.
|
1458
1435
|
invalid_fields = []
|
1459
1436
|
|
@@ -1469,7 +1446,7 @@ class Model(metaclass=ModelBase):
|
|
1469
1446
|
)
|
1470
1447
|
)
|
1471
1448
|
|
1472
|
-
invalid_fields.extend(fields - valid_fields)
|
1449
|
+
invalid_fields.extend(set(fields) - valid_fields)
|
1473
1450
|
|
1474
1451
|
for invalid_field in invalid_fields:
|
1475
1452
|
errors.append(
|
@@ -1712,17 +1689,12 @@ class Model(metaclass=ModelBase):
|
|
1712
1689
|
),
|
1713
1690
|
)
|
1714
1691
|
for field_name, *lookups in references:
|
1715
|
-
|
1716
|
-
if field_name != "pk":
|
1717
|
-
fields.add(field_name)
|
1692
|
+
fields.add(field_name)
|
1718
1693
|
if not lookups:
|
1719
1694
|
# If it has no lookups it cannot result in a JOIN.
|
1720
1695
|
continue
|
1721
1696
|
try:
|
1722
|
-
|
1723
|
-
field = cls._meta.pk
|
1724
|
-
else:
|
1725
|
-
field = cls._meta.get_field(field_name)
|
1697
|
+
field = cls._meta.get_field(field_name)
|
1726
1698
|
if not field.is_relation or field.many_to_many or field.one_to_many:
|
1727
1699
|
continue
|
1728
1700
|
except FieldDoesNotExist:
|
@@ -384,9 +384,9 @@ class UniqueConstraint(BaseConstraint):
|
|
384
384
|
expr = expr.expression
|
385
385
|
expressions.append(Exact(expr, expr.replace_expressions(replacements)))
|
386
386
|
queryset = queryset.filter(*expressions)
|
387
|
-
|
388
|
-
if not instance._state.adding and
|
389
|
-
queryset = queryset.exclude(
|
387
|
+
model_class_id = instance.id
|
388
|
+
if not instance._state.adding and model_class_id is not None:
|
389
|
+
queryset = queryset.exclude(id=model_class_id)
|
390
390
|
if not self.condition:
|
391
391
|
if queryset.exists():
|
392
392
|
if self.expressions:
|