plain.models 0.41.1__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 +32 -0
- plain/models/README.md +65 -22
- plain/models/__init__.py +0 -2
- plain/models/base.py +14 -35
- plain/models/cli.py +16 -22
- plain/models/constraints.py +1 -1
- plain/models/deletion.py +1 -1
- plain/models/fields/__init__.py +1 -1
- plain/models/fields/related.py +7 -32
- plain/models/fields/related_descriptors.py +81 -631
- plain/models/fields/related_lookups.py +2 -2
- plain/models/fields/related_managers.py +629 -0
- plain/models/fields/reverse_related.py +5 -8
- plain/models/forms.py +1 -1
- plain/models/migrations/autodetector.py +0 -18
- plain/models/migrations/operations/__init__.py +0 -2
- plain/models/migrations/operations/models.py +3 -57
- plain/models/migrations/operations/special.py +2 -8
- plain/models/migrations/recorder.py +1 -1
- plain/models/migrations/serializer.py +0 -12
- plain/models/migrations/state.py +1 -55
- plain/models/options.py +23 -86
- plain/models/query.py +10 -41
- plain/models/query_utils.py +1 -1
- plain/models/sql/compiler.py +5 -5
- plain/models/sql/query.py +2 -2
- {plain_models-0.41.1.dist-info → plain_models-0.43.0.dist-info}/METADATA +66 -23
- {plain_models-0.41.1.dist-info → plain_models-0.43.0.dist-info}/RECORD +31 -31
- plain/models/manager.py +0 -176
- {plain_models-0.41.1.dist-info → plain_models-0.43.0.dist-info}/WHEEL +0 -0
- {plain_models-0.41.1.dist-info → plain_models-0.43.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.41.1.dist-info → plain_models-0.43.0.dist-info}/licenses/LICENSE +0 -0
plain/models/query.py
CHANGED
@@ -255,9 +255,8 @@ class FlatValuesListIterable(BaseIterable):
|
|
255
255
|
class QuerySet:
|
256
256
|
"""Represent a lazy database lookup for a set of objects."""
|
257
257
|
|
258
|
-
def __init__(self, model=None, query=None
|
258
|
+
def __init__(self, *, model=None, query=None):
|
259
259
|
self.model = model
|
260
|
-
self._hints = hints or {}
|
261
260
|
self._query = query or sql.Query(self.model)
|
262
261
|
self._result_cache = None
|
263
262
|
self._sticky_filter = False
|
@@ -284,17 +283,6 @@ class QuerySet:
|
|
284
283
|
self._iterable_class = ValuesIterable
|
285
284
|
self._query = value
|
286
285
|
|
287
|
-
def as_manager(cls):
|
288
|
-
# Address the circular dependency between `Queryset` and `Manager`.
|
289
|
-
from plain.models.manager import Manager
|
290
|
-
|
291
|
-
manager = Manager.from_queryset(cls)()
|
292
|
-
manager._built_with_as_manager = True
|
293
|
-
return manager
|
294
|
-
|
295
|
-
as_manager.queryset_only = True
|
296
|
-
as_manager = classmethod(as_manager)
|
297
|
-
|
298
286
|
########################
|
299
287
|
# PYTHON MAGIC METHODS #
|
300
288
|
########################
|
@@ -425,12 +413,12 @@ class QuerySet:
|
|
425
413
|
query = (
|
426
414
|
self
|
427
415
|
if self.query.can_filter()
|
428
|
-
else self.model.
|
416
|
+
else self.model._meta.base_queryset.filter(id__in=self.values("id"))
|
429
417
|
)
|
430
418
|
combined = query._chain()
|
431
419
|
combined._merge_known_related_objects(other)
|
432
420
|
if not other.query.can_filter():
|
433
|
-
other = other.model.
|
421
|
+
other = other.model._meta.base_queryset.filter(id__in=other.values("id"))
|
434
422
|
combined.query.combine(other.query, sql.OR)
|
435
423
|
return combined
|
436
424
|
|
@@ -444,12 +432,12 @@ class QuerySet:
|
|
444
432
|
query = (
|
445
433
|
self
|
446
434
|
if self.query.can_filter()
|
447
|
-
else self.model.
|
435
|
+
else self.model._meta.base_queryset.filter(id__in=self.values("id"))
|
448
436
|
)
|
449
437
|
combined = query._chain()
|
450
438
|
combined._merge_known_related_objects(other)
|
451
439
|
if not other.query.can_filter():
|
452
|
-
other = other.model.
|
440
|
+
other = other.model._meta.base_queryset.filter(id__in=other.values("id"))
|
453
441
|
combined.query.combine(other.query, sql.XOR)
|
454
442
|
return combined
|
455
443
|
|
@@ -957,8 +945,6 @@ class QuerySet:
|
|
957
945
|
self._result_cache = None
|
958
946
|
return deleted, _rows_count
|
959
947
|
|
960
|
-
delete.queryset_only = True
|
961
|
-
|
962
948
|
def _raw_delete(self):
|
963
949
|
"""
|
964
950
|
Delete objects found from the given queryset in single direct SQL
|
@@ -1027,8 +1013,6 @@ class QuerySet:
|
|
1027
1013
|
self._result_cache = None
|
1028
1014
|
return query.get_compiler().execute_sql(CURSOR)
|
1029
1015
|
|
1030
|
-
_update.queryset_only = False
|
1031
|
-
|
1032
1016
|
def exists(self):
|
1033
1017
|
"""
|
1034
1018
|
Return True if the QuerySet would have any results, False otherwise.
|
@@ -1201,7 +1185,7 @@ class QuerySet:
|
|
1201
1185
|
def all(self):
|
1202
1186
|
"""
|
1203
1187
|
Return a new QuerySet that is a copy of the current one. This allows a
|
1204
|
-
QuerySet to proxy for a model
|
1188
|
+
QuerySet to proxy for a model queryset in some cases.
|
1205
1189
|
"""
|
1206
1190
|
return self._chain()
|
1207
1191
|
|
@@ -1565,8 +1549,6 @@ class QuerySet:
|
|
1565
1549
|
query.insert_values(fields, objs, raw=raw)
|
1566
1550
|
return query.get_compiler().execute_sql(returning_fields)
|
1567
1551
|
|
1568
|
-
_insert.queryset_only = False
|
1569
|
-
|
1570
1552
|
def _batched_insert(
|
1571
1553
|
self,
|
1572
1554
|
objs,
|
@@ -1622,7 +1604,6 @@ class QuerySet:
|
|
1622
1604
|
c = self.__class__(
|
1623
1605
|
model=self.model,
|
1624
1606
|
query=self.query.chain(),
|
1625
|
-
hints=self._hints,
|
1626
1607
|
)
|
1627
1608
|
c._sticky_filter = self._sticky_filter
|
1628
1609
|
c._for_write = self._for_write
|
@@ -1678,15 +1659,6 @@ class QuerySet:
|
|
1678
1659
|
query = self.query.resolve_expression(*args, **kwargs)
|
1679
1660
|
return query
|
1680
1661
|
|
1681
|
-
resolve_expression.queryset_only = True
|
1682
|
-
|
1683
|
-
def _add_hints(self, **hints):
|
1684
|
-
"""
|
1685
|
-
Update hinting information for use by routers. Add new key/values or
|
1686
|
-
overwrite existing key/values.
|
1687
|
-
"""
|
1688
|
-
self._hints.update(hints)
|
1689
|
-
|
1690
1662
|
def _has_filters(self):
|
1691
1663
|
"""
|
1692
1664
|
Check if this QuerySet has any filtering going on. This isn't
|
@@ -1747,11 +1719,9 @@ class RawQuerySet:
|
|
1747
1719
|
query=None,
|
1748
1720
|
params=(),
|
1749
1721
|
translations=None,
|
1750
|
-
hints=None,
|
1751
1722
|
):
|
1752
1723
|
self.raw_query = raw_query
|
1753
1724
|
self.model = model
|
1754
|
-
self._hints = hints or {}
|
1755
1725
|
self.query = query or sql.RawQuery(sql=raw_query, params=params)
|
1756
1726
|
self.params = params
|
1757
1727
|
self.translations = translations or {}
|
@@ -1797,7 +1767,6 @@ class RawQuerySet:
|
|
1797
1767
|
query=self.query,
|
1798
1768
|
params=self.params,
|
1799
1769
|
translations=self.translations,
|
1800
|
-
hints=self._hints,
|
1801
1770
|
)
|
1802
1771
|
c._prefetch_related_lookups = self._prefetch_related_lookups[:]
|
1803
1772
|
return c
|
@@ -2180,7 +2149,7 @@ def prefetch_one_level(instances, prefetcher, lookup, level):
|
|
2180
2149
|
for additional_lookup in getattr(rel_qs, "_prefetch_related_lookups", ())
|
2181
2150
|
]
|
2182
2151
|
if additional_lookups:
|
2183
|
-
# Don't need to clone because the
|
2152
|
+
# Don't need to clone because the queryset should have given us a fresh
|
2184
2153
|
# instance, so we access an internal instead of using public interface
|
2185
2154
|
# for performance reasons.
|
2186
2155
|
rel_qs._prefetch_related_lookups = ()
|
@@ -2231,11 +2200,11 @@ def prefetch_one_level(instances, prefetcher, lookup, level):
|
|
2231
2200
|
if as_attr:
|
2232
2201
|
setattr(obj, to_attr, vals)
|
2233
2202
|
else:
|
2234
|
-
|
2203
|
+
queryset = getattr(obj, to_attr)
|
2235
2204
|
if leaf and lookup.queryset is not None:
|
2236
|
-
qs =
|
2205
|
+
qs = queryset._apply_rel_filters(lookup.queryset)
|
2237
2206
|
else:
|
2238
|
-
qs =
|
2207
|
+
qs = queryset.__class__(model=queryset.model)
|
2239
2208
|
qs._result_cache = vals
|
2240
2209
|
# We don't want the individual qs doing prefetch_related now,
|
2241
2210
|
# since we have merged this into the current work.
|
plain/models/query_utils.py
CHANGED
@@ -361,7 +361,7 @@ def check_rel_lookup_compatibility(model, target_opts, field):
|
|
361
361
|
# model is ok, too. Consider the case:
|
362
362
|
# class Restaurant(models.Model):
|
363
363
|
# place = OneToOneField(Place, primary_key=True):
|
364
|
-
# Restaurant.
|
364
|
+
# Restaurant.query.filter(id__in=Restaurant.query.all()).
|
365
365
|
# If we didn't have the primary key check, then id__in (== place__in) would
|
366
366
|
# give Place's opts as the target opts, but Restaurant isn't compatible
|
367
367
|
# with that. This logic applies only to primary keys, as when doing __in=qs,
|
plain/models/sql/compiler.py
CHANGED
@@ -98,20 +98,20 @@ class SQLCompiler:
|
|
98
98
|
then it is correct".
|
99
99
|
"""
|
100
100
|
# Some examples:
|
101
|
-
# SomeModel.
|
101
|
+
# SomeModel.query.annotate(Count('somecol'))
|
102
102
|
# GROUP BY: all fields of the model
|
103
103
|
#
|
104
|
-
# SomeModel.
|
104
|
+
# SomeModel.query.values('name').annotate(Count('somecol'))
|
105
105
|
# GROUP BY: name
|
106
106
|
#
|
107
|
-
# SomeModel.
|
107
|
+
# SomeModel.query.annotate(Count('somecol')).values('name')
|
108
108
|
# GROUP BY: all cols of the model
|
109
109
|
#
|
110
|
-
# SomeModel.
|
110
|
+
# SomeModel.query.values('name', 'id')
|
111
111
|
# .annotate(Count('somecol')).values('id')
|
112
112
|
# GROUP BY: name, id
|
113
113
|
#
|
114
|
-
# SomeModel.
|
114
|
+
# SomeModel.query.values('name').annotate(Count('somecol')).values('id')
|
115
115
|
# GROUP BY: name, id
|
116
116
|
#
|
117
117
|
# In fact, the self.query.group_by is the minimal set to GROUP BY. It
|
plain/models/sql/query.py
CHANGED
@@ -1174,9 +1174,9 @@ class Query(BaseExpression):
|
|
1174
1174
|
"""Check the type of object passed to query relations."""
|
1175
1175
|
if field.is_relation:
|
1176
1176
|
# Check that the field and the queryset use the same model in a
|
1177
|
-
# query like .filter(author=Author.
|
1177
|
+
# query like .filter(author=Author.query.all()). For example, the
|
1178
1178
|
# opts would be Author's (from the author field) and value.model
|
1179
|
-
# would be Author.
|
1179
|
+
# would be Author.query.all() queryset's .model (Author also).
|
1180
1180
|
# The field is the related field on the lhs side.
|
1181
1181
|
if (
|
1182
1182
|
isinstance(value, Query)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: plain.models
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.43.0
|
4
4
|
Summary: Model your data and store it in a database.
|
5
5
|
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
6
6
|
License-File: LICENSE
|
@@ -20,7 +20,7 @@ Description-Content-Type: text/markdown
|
|
20
20
|
- [Fields](#fields)
|
21
21
|
- [Validation](#validation)
|
22
22
|
- [Indexes and constraints](#indexes-and-constraints)
|
23
|
-
- [
|
23
|
+
- [Custom QuerySets](#custom-querysets)
|
24
24
|
- [Forms](#forms)
|
25
25
|
- [Sharing fields across models](#sharing-fields-across-models)
|
26
26
|
- [Installation](#installation)
|
@@ -54,7 +54,7 @@ from .models import User
|
|
54
54
|
|
55
55
|
|
56
56
|
# Create a new user
|
57
|
-
user = User.
|
57
|
+
user = User.query.create(
|
58
58
|
email="test@example.com",
|
59
59
|
password="password",
|
60
60
|
)
|
@@ -67,7 +67,7 @@ user.save()
|
|
67
67
|
user.delete()
|
68
68
|
|
69
69
|
# Query for users
|
70
|
-
admin_users = User.
|
70
|
+
admin_users = User.query.filter(is_admin=True)
|
71
71
|
```
|
72
72
|
|
73
73
|
## Database connection
|
@@ -96,30 +96,30 @@ Multiple backends are supported, including Postgres, MySQL, and SQLite.
|
|
96
96
|
|
97
97
|
## Querying
|
98
98
|
|
99
|
-
Models come with a powerful query API through their [`
|
99
|
+
Models come with a powerful query API through their [`QuerySet`](./query.py#QuerySet) interface:
|
100
100
|
|
101
101
|
```python
|
102
102
|
# Get all users
|
103
|
-
all_users = User.
|
103
|
+
all_users = User.query.all()
|
104
104
|
|
105
105
|
# Filter users
|
106
|
-
admin_users = User.
|
107
|
-
recent_users = User.
|
106
|
+
admin_users = User.query.filter(is_admin=True)
|
107
|
+
recent_users = User.query.filter(created_at__gte=datetime.now() - timedelta(days=7))
|
108
108
|
|
109
109
|
# Get a single user
|
110
|
-
user = User.
|
110
|
+
user = User.query.get(email="test@example.com")
|
111
111
|
|
112
112
|
# Complex queries with Q objects
|
113
113
|
from plain.models import Q
|
114
|
-
users = User.
|
114
|
+
users = User.query.filter(
|
115
115
|
Q(is_admin=True) | Q(email__endswith="@example.com")
|
116
116
|
)
|
117
117
|
|
118
118
|
# Ordering
|
119
|
-
users = User.
|
119
|
+
users = User.query.order_by("-created_at")
|
120
120
|
|
121
121
|
# Limiting results
|
122
|
-
first_10_users = User.
|
122
|
+
first_10_users = User.query.all()[:10]
|
123
123
|
```
|
124
124
|
|
125
125
|
For more advanced querying options, see the [`QuerySet`](./query.py#QuerySet) class.
|
@@ -222,28 +222,71 @@ class User(models.Model):
|
|
222
222
|
]
|
223
223
|
```
|
224
224
|
|
225
|
-
##
|
225
|
+
## Custom QuerySets
|
226
226
|
|
227
|
-
[`
|
227
|
+
With the Manager functionality now merged into QuerySet, you can customize [`QuerySet`](./query.py#QuerySet) classes to provide specialized query methods. There are several ways to use custom QuerySets:
|
228
|
+
|
229
|
+
### Setting a default QuerySet for a model
|
230
|
+
|
231
|
+
Use `Meta.queryset_class` to set a custom QuerySet that will be used by `Model.query`:
|
232
|
+
|
233
|
+
```python
|
234
|
+
class PublishedQuerySet(models.QuerySet):
|
235
|
+
def published_only(self):
|
236
|
+
return self.filter(status="published")
|
237
|
+
|
238
|
+
def draft_only(self):
|
239
|
+
return self.filter(status="draft")
|
240
|
+
|
241
|
+
@models.register_model
|
242
|
+
class Article(models.Model):
|
243
|
+
title = models.CharField(max_length=200)
|
244
|
+
status = models.CharField(max_length=20)
|
245
|
+
|
246
|
+
class Meta:
|
247
|
+
queryset_class = PublishedQuerySet
|
248
|
+
|
249
|
+
# Usage - all methods available on Article.objects
|
250
|
+
all_articles = Article.query.all()
|
251
|
+
published_articles = Article.query.published_only()
|
252
|
+
draft_articles = Article.query.draft_only()
|
253
|
+
```
|
254
|
+
|
255
|
+
### Using custom QuerySets without formal attachment
|
256
|
+
|
257
|
+
You can also use custom QuerySets manually without setting them as the default:
|
228
258
|
|
229
259
|
```python
|
230
|
-
class
|
231
|
-
def
|
232
|
-
return
|
260
|
+
class SpecialQuerySet(models.QuerySet):
|
261
|
+
def special_filter(self):
|
262
|
+
return self.filter(special=True)
|
233
263
|
|
264
|
+
# Create and use the QuerySet manually
|
265
|
+
special_qs = SpecialQuerySet(model=Article)
|
266
|
+
special_articles = special_qs.special_filter()
|
267
|
+
```
|
268
|
+
|
269
|
+
### Using classmethods for convenience
|
270
|
+
|
271
|
+
For even cleaner API, add classmethods to your model:
|
272
|
+
|
273
|
+
```python
|
274
|
+
@models.register_model
|
234
275
|
class Article(models.Model):
|
235
276
|
title = models.CharField(max_length=200)
|
236
277
|
status = models.CharField(max_length=20)
|
237
278
|
|
238
|
-
|
239
|
-
|
279
|
+
@classmethod
|
280
|
+
def published(cls):
|
281
|
+
return PublishedQuerySet(model=cls).published_only()
|
240
282
|
|
241
|
-
|
242
|
-
|
283
|
+
@classmethod
|
284
|
+
def drafts(cls):
|
285
|
+
return PublishedQuerySet(model=cls).draft_only()
|
243
286
|
|
244
287
|
# Usage
|
245
|
-
|
246
|
-
|
288
|
+
published_articles = Article.published()
|
289
|
+
draft_articles = Article.drafts()
|
247
290
|
```
|
248
291
|
|
249
292
|
## Forms
|
@@ -1,31 +1,30 @@
|
|
1
1
|
plain/models/AGENTS.md,sha256=xQQW-z-DehnCUyjiGSBfLqUjoSUdo_W1b0JmwYmWieA,209
|
2
|
-
plain/models/CHANGELOG.md,sha256=
|
3
|
-
plain/models/README.md,sha256=
|
4
|
-
plain/models/__init__.py,sha256=
|
2
|
+
plain/models/CHANGELOG.md,sha256=yPHtgiBY7hwtydus3mAiT945vgMTO5c9pF64KC9V37s,14530
|
3
|
+
plain/models/README.md,sha256=lqzWJrEIxBCHC1P8X1YoRjbsMFlu0-kG4ujP76B_ZO4,8572
|
4
|
+
plain/models/__init__.py,sha256=aB9HhIKBh0iK3LZztInAE-rDF-yKsdfcjfMtwtN5vnI,2920
|
5
5
|
plain/models/aggregates.py,sha256=P0mhsMl1VZt2CVHMuCHnNI8SxZ9citjDLEgioN6NOpo,7240
|
6
|
-
plain/models/base.py,sha256=
|
7
|
-
plain/models/cli.py,sha256=
|
6
|
+
plain/models/base.py,sha256=Yp08EC4I0wQ65AXGNUe3mgM01zmuJ6B6XfFyCkcsmEc,66161
|
7
|
+
plain/models/cli.py,sha256=lhvlw7DVOU3CVoMUpHaj7qIbQ2d6qtSm_l9PdCeCnc0,36404
|
8
8
|
plain/models/config.py,sha256=OF7eIEtXNZyGwgc3eMEpb5uEAup5RXeT-0um60dfBeU,636
|
9
9
|
plain/models/connections.py,sha256=RBNa2FZ0x3C9un6PaYL-IYzH_OesRSpdHNGKvYHGiOM,2276
|
10
10
|
plain/models/constants.py,sha256=ndnj9TOTKW0p4YcIPLOLEbsH6mOgFi6B1-rIzr_iwwU,210
|
11
|
-
plain/models/constraints.py,sha256=
|
11
|
+
plain/models/constraints.py,sha256=Mm9gm5D7EKmo486dL481-hrTcxi2gxgqyUUtbGrkLjs,16749
|
12
12
|
plain/models/database_url.py,sha256=iidKVhOylf5N6t1EMPRySRQiv6LiuRjYRECB_UJ3MI8,6419
|
13
13
|
plain/models/db.py,sha256=FpdfLYrRX2THUzDy4QdJ_OpSo9IFKLerZIEQ-T2x8zA,1348
|
14
14
|
plain/models/default_settings.py,sha256=cDym1o_DtHySWgDRIdjgEM0YxjgYU51ZqzWVA3vpzTk,569
|
15
|
-
plain/models/deletion.py,sha256=
|
15
|
+
plain/models/deletion.py,sha256=3eW3bb39NbIlIAp4ZAEtSZuiy9oH4i5e83OK_8oIaxI,17607
|
16
16
|
plain/models/entrypoints.py,sha256=EC14mW19tK9dCumaNHnv4_9jQV8jomQ8jXy8Ib89VBw,191
|
17
17
|
plain/models/enums.py,sha256=Zr-JKt2aeYsSADtAm69fDRfajS7jYwop2vWQVLJ9YYI,2726
|
18
18
|
plain/models/exceptions.py,sha256=IqzK60-hY3TYsgOMxlWwgpVa21E7ydC-gqUG4tNvVJc,2042
|
19
19
|
plain/models/expressions.py,sha256=hN6sfOxqxpP0qmYOUotsFAAn2-bnl35iHwyINyXA7CI,62763
|
20
|
-
plain/models/forms.py,sha256=
|
20
|
+
plain/models/forms.py,sha256=VreclYMITYh3D-sfZV3iSQH9QjNmwNwIYQL_90owOVM,25805
|
21
21
|
plain/models/indexes.py,sha256=fazIZPJgCX5_Bhwk7MQy3YbWOxpHvaCe1dDLGGldTuY,11540
|
22
22
|
plain/models/lookups.py,sha256=eCsxQXUcOoAa_U_fAAd3edcgXI1wfyFW8hPgUh8TwTo,24776
|
23
|
-
plain/models/
|
24
|
-
plain/models/options.py,sha256=1EtFAXG3RqwoT8DzRKUmZvfXlhk98MvCf4q4FG6jBN4,23572
|
23
|
+
plain/models/options.py,sha256=BOnu9NDVcgL0tJhan5gBbaK1SWNeg4NVTPNAzkKT3NE,21528
|
25
24
|
plain/models/otel.py,sha256=36QSJS6UXv1YPJTqeSmEvdMVHRkXa_zgqqItJaXc59g,7619
|
26
25
|
plain/models/preflight.py,sha256=PlS1S2YHEpSKZ57KbTP6TbED98dDDXYSBUk6xMIpgsI,8136
|
27
|
-
plain/models/query.py,sha256=
|
28
|
-
plain/models/query_utils.py,sha256=
|
26
|
+
plain/models/query.py,sha256=6t0ow7oQfVB6WiC3fQ3kLme8TBeSOscEt5xl2lu6oOQ,89703
|
27
|
+
plain/models/query_utils.py,sha256=zxAdfwDbOmaN_SJODl4Wl9gs-q2EzOjXbsBFTWWhh8g,14174
|
29
28
|
plain/models/registry.py,sha256=5yxVgT_W8GlyL2bsGT2HvMQB5sKolXucP2qrhr7Wlnk,8126
|
30
29
|
plain/models/transaction.py,sha256=KqkRDT6aqMgbPA_ch7qO8a9NyDvwY_2FaxM7FkBkcgY,9357
|
31
30
|
plain/models/utils.py,sha256=rD47CAMH4SsznTe-kUnRUdnaZeZHVv1fwLUiU3KOFW0,1630
|
@@ -72,13 +71,14 @@ plain/models/backups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
72
71
|
plain/models/backups/cli.py,sha256=AKVh_Go0LxnxFi2jQjmpPEbbJvYCurjJsGCWESm1X8A,2960
|
73
72
|
plain/models/backups/clients.py,sha256=WQ4X4KrkNeiKYJcpGRwnfRiAeeyduHWEnq4tA4mtjvw,4047
|
74
73
|
plain/models/backups/core.py,sha256=09IZUhBEe1Yej3PC8AidtkaI0c8tt7VnqGBCWK-WrFg,3318
|
75
|
-
plain/models/fields/__init__.py,sha256=
|
74
|
+
plain/models/fields/__init__.py,sha256=HrGTFOeG8Bt5pHWmiClmdn1qZcvCOIcXx4a5RKiIdr4,77938
|
76
75
|
plain/models/fields/json.py,sha256=OdGW4EYBSHQgkuugB-EiIXOqGstUqcMKUvOTOVUHqEQ,18049
|
77
76
|
plain/models/fields/mixins.py,sha256=K_ocrSbb6pPuGwYZeqgzoZskwCIMFIB6IV3T5CrU5J0,1805
|
78
|
-
plain/models/fields/related.py,sha256=
|
79
|
-
plain/models/fields/related_descriptors.py,sha256=
|
80
|
-
plain/models/fields/related_lookups.py,sha256=
|
81
|
-
plain/models/fields/
|
77
|
+
plain/models/fields/related.py,sha256=W5pvb_J0AulyQmd-q8Vzh4b9GT7IDmeAaJNCZhsMqkw,49698
|
78
|
+
plain/models/fields/related_descriptors.py,sha256=nsVgLjpOlrha90eTfg7ad_il6_uI_YG0d4bH51LP3Os,15180
|
79
|
+
plain/models/fields/related_lookups.py,sha256=9y6AfEcg8xRRZne2LXFP6jym9mecFlB_toYih7lD8Uw,7781
|
80
|
+
plain/models/fields/related_managers.py,sha256=XiV2IvuEFLEWnej2KokCu6HFx87UyZTqqsP74v2xIHw,25011
|
81
|
+
plain/models/fields/reverse_related.py,sha256=SNFytCI3BeAlB5kY6UQrv6QGqtRMo_aHWPx_-fCkfu4,10404
|
82
82
|
plain/models/functions/__init__.py,sha256=aglCm_JtzDYk2KmxubDN_78CGG3JCfRWnfJ74Oj5YJ4,2658
|
83
83
|
plain/models/functions/comparison.py,sha256=9uAiEuNXZiGFzJKBvktsHwx58Qpa2cPQkr6pUWsGcKo,6554
|
84
84
|
plain/models/functions/datetime.py,sha256=ov1H0Oq9qHSeu8L5CZsln0_SMU2C5M_P5HvKxppA24o,13089
|
@@ -87,7 +87,7 @@ plain/models/functions/mixins.py,sha256=s6H0Ejugo8707EnxN6zS4givmTbPrFuk3Q_mqCGQ
|
|
87
87
|
plain/models/functions/text.py,sha256=Vn4Rh-cVJCL4NUaBcj72cwHa6yHap5ql0XPwn0-K_tc,8908
|
88
88
|
plain/models/functions/window.py,sha256=3S0QIZc_pIVcWpE5Qq-OxixmtATLb8rZrWkvCfVtJ7Q,2809
|
89
89
|
plain/models/migrations/__init__.py,sha256=ZAQUGrfr_OxYMIO7vUBIHLs_M3oZ4iQSjDzCHRFUdtI,96
|
90
|
-
plain/models/migrations/autodetector.py,sha256=
|
90
|
+
plain/models/migrations/autodetector.py,sha256=MuJEVaU9IZ8c6HfcDsEANNujUrSGMN-iDd5WDPM7ht8,60957
|
91
91
|
plain/models/migrations/exceptions.py,sha256=_bGjIMaBP2Py9ePUxUhiH0p1zXrQM4JhJO4lWfyF8-g,1044
|
92
92
|
plain/models/migrations/executor.py,sha256=2_1bWM7Dp3s8z6PADAEN-Y0KnIiRzAqsUkn_nRQl5TA,6757
|
93
93
|
plain/models/migrations/graph.py,sha256=nrztu_8dU0wAUSxKUqqFWpvZcSQxGEqE6dXWkPytmCU,12570
|
@@ -95,28 +95,28 @@ plain/models/migrations/loader.py,sha256=qUTmaEYI1_mV6goQPQYZKjSz8rMbE6G1wqvrAsm
|
|
95
95
|
plain/models/migrations/migration.py,sha256=22YwRHnaRnCkBpW5p7K89tAU6h4QSsG5yiq-o7W-cSI,6505
|
96
96
|
plain/models/migrations/optimizer.py,sha256=HH-uz-jnWw_Ni6F2_rRW1nax1Dxmf1s_F_8s8N2OlVc,3266
|
97
97
|
plain/models/migrations/questioner.py,sha256=qAsePI5JHiSJrlY_kmpgMuK9Dom22q17edov7RtBeGw,11967
|
98
|
-
plain/models/migrations/recorder.py,sha256=
|
99
|
-
plain/models/migrations/serializer.py,sha256=
|
100
|
-
plain/models/migrations/state.py,sha256=
|
98
|
+
plain/models/migrations/recorder.py,sha256=_ncIVLJ4ns6AaO0vVmCoXfABlOFXDKu8NTPsutqKjK4,3653
|
99
|
+
plain/models/migrations/serializer.py,sha256=yBP9TyUZlSh_8qdw1I4VotbsZqoomz3mBs7ASQLsDH8,12459
|
100
|
+
plain/models/migrations/state.py,sha256=Q70cByjFgb8UG-wWqnLntf5s0hNv5DuDv0Bw1ovJ0yg,32116
|
101
101
|
plain/models/migrations/utils.py,sha256=Ih_mu6UbdUSt-ZtHaB0xIXHDrBANuFZyftTQ56BFJYs,4174
|
102
102
|
plain/models/migrations/writer.py,sha256=N8Rnjv5ccsA_CTcS7zZyppzyHFOUQVJy0l6RZYjwF-0,10981
|
103
|
-
plain/models/migrations/operations/__init__.py,sha256=
|
103
|
+
plain/models/migrations/operations/__init__.py,sha256=YKZsQsJ4G5iw9F4o6dOSgSCuLiiKuApvneoV4jqjZaA,752
|
104
104
|
plain/models/migrations/operations/base.py,sha256=JsKGjM6ouvEbFHzV14km7YjkpOUC4PoUR1M2yGZ82bE,4323
|
105
105
|
plain/models/migrations/operations/fields.py,sha256=ARL945rbztAnMsbd0lvQRsQJEmxYA3gDof0-4aOTeC4,11255
|
106
|
-
plain/models/migrations/operations/models.py,sha256=
|
107
|
-
plain/models/migrations/operations/special.py,sha256=
|
106
|
+
plain/models/migrations/operations/models.py,sha256=jwhck8ygfi8cW8baVeGUB6coVVv4HnGVSFVb2IW_aTU,24947
|
107
|
+
plain/models/migrations/operations/special.py,sha256=cKieE9AQcFrpvEw5_TJCum56uu6cfN1p-azY7sKdB7E,4944
|
108
108
|
plain/models/sql/__init__.py,sha256=FoRCcab-kh_XY8C4eldgLy9-zuk-M63Nyi9cFsYjclU,225
|
109
|
-
plain/models/sql/compiler.py,sha256=
|
109
|
+
plain/models/sql/compiler.py,sha256=FVa_v4Q8izuFc9VQTrW4jAqM1uqRnTVdOeP7IKt3yM8,84691
|
110
110
|
plain/models/sql/constants.py,sha256=usb1LSh9WNGPsurWAGppDkV0wYJJg5GEegKibQdS718,533
|
111
111
|
plain/models/sql/datastructures.py,sha256=FC88CVCukLyU621JrmKLBhmgvotEHgAhIOYfVvJpuR0,7084
|
112
|
-
plain/models/sql/query.py,sha256=
|
112
|
+
plain/models/sql/query.py,sha256=ASR6jeRTjGlnO5E_hb5cnUYQH2_JrpHa_M545xOG7dg,108745
|
113
113
|
plain/models/sql/subqueries.py,sha256=1YYlgoDrx_mW19MWWawLdgDTJnLgvvkGBQ30YQ702W4,5860
|
114
114
|
plain/models/sql/where.py,sha256=ezE9Clt2BmKo-I7ARsgqZ_aVA-1UdayCwr6ULSWZL6c,12635
|
115
115
|
plain/models/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
116
116
|
plain/models/test/pytest.py,sha256=KD5-mxonBxOYIhUh9Ql5uJOIiC9R4t-LYfb6sjA0UdE,3486
|
117
117
|
plain/models/test/utils.py,sha256=S3d6zf3OFWDxB_kBJr0tDvwn51bjwDVWKPumv37N-p8,467
|
118
|
-
plain_models-0.
|
119
|
-
plain_models-0.
|
120
|
-
plain_models-0.
|
121
|
-
plain_models-0.
|
122
|
-
plain_models-0.
|
118
|
+
plain_models-0.43.0.dist-info/METADATA,sha256=1lUO79n5x4G0s1FzZinEIm5izVs3R-ii1NOR8ecaHvA,8884
|
119
|
+
plain_models-0.43.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
120
|
+
plain_models-0.43.0.dist-info/entry_points.txt,sha256=IYJAW9MpL3PXyXFWmKmALagAGXC_5rzBn2eEGJlcV04,112
|
121
|
+
plain_models-0.43.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
122
|
+
plain_models-0.43.0.dist-info/RECORD,,
|
plain/models/manager.py
DELETED
@@ -1,176 +0,0 @@
|
|
1
|
-
import inspect
|
2
|
-
from functools import wraps
|
3
|
-
from importlib import import_module
|
4
|
-
|
5
|
-
from plain.models.query import QuerySet
|
6
|
-
|
7
|
-
|
8
|
-
class BaseManager:
|
9
|
-
# To retain order, track each time a Manager instance is created.
|
10
|
-
creation_counter = 0
|
11
|
-
|
12
|
-
# Set to True for the 'objects' managers that are automatically created.
|
13
|
-
auto_created = False
|
14
|
-
|
15
|
-
#: If set to True the manager will be serialized into migrations and will
|
16
|
-
#: thus be available in e.g. RunPython operations.
|
17
|
-
use_in_migrations = False
|
18
|
-
|
19
|
-
def __new__(cls, *args, **kwargs):
|
20
|
-
# Capture the arguments to make returning them trivial.
|
21
|
-
obj = super().__new__(cls)
|
22
|
-
obj._constructor_args = (args, kwargs)
|
23
|
-
return obj
|
24
|
-
|
25
|
-
def __init__(self):
|
26
|
-
super().__init__()
|
27
|
-
self._set_creation_counter()
|
28
|
-
self.model = None
|
29
|
-
self.name = None
|
30
|
-
self._hints = {}
|
31
|
-
|
32
|
-
def __str__(self):
|
33
|
-
"""Return "package_label.model_label.manager_name"."""
|
34
|
-
return f"{self.model._meta.label}.{self.name}"
|
35
|
-
|
36
|
-
def __class_getitem__(cls, *args, **kwargs):
|
37
|
-
return cls
|
38
|
-
|
39
|
-
def deconstruct(self):
|
40
|
-
"""
|
41
|
-
Return a 5-tuple of the form (as_manager (True), manager_class,
|
42
|
-
queryset_class, args, kwargs).
|
43
|
-
|
44
|
-
Raise a ValueError if the manager is dynamically generated.
|
45
|
-
"""
|
46
|
-
qs_class = self._queryset_class
|
47
|
-
if getattr(self, "_built_with_as_manager", False):
|
48
|
-
# using MyQuerySet.as_manager()
|
49
|
-
return (
|
50
|
-
True, # as_manager
|
51
|
-
None, # manager_class
|
52
|
-
f"{qs_class.__module__}.{qs_class.__name__}", # qs_class
|
53
|
-
None, # args
|
54
|
-
None, # kwargs
|
55
|
-
)
|
56
|
-
else:
|
57
|
-
module_name = self.__module__
|
58
|
-
name = self.__class__.__name__
|
59
|
-
# Make sure it's actually there and not an inner class
|
60
|
-
module = import_module(module_name)
|
61
|
-
if not hasattr(module, name):
|
62
|
-
raise ValueError(
|
63
|
-
f"Could not find manager {name} in {module_name}.\n"
|
64
|
-
"Please note that you need to inherit from managers you "
|
65
|
-
"dynamically generated with 'from_queryset()'."
|
66
|
-
)
|
67
|
-
return (
|
68
|
-
False, # as_manager
|
69
|
-
f"{module_name}.{name}", # manager_class
|
70
|
-
None, # qs_class
|
71
|
-
self._constructor_args[0], # args
|
72
|
-
self._constructor_args[1], # kwargs
|
73
|
-
)
|
74
|
-
|
75
|
-
def check(self, **kwargs):
|
76
|
-
return []
|
77
|
-
|
78
|
-
@classmethod
|
79
|
-
def _get_queryset_methods(cls, queryset_class):
|
80
|
-
def create_method(name, method):
|
81
|
-
@wraps(method)
|
82
|
-
def manager_method(self, *args, **kwargs):
|
83
|
-
return getattr(self.get_queryset(), name)(*args, **kwargs)
|
84
|
-
|
85
|
-
return manager_method
|
86
|
-
|
87
|
-
new_methods = {}
|
88
|
-
for name, method in inspect.getmembers(
|
89
|
-
queryset_class, predicate=inspect.isfunction
|
90
|
-
):
|
91
|
-
# Only copy missing methods.
|
92
|
-
if hasattr(cls, name):
|
93
|
-
continue
|
94
|
-
# Only copy public methods or methods with the attribute
|
95
|
-
# queryset_only=False.
|
96
|
-
queryset_only = getattr(method, "queryset_only", None)
|
97
|
-
if queryset_only or (queryset_only is None and name.startswith("_")):
|
98
|
-
continue
|
99
|
-
# Copy the method onto the manager.
|
100
|
-
new_methods[name] = create_method(name, method)
|
101
|
-
return new_methods
|
102
|
-
|
103
|
-
@classmethod
|
104
|
-
def from_queryset(cls, queryset_class, class_name=None):
|
105
|
-
if class_name is None:
|
106
|
-
class_name = f"{cls.__name__}From{queryset_class.__name__}"
|
107
|
-
return type(
|
108
|
-
class_name,
|
109
|
-
(cls,),
|
110
|
-
{
|
111
|
-
"_queryset_class": queryset_class,
|
112
|
-
**cls._get_queryset_methods(queryset_class),
|
113
|
-
},
|
114
|
-
)
|
115
|
-
|
116
|
-
def contribute_to_class(self, cls, name):
|
117
|
-
self.name = self.name or name
|
118
|
-
self.model = cls
|
119
|
-
|
120
|
-
setattr(cls, name, ManagerDescriptor(self))
|
121
|
-
|
122
|
-
cls._meta.add_manager(self)
|
123
|
-
|
124
|
-
def _set_creation_counter(self):
|
125
|
-
"""
|
126
|
-
Set the creation counter value for this instance and increment the
|
127
|
-
class-level copy.
|
128
|
-
"""
|
129
|
-
self.creation_counter = BaseManager.creation_counter
|
130
|
-
BaseManager.creation_counter += 1
|
131
|
-
|
132
|
-
#######################
|
133
|
-
# PROXIES TO QUERYSET #
|
134
|
-
#######################
|
135
|
-
|
136
|
-
def get_queryset(self):
|
137
|
-
"""
|
138
|
-
Return a new QuerySet object. Subclasses can override this method to
|
139
|
-
customize the behavior of the Manager.
|
140
|
-
"""
|
141
|
-
return self._queryset_class(model=self.model, hints=self._hints)
|
142
|
-
|
143
|
-
def all(self):
|
144
|
-
# We can't proxy this method through the `QuerySet` like we do for the
|
145
|
-
# rest of the `QuerySet` methods. This is because `QuerySet.all()`
|
146
|
-
# works by creating a "copy" of the current queryset and in making said
|
147
|
-
# copy, all the cached `prefetch_related` lookups are lost. See the
|
148
|
-
# implementation of `RelatedManager.get_queryset()` for a better
|
149
|
-
# understanding of how this comes into play.
|
150
|
-
return self.get_queryset()
|
151
|
-
|
152
|
-
def __eq__(self, other):
|
153
|
-
return (
|
154
|
-
isinstance(other, self.__class__)
|
155
|
-
and self._constructor_args == other._constructor_args
|
156
|
-
)
|
157
|
-
|
158
|
-
def __hash__(self):
|
159
|
-
return id(self)
|
160
|
-
|
161
|
-
|
162
|
-
class Manager(BaseManager.from_queryset(QuerySet)):
|
163
|
-
pass
|
164
|
-
|
165
|
-
|
166
|
-
class ManagerDescriptor:
|
167
|
-
def __init__(self, manager):
|
168
|
-
self.manager = manager
|
169
|
-
|
170
|
-
def __get__(self, instance, cls=None):
|
171
|
-
if instance is not None:
|
172
|
-
raise AttributeError(
|
173
|
-
f"Manager isn't accessible via {cls.__name__} instances"
|
174
|
-
)
|
175
|
-
|
176
|
-
return cls._meta.managers_map[self.manager.name]
|
File without changes
|
File without changes
|
File without changes
|