plain.models 0.42.0__py3-none-any.whl → 0.43.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 +15 -0
- plain/models/base.py +3 -2
- plain/models/fields/related.py +6 -28
- plain/models/fields/related_descriptors.py +79 -633
- plain/models/fields/related_managers.py +629 -0
- plain/models/fields/reverse_related.py +4 -7
- {plain_models-0.42.0.dist-info → plain_models-0.43.0.dist-info}/METADATA +1 -1
- {plain_models-0.42.0.dist-info → plain_models-0.43.0.dist-info}/RECORD +11 -10
- {plain_models-0.42.0.dist-info → plain_models-0.43.0.dist-info}/WHEEL +0 -0
- {plain_models-0.42.0.dist-info → plain_models-0.43.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.42.0.dist-info → plain_models-0.43.0.dist-info}/licenses/LICENSE +0 -0
plain/models/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# plain-models changelog
|
2
2
|
|
3
|
+
## [0.43.0](https://github.com/dropseed/plain/releases/plain-models@0.43.0) (2025-09-12)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- The `related_name` parameter is now required for ForeignKey and ManyToManyField relationships if you want a reverse accessor. The `"+"` suffix to disable reverse relations has been removed, and automatic `_set` suffixes are no longer generated ([89fa03979f](https://github.com/dropseed/plain/commit/89fa03979f))
|
8
|
+
- Refactored related descriptors and managers for better internal organization and type safety ([9f0b03957a](https://github.com/dropseed/plain/commit/9f0b03957a))
|
9
|
+
- Added docstrings and return type annotations to model `query` property and related manager methods for improved developer experience ([544d85b60b](https://github.com/dropseed/plain/commit/544d85b60b))
|
10
|
+
|
11
|
+
### Upgrade instructions
|
12
|
+
|
13
|
+
- Remove any `related_name="+"` usage - if you don't want a reverse accessor, simply omit the `related_name` parameter entirely
|
14
|
+
- Update any code that relied on automatic `_set` suffixes - these are no longer generated, so you must use explicit `related_name` values
|
15
|
+
- Add explicit `related_name` arguments to all ForeignKey and ManyToManyField definitions where you want reverse access (e.g., `models.ForeignKey(User, on_delete=models.CASCADE, related_name="articles")`)
|
16
|
+
- Consider removing `related_name` arguments that are not used in practice
|
17
|
+
|
3
18
|
## [0.42.0](https://github.com/dropseed/plain/releases/plain-models@0.42.0) (2025-09-12)
|
4
19
|
|
5
20
|
### What's changed
|
plain/models/base.py
CHANGED
@@ -25,7 +25,7 @@ from plain.models.expressions import RawSQL, Value
|
|
25
25
|
from plain.models.fields import NOT_PROVIDED
|
26
26
|
from plain.models.fields.reverse_related import ForeignObjectRel
|
27
27
|
from plain.models.options import Options
|
28
|
-
from plain.models.query import F, Q
|
28
|
+
from plain.models.query import F, Q, QuerySet
|
29
29
|
from plain.packages import packages_registry
|
30
30
|
from plain.utils.encoding import force_str
|
31
31
|
from plain.utils.hashable import make_hashable
|
@@ -168,7 +168,8 @@ class ModelBase(type):
|
|
168
168
|
index.set_name_with_model(cls)
|
169
169
|
|
170
170
|
@property
|
171
|
-
def query(cls):
|
171
|
+
def query(cls) -> QuerySet:
|
172
|
+
"""Create a new QuerySet for this model."""
|
172
173
|
return cls._meta.queryset
|
173
174
|
|
174
175
|
|
plain/models/fields/related.py
CHANGED
@@ -12,8 +12,9 @@ from . import Field
|
|
12
12
|
from .mixins import FieldCacheMixin
|
13
13
|
from .related_descriptors import (
|
14
14
|
ForeignKeyDeferredAttribute,
|
15
|
+
ForwardManyToManyDescriptor,
|
15
16
|
ForwardManyToOneDescriptor,
|
16
|
-
|
17
|
+
ReverseManyToManyDescriptor,
|
17
18
|
ReverseManyToOneDescriptor,
|
18
19
|
)
|
19
20
|
from .related_lookups import (
|
@@ -123,14 +124,11 @@ class RelatedField(FieldCacheMixin, Field):
|
|
123
124
|
is_valid_id = (
|
124
125
|
not keyword.iskeyword(related_name) and related_name.isidentifier()
|
125
126
|
)
|
126
|
-
if not
|
127
|
+
if not is_valid_id:
|
127
128
|
return [
|
128
129
|
preflight.Error(
|
129
130
|
f"The name '{self.remote_field.related_name}' is invalid related_name for field {self.model._meta.object_name}.{self.name}",
|
130
|
-
hint=
|
131
|
-
"Related name must be a valid Python identifier or end with a "
|
132
|
-
"'+'"
|
133
|
-
),
|
131
|
+
hint="Related name must be a valid Python identifier.",
|
134
132
|
obj=self,
|
135
133
|
id="fields.E306",
|
136
134
|
)
|
@@ -1236,26 +1234,6 @@ class ManyToManyField(RelatedField):
|
|
1236
1234
|
return getattr(self, cache_attr)
|
1237
1235
|
|
1238
1236
|
def contribute_to_class(self, cls, name, **kwargs):
|
1239
|
-
# To support multiple relations to self, it's useful to have a non-None
|
1240
|
-
# related name on symmetrical relations for internal reasons. The
|
1241
|
-
# concept doesn't make a lot of sense externally ("you want me to
|
1242
|
-
# specify *what* on my non-reversible relation?!"), so we set it up
|
1243
|
-
# automatically. The funky name reduces the chance of an accidental
|
1244
|
-
# clash.
|
1245
|
-
if self.remote_field.symmetrical and (
|
1246
|
-
self.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT
|
1247
|
-
or self.remote_field.model == cls._meta.object_name
|
1248
|
-
):
|
1249
|
-
self.remote_field.related_name = f"{name}_rel_+"
|
1250
|
-
elif self.remote_field.is_hidden():
|
1251
|
-
# If the backwards relation is disabled, replace the original
|
1252
|
-
# related_name with one generated from the m2m field name. Plain
|
1253
|
-
# still uses backwards relations internally and we need to avoid
|
1254
|
-
# clashes between multiple m2m fields with related_name == '+'.
|
1255
|
-
self.remote_field.related_name = (
|
1256
|
-
f"_{cls._meta.package_label}_{cls.__name__.lower()}_{name}_+"
|
1257
|
-
)
|
1258
|
-
|
1259
1237
|
super().contribute_to_class(cls, name, **kwargs)
|
1260
1238
|
|
1261
1239
|
def resolve_through_model(_, model, field):
|
@@ -1266,7 +1244,7 @@ class ManyToManyField(RelatedField):
|
|
1266
1244
|
)
|
1267
1245
|
|
1268
1246
|
# Add the descriptor for the m2m relation.
|
1269
|
-
setattr(cls, self.name,
|
1247
|
+
setattr(cls, self.name, ForwardManyToManyDescriptor(self.remote_field))
|
1270
1248
|
|
1271
1249
|
# Set up the accessor for the m2m table name for the relation.
|
1272
1250
|
self.m2m_db_table = self._get_m2m_db_table
|
@@ -1278,7 +1256,7 @@ class ManyToManyField(RelatedField):
|
|
1278
1256
|
setattr(
|
1279
1257
|
cls,
|
1280
1258
|
related.get_accessor_name(),
|
1281
|
-
|
1259
|
+
ReverseManyToManyDescriptor(self.remote_field),
|
1282
1260
|
)
|
1283
1261
|
|
1284
1262
|
# Set up the accessors for the column names on the m2m table.
|