plain.models 0.34.3__py3-none-any.whl → 0.35.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # plain-models changelog
2
2
 
3
+ ## [0.35.0](https://github.com/dropseed/plain/releases/plain-models@0.35.0) (2025-07-07)
4
+
5
+ ### What's changed
6
+
7
+ - Added the `plain models list` CLI command which prints a nicely formatted list of all installed models, including their table name, fields, and originating package. You can pass package labels to filter the output or use the `--app-only` flag to only show first-party app models ([1bc40ce](https://github.com/dropseed/plain/commit/1bc40ce)).
8
+ - The MySQL backend no longer enforces a strict `mysqlclient >= 1.4.3` version check and had several unused constraint-handling methods removed, reducing boilerplate and improving compatibility with a wider range of `mysqlclient` versions ([6322400](https://github.com/dropseed/plain/commit/6322400), [67f21f6](https://github.com/dropseed/plain/commit/67f21f6)).
9
+
10
+ ### Upgrade instructions
11
+
12
+ - No changes required
13
+
14
+ ## [0.34.4](https://github.com/dropseed/plain/releases/plain-models@0.34.4) (2025-07-02)
15
+
16
+ ### What's changed
17
+
18
+ - The built-in `on_delete` behaviors (`CASCADE`, `PROTECT`, `RESTRICT`, `SET_NULL`, `SET_DEFAULT`, and the callables returned by `SET(...)`) no longer receive the legacy `using` argument. Their signatures are now `(collector, field, sub_objs)` ([20325a1](https://github.com/dropseed/plain/commit/20325a1)).
19
+ - Removed the unused `interprets_empty_strings_as_nulls` backend feature flag and the related fallback logic ([285378c](https://github.com/dropseed/plain/commit/285378c)).
20
+
21
+ ### Upgrade instructions
22
+
23
+ - No changes required
24
+
3
25
  ## [0.34.3](https://github.com/dropseed/plain/releases/plain-models@0.34.3) (2025-06-29)
4
26
 
5
27
  ### What's changed
@@ -9,9 +9,6 @@ class BaseDatabaseFeatures:
9
9
  empty_fetchmany_value = []
10
10
  update_can_self_select = True
11
11
 
12
- # Does the backend distinguish between '' and None?
13
- interprets_empty_strings_as_nulls = False
14
-
15
12
  # Does the backend support initially deferrable unique constraints?
16
13
  supports_deferrable_unique_constraints = False
17
14
 
@@ -297,14 +297,6 @@ class BaseDatabaseSchemaEditor:
297
297
  else:
298
298
  yield column_default
299
299
  params.append(default_value)
300
- # Oracle treats the empty string ('') as null, so coerce the null
301
- # option whenever '' is a possible value.
302
- if (
303
- field.empty_strings_allowed
304
- and not field.primary_key
305
- and self.connection.features.interprets_empty_strings_as_nulls
306
- ):
307
- null = True
308
300
 
309
301
  if not null:
310
302
  yield "NOT NULL"
@@ -1042,27 +1034,20 @@ class BaseDatabaseSchemaEditor:
1042
1034
  Return a (sql, params) fragment to set a column to null or non-null
1043
1035
  as required by new_field, or None if no changes are required.
1044
1036
  """
1045
- if (
1046
- self.connection.features.interprets_empty_strings_as_nulls
1047
- and new_field.empty_strings_allowed
1048
- ):
1049
- # The field is nullable in the database anyway, leave it alone.
1050
- return
1051
- else:
1052
- new_db_params = new_field.db_parameters(connection=self.connection)
1053
- sql = (
1054
- self.sql_alter_column_null
1055
- if new_field.allow_null
1056
- else self.sql_alter_column_not_null
1057
- )
1058
- return (
1059
- sql
1060
- % {
1061
- "column": self.quote_name(new_field.column),
1062
- "type": new_db_params["type"],
1063
- },
1064
- [],
1065
- )
1037
+ new_db_params = new_field.db_parameters(connection=self.connection)
1038
+ sql = (
1039
+ self.sql_alter_column_null
1040
+ if new_field.allow_null
1041
+ else self.sql_alter_column_not_null
1042
+ )
1043
+ return (
1044
+ sql
1045
+ % {
1046
+ "column": self.quote_name(new_field.column),
1047
+ "type": new_db_params["type"],
1048
+ },
1049
+ [],
1050
+ )
1066
1051
 
1067
1052
  def _alter_column_default_sql(self, model, old_field, new_field, drop=False):
1068
1053
  """
@@ -6,23 +6,16 @@ Requires mysqlclient: https://pypi.org/project/mysqlclient/
6
6
 
7
7
  from functools import cached_property
8
8
 
9
+ import MySQLdb as Database
10
+ from MySQLdb.constants import CLIENT, FIELD_TYPE
11
+ from MySQLdb.converters import conversions
12
+
9
13
  from plain.exceptions import ImproperlyConfigured
10
14
  from plain.models.backends import utils as backend_utils
11
15
  from plain.models.backends.base.base import BaseDatabaseWrapper
12
16
  from plain.models.db import IntegrityError
13
17
  from plain.utils.regex_helper import _lazy_re_compile
14
18
 
15
- try:
16
- import MySQLdb as Database
17
- except ImportError as err:
18
- raise ImproperlyConfigured(
19
- "Error loading MySQLdb module.\nDid you install mysqlclient?"
20
- ) from err
21
-
22
- from MySQLdb.constants import CLIENT, FIELD_TYPE
23
- from MySQLdb.converters import conversions
24
-
25
- # Some of these import MySQLdb, so import them after checking if it's installed.
26
19
  from .client import DatabaseClient
27
20
  from .creation import DatabaseCreation
28
21
  from .features import DatabaseFeatures
@@ -31,13 +24,6 @@ from .operations import DatabaseOperations
31
24
  from .schema import DatabaseSchemaEditor
32
25
  from .validation import DatabaseValidation
33
26
 
34
- version = Database.version_info
35
- if version < (1, 4, 3):
36
- raise ImproperlyConfigured(
37
- f"mysqlclient 1.4.3 or newer is required; you have {Database.__version__}."
38
- )
39
-
40
-
41
27
  # MySQLdb returns TIME columns as timedelta -- they are more like timedelta in
42
28
  # terms of actual behavior as they are signed and include days -- and Plain
43
29
  # expects time.
@@ -281,37 +267,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
281
267
  with self.wrap_database_errors:
282
268
  self.connection.autocommit(autocommit)
283
269
 
284
- def disable_constraint_checking(self):
285
- """
286
- Disable foreign key checks, primarily for use in adding rows with
287
- forward references. Always return True to indicate constraint checks
288
- need to be re-enabled.
289
- """
290
- with self.cursor() as cursor:
291
- cursor.execute("SET foreign_key_checks=0")
292
- return True
293
-
294
- def enable_constraint_checking(self):
295
- """
296
- Re-enable foreign key checks after they have been disabled.
297
- """
298
- # Override needs_rollback in case constraint_checks_disabled is
299
- # nested inside transaction.atomic.
300
- self.needs_rollback, needs_rollback = False, self.needs_rollback
301
- try:
302
- with self.cursor() as cursor:
303
- cursor.execute("SET foreign_key_checks=1")
304
- finally:
305
- self.needs_rollback = needs_rollback
306
-
307
270
  def check_constraints(self, table_names=None):
308
- """
309
- Check each table name in `table_names` for rows with invalid foreign
310
- key references. This method is intended to be used in conjunction with
311
- `disable_constraint_checking()` and `enable_constraint_checking()`, to
312
- determine if rows with invalid references were entered while constraint
313
- checks were off.
314
- """
271
+ """Check ``table_names`` for rows with invalid foreign key references."""
315
272
  with self.cursor() as cursor:
316
273
  if table_names is None:
317
274
  table_names = self.introspection.table_names(cursor)
@@ -1,6 +1,8 @@
1
1
  from plain.models.backends.base.schema import BaseDatabaseSchemaEditor
2
2
  from plain.models.constants import LOOKUP_SEP
3
- from plain.models.fields import NOT_PROVIDED, F, UniqueConstraint
3
+ from plain.models.constraints import UniqueConstraint
4
+ from plain.models.expressions import F
5
+ from plain.models.fields import NOT_PROVIDED
4
6
 
5
7
 
6
8
  class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
plain/models/base.py CHANGED
@@ -833,10 +833,7 @@ class Model(metaclass=ModelBase):
833
833
  f = self._meta.get_field(field_name)
834
834
  lookup_value = getattr(self, f.attname)
835
835
  # TODO: Handle multiple backends with different feature flags.
836
- if lookup_value is None or (
837
- lookup_value == ""
838
- and db_connection.features.interprets_empty_strings_as_nulls
839
- ):
836
+ if lookup_value is None:
840
837
  # no value, skip the lookup
841
838
  continue
842
839
  if f.primary_key and not self._state.adding:
plain/models/cli.py CHANGED
@@ -96,6 +96,37 @@ def db_wait():
96
96
  break
97
97
 
98
98
 
99
+ @cli.command(name="list")
100
+ @click.argument("package_labels", nargs=-1)
101
+ @click.option(
102
+ "--app-only",
103
+ is_flag=True,
104
+ help="Only show models from packages that start with 'app'.",
105
+ )
106
+ def list_models(package_labels, app_only):
107
+ """List installed models."""
108
+
109
+ packages = set(package_labels)
110
+
111
+ for model in sorted(
112
+ models_registry.get_models(),
113
+ key=lambda m: (m._meta.package_label, m._meta.model_name),
114
+ ):
115
+ pkg = model._meta.package_label
116
+ pkg_name = packages_registry.get_package_config(pkg).name
117
+ if app_only and not pkg_name.startswith("app"):
118
+ continue
119
+ if packages and pkg not in packages:
120
+ continue
121
+ fields = ", ".join(f.name for f in model._meta.get_fields())
122
+ click.echo(
123
+ f"{click.style(pkg, fg='cyan')}.{click.style(model.__name__, fg='blue')}"
124
+ )
125
+ click.echo(f" table: {model._meta.db_table}")
126
+ click.echo(f" fields: {fields}")
127
+ click.echo(f" package: {pkg_name}\n")
128
+
129
+
99
130
  @register_cli("makemigrations")
100
131
  @cli.command()
101
132
  @click.argument("package_labels", nargs=-1)
@@ -2,7 +2,6 @@ from enum import Enum
2
2
  from types import NoneType
3
3
 
4
4
  from plain.exceptions import FieldError, ValidationError
5
- from plain.models.db import db_connection
6
5
  from plain.models.expressions import Exists, ExpressionList, F, OrderBy
7
6
  from plain.models.indexes import IndexExpression
8
7
  from plain.models.lookups import Exact
@@ -356,10 +355,7 @@ class UniqueConstraint(BaseConstraint):
356
355
  return
357
356
  field = model._meta.get_field(field_name)
358
357
  lookup_value = getattr(instance, field.attname)
359
- if lookup_value is None or (
360
- lookup_value == ""
361
- and db_connection.features.interprets_empty_strings_as_nulls
362
- ):
358
+ if lookup_value is None:
363
359
  # A composite constraint containing NULL value cannot cause
364
360
  # a violation since NULL != NULL in SQL.
365
361
  return
plain/models/deletion.py CHANGED
@@ -24,7 +24,7 @@ class RestrictedError(IntegrityError):
24
24
  super().__init__(msg, restricted_objects)
25
25
 
26
26
 
27
- def CASCADE(collector, field, sub_objs, using):
27
+ def CASCADE(collector, field, sub_objs):
28
28
  collector.collect(
29
29
  sub_objs,
30
30
  source=field.remote_field.model,
@@ -35,7 +35,7 @@ def CASCADE(collector, field, sub_objs, using):
35
35
  collector.add_field_update(field, None, sub_objs)
36
36
 
37
37
 
38
- def PROTECT(collector, field, sub_objs, using):
38
+ def PROTECT(collector, field, sub_objs):
39
39
  raise ProtectedError(
40
40
  f"Cannot delete some instances of model '{field.remote_field.model.__name__}' because they are "
41
41
  f"referenced through a protected foreign key: '{sub_objs[0].__class__.__name__}.{field.name}'",
@@ -43,7 +43,7 @@ def PROTECT(collector, field, sub_objs, using):
43
43
  )
44
44
 
45
45
 
46
- def RESTRICT(collector, field, sub_objs, using):
46
+ def RESTRICT(collector, field, sub_objs):
47
47
  collector.add_restricted_objects(field, sub_objs)
48
48
  collector.add_dependency(field.remote_field.model, field.model)
49
49
 
@@ -51,12 +51,12 @@ def RESTRICT(collector, field, sub_objs, using):
51
51
  def SET(value):
52
52
  if callable(value):
53
53
 
54
- def set_on_delete(collector, field, sub_objs, using):
54
+ def set_on_delete(collector, field, sub_objs):
55
55
  collector.add_field_update(field, value(), sub_objs)
56
56
 
57
57
  else:
58
58
 
59
- def set_on_delete(collector, field, sub_objs, using):
59
+ def set_on_delete(collector, field, sub_objs):
60
60
  collector.add_field_update(field, value, sub_objs)
61
61
 
62
62
  set_on_delete.deconstruct = lambda: ("plain.models.SET", (value,), {})
@@ -64,21 +64,21 @@ def SET(value):
64
64
  return set_on_delete
65
65
 
66
66
 
67
- def SET_NULL(collector, field, sub_objs, using):
67
+ def SET_NULL(collector, field, sub_objs):
68
68
  collector.add_field_update(field, None, sub_objs)
69
69
 
70
70
 
71
71
  SET_NULL.lazy_sub_objs = True
72
72
 
73
73
 
74
- def SET_DEFAULT(collector, field, sub_objs, using):
74
+ def SET_DEFAULT(collector, field, sub_objs):
75
75
  collector.add_field_update(field, field.get_default(), sub_objs)
76
76
 
77
77
 
78
78
  SET_DEFAULT.lazy_sub_objs = True
79
79
 
80
80
 
81
- def DO_NOTHING(collector, field, sub_objs, using):
81
+ def DO_NOTHING(collector, field, sub_objs):
82
82
  pass
83
83
 
84
84
 
@@ -365,11 +365,7 @@ class Field(RegisterLookupMixin):
365
365
  return errors
366
366
 
367
367
  def _check_null_allowed_for_primary_keys(self):
368
- if (
369
- self.primary_key
370
- and self.allow_null
371
- and not db_connection.features.interprets_empty_strings_as_nulls
372
- ):
368
+ if self.primary_key and self.allow_null:
373
369
  # We cannot reliably check this for backends like Oracle which
374
370
  # consider NULL and '' to be equal (and thus set up
375
371
  # character-based fields a little differently).
@@ -885,11 +881,7 @@ class Field(RegisterLookupMixin):
885
881
  return self.default
886
882
  return lambda: self.default
887
883
 
888
- if (
889
- not self.empty_strings_allowed
890
- or self.allow_null
891
- and not db_connection.features.interprets_empty_strings_as_nulls
892
- ):
884
+ if not self.empty_strings_allowed or self.allow_null:
893
885
  return return_None
894
886
  return str # return empty string
895
887
 
@@ -974,11 +974,7 @@ class ForeignKey(ForeignObject):
974
974
 
975
975
  def get_db_prep_save(self, value, connection):
976
976
  if value is None or (
977
- value == ""
978
- and (
979
- not self.target_field.empty_strings_allowed
980
- or connection.features.interprets_empty_strings_as_nulls
981
- )
977
+ value == "" and not self.target_field.empty_strings_allowed
982
978
  ):
983
979
  return None
984
980
  else:
@@ -1019,8 +1015,6 @@ class ForeignKey(ForeignObject):
1019
1015
 
1020
1016
  def get_db_converters(self, connection):
1021
1017
  converters = super().get_db_converters(connection)
1022
- if connection.features.interprets_empty_strings_as_nulls:
1023
- converters += [self.convert_empty_strings]
1024
1018
  return converters
1025
1019
 
1026
1020
  def get_col(self, alias, output_field=None):
@@ -369,15 +369,12 @@ def create_reverse_many_to_one_manager(superclass, rel):
369
369
  """
370
370
  Filter the queryset for the instance this manager is bound to.
371
371
  """
372
- empty_strings_as_null = (
373
- db_connection.features.interprets_empty_strings_as_nulls
374
- )
375
372
  queryset._add_hints(instance=self.instance)
376
373
  queryset._defer_next_filter = True
377
374
  queryset = queryset.filter(**self.core_filters)
378
375
  for field in self.field.foreign_related_fields:
379
376
  val = getattr(self.instance, field.attname)
380
- if val is None or (val == "" and empty_strings_as_null):
377
+ if val is None:
381
378
  return queryset.none()
382
379
  if self.field.many_to_one:
383
380
  # Guard against field-like objects such as GenericRelation
plain/models/forms.py CHANGED
@@ -769,13 +769,7 @@ def modelfield_to_formfield(
769
769
  # Passing max_length to forms.CharField means that the value's length
770
770
  # will be validated twice. This is considered acceptable since we want
771
771
  # the value in the form field (to pass into widget for example).
772
- # TODO: Handle multiple backends with different feature flags.
773
- from plain.models.db import db_connection
774
-
775
- if (
776
- modelfield.allow_null
777
- and not db_connection.features.interprets_empty_strings_as_nulls
778
- ):
772
+ if modelfield.allow_null:
779
773
  defaults["empty_value"] = None
780
774
  return fields.CharField(
781
775
  max_length=modelfield.max_length,
plain/models/sql/query.py CHANGED
@@ -1233,16 +1233,6 @@ class Query(BaseExpression):
1233
1233
  raise ValueError("Cannot use None as a query value")
1234
1234
  return lhs.get_lookup("isnull")(lhs, True)
1235
1235
 
1236
- # For Oracle '' is equivalent to null. The check must be done at this
1237
- # stage because join promotion can't be done in the compiler. A similar
1238
- # thing is done in is_nullable(), too.
1239
- if (
1240
- lookup_name == "exact"
1241
- and lookup.rhs == ""
1242
- and db_connection.features.interprets_empty_strings_as_nulls
1243
- ):
1244
- return lhs.get_lookup("isnull")(lhs, True)
1245
-
1246
1236
  return lookup
1247
1237
 
1248
1238
  def try_transform(self, lhs, name):
@@ -2466,20 +2456,11 @@ class Query(BaseExpression):
2466
2456
  return trimmed_prefix, contains_louter
2467
2457
 
2468
2458
  def is_nullable(self, field):
2469
- """
2470
- Check if the given field should be treated as nullable.
2471
-
2472
- Some backends treat '' as null and Plain treats such fields as
2473
- nullable for those backends. In such situations field.allow_null can be
2474
- False even if we should treat the field as nullable.
2475
- """
2459
+ """Check if the given field should be treated as nullable."""
2476
2460
  # QuerySet does not have knowledge of which connection is going to be
2477
2461
  # used. For the single-database setup we always reference the default
2478
2462
  # connection here.
2479
- return field.allow_null or (
2480
- field.empty_strings_allowed
2481
- and db_connection.features.interprets_empty_strings_as_nulls
2482
- )
2463
+ return field.allow_null
2483
2464
 
2484
2465
 
2485
2466
  def get_order_dir(field, default="ASC"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.models
3
- Version: 0.34.3
3
+ Version: 0.35.0
4
4
  Summary: Database models for Plain.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-File: LICENSE
@@ -1,22 +1,22 @@
1
- plain/models/CHANGELOG.md,sha256=pqRLcrM3g7kol5Zkxrxjd9xjtgTL3bGlFUnNJ4ersTU,2621
1
+ plain/models/CHANGELOG.md,sha256=bUinpgx_HxNjgItauzHyszRtwU7F3Zm33u0mMAZBoDs,4127
2
2
  plain/models/README.md,sha256=vsZPev3Fna-Irdcs3-wrOcAoII5LOhXdcWLBMT87CS0,1626
3
3
  plain/models/__init__.py,sha256=dnU6MOXs3lGoK31nLWjCqbf7zigkaUccomchz9lNDJ8,2950
4
4
  plain/models/aggregates.py,sha256=P0mhsMl1VZt2CVHMuCHnNI8SxZ9citjDLEgioN6NOpo,7240
5
- plain/models/base.py,sha256=rjPWtHI0YzTJ--uRgQ-CrMK-iNhttNFAWWWC2h4pqis,68095
6
- plain/models/cli.py,sha256=-K5ZrMbwL-fcUOze64_U4uOPpCuppa-ibdoOaCFvBF4,43966
5
+ plain/models/base.py,sha256=8-F9DvSF5s2_zoGyEmZU5xg9GdEigthVT0HENv9iigI,67952
6
+ plain/models/cli.py,sha256=4iZRHRPXxUCrsW-SzeimIE_lrSkWkLhgYOSk4BxUF_8,44985
7
7
  plain/models/config.py,sha256=OF7eIEtXNZyGwgc3eMEpb5uEAup5RXeT-0um60dfBeU,636
8
8
  plain/models/connections.py,sha256=RBNa2FZ0x3C9un6PaYL-IYzH_OesRSpdHNGKvYHGiOM,2276
9
9
  plain/models/constants.py,sha256=ndnj9TOTKW0p4YcIPLOLEbsH6mOgFi6B1-rIzr_iwwU,210
10
- plain/models/constraints.py,sha256=ZECMnw6urCjRu6XLDjXE5zuFdeZOE-hzowCAa8N8xGU,16967
10
+ plain/models/constraints.py,sha256=iMQj0AoExqTKEgn_Z3T2LKIlKC2TREPFwHxjvbc2iGU,16782
11
11
  plain/models/database_url.py,sha256=iidKVhOylf5N6t1EMPRySRQiv6LiuRjYRECB_UJ3MI8,6419
12
12
  plain/models/db.py,sha256=FpdfLYrRX2THUzDy4QdJ_OpSo9IFKLerZIEQ-T2x8zA,1348
13
13
  plain/models/default_settings.py,sha256=cDym1o_DtHySWgDRIdjgEM0YxjgYU51ZqzWVA3vpzTk,569
14
- plain/models/deletion.py,sha256=izvpGwvP8xxmuNVKvuYaeAKFD03FZMMfjkRUmMBiTDI,17631
14
+ plain/models/deletion.py,sha256=iyVKgbEdphncpJiwofNaXPa72z301hp2bmRHp1RQFSA,17575
15
15
  plain/models/entrypoints.py,sha256=EC14mW19tK9dCumaNHnv4_9jQV8jomQ8jXy8Ib89VBw,191
16
16
  plain/models/enums.py,sha256=Zr-JKt2aeYsSADtAm69fDRfajS7jYwop2vWQVLJ9YYI,2726
17
17
  plain/models/exceptions.py,sha256=IqzK60-hY3TYsgOMxlWwgpVa21E7ydC-gqUG4tNvVJc,2042
18
18
  plain/models/expressions.py,sha256=hN6sfOxqxpP0qmYOUotsFAAn2-bnl35iHwyINyXA7CI,62763
19
- plain/models/forms.py,sha256=M53zESGUdkJk6B6GD6vcGQ_aVvqF3S2eKBgcmMdvHyI,28548
19
+ plain/models/forms.py,sha256=tyLRWwbC0CGLYinsFlnGiTjjjKY3kFSqL_MOuJJ5HPs,28325
20
20
  plain/models/indexes.py,sha256=fazIZPJgCX5_Bhwk7MQy3YbWOxpHvaCe1dDLGGldTuY,11540
21
21
  plain/models/lookups.py,sha256=0tbuMBpd4DlTUeO0IdZPtSO2GcjsSgcbRcj5lYfe87M,24776
22
22
  plain/models/manager.py,sha256=zc2W-vTTk3zkDXCds5-TCXgLhVmM4PdQb-qtu-njeLQ,5827
@@ -34,20 +34,20 @@ plain/models/backends/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
34
34
  plain/models/backends/base/base.py,sha256=I5040pUAe7-yNTb9wfMNOYjeiFQLZ5dcRI4rtmVKJ04,26457
35
35
  plain/models/backends/base/client.py,sha256=90Ffs6zZYCli3tJjwsPH8TItZ8tz1Pp-zhQa-EpsNqc,937
36
36
  plain/models/backends/base/creation.py,sha256=T8JaQ67KOtp6YIPksrmOa8BF9oTaTPXgKB5ct6bxh9E,9352
37
- plain/models/backends/base/features.py,sha256=nHUkFjKSx_w9QwD4l-jXP_zotvspy9o6ayn-AHzzrHI,7959
37
+ plain/models/backends/base/features.py,sha256=1AehdhpeC7VobhggwpeXIt7HJNY2EWY700j4gYX8Xjs,7856
38
38
  plain/models/backends/base/introspection.py,sha256=8icKf9h8y4kobmyrbo8JWTDcpQIAt4oS_4FtCnY7FqQ,6815
39
39
  plain/models/backends/base/operations.py,sha256=Y08cS7IZwYXMKVUoEpBCjyOTh1PW2OShoRU9TZdipss,25740
40
- plain/models/backends/base/schema.py,sha256=eJIeoN1nm7abIw_2eKFxeckcZHldixgvDlgCTwvb7GI,65649
40
+ plain/models/backends/base/schema.py,sha256=1dLiVNz1huv8xJo1JphgfWfhXXZ-YGw_MgZ6eWazKQA,65016
41
41
  plain/models/backends/base/validation.py,sha256=2zpI11hyUJr0I0cA1xmvoFwQVdZ-7_1T2F11TpQ0Rkk,1067
42
42
  plain/models/backends/mysql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- plain/models/backends/mysql/base.py,sha256=9PEaRvRcU9MBfir-s_rNooemDmjQQpfzB1DxJJdmC14,15985
43
+ plain/models/backends/mysql/base.py,sha256=BfFi7QipjpW38LJquRINdUnm6cIBKEDVgiApMuWo1OQ,14421
44
44
  plain/models/backends/mysql/client.py,sha256=YGjk4f5VOuIKcj5SOOJeBqiSDF0Ft9m1aUPOmLzdD6c,2973
45
45
  plain/models/backends/mysql/compiler.py,sha256=NaYgcGyFUZ2lTOXXJj87hLMMMnXRAS1jUYkJq1NoCv4,3289
46
46
  plain/models/backends/mysql/creation.py,sha256=ozuc3mz65bjFz6sn5BFLRRGQsUYaxwIGjQyKod31c1Y,952
47
47
  plain/models/backends/mysql/features.py,sha256=OPHaiqPmAFAUZqnZdLOqbCmxmuGMnIqa62fJqAkYQ0o,5875
48
48
  plain/models/backends/mysql/introspection.py,sha256=nKhuFDi8xxj0DZH8_45F-XOARiHxWBoW21_2lTFARUc,14131
49
49
  plain/models/backends/mysql/operations.py,sha256=zLtg50k7WzqO3eZjLAknhKg29bxm0-ysd1_NydlDlzQ,16665
50
- plain/models/backends/mysql/schema.py,sha256=W6sV2DLdGAeM7ZX80m1XCRtV95DDhyochy8oJ18bet8,9870
50
+ plain/models/backends/mysql/schema.py,sha256=LmP62COVuhpbH4gYulcSQNJgGuolc3RTFGsudoohJOQ,9942
51
51
  plain/models/backends/mysql/validation.py,sha256=TFYpuRoy3yG3SGfMFtRewaWq5gISN6FBaq-AD0DY7SI,1946
52
52
  plain/models/backends/postgresql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
53
  plain/models/backends/postgresql/base.py,sha256=SE6wmmxOtCnMDwycUs9yn_WATl97eV-4t8AaiK41E20,16053
@@ -70,11 +70,11 @@ plain/models/backups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
70
70
  plain/models/backups/cli.py,sha256=du_751qWPtJT7C3GF_nLfHucMoUzlUOz9OKUwqHQ16s,2990
71
71
  plain/models/backups/clients.py,sha256=WQ4X4KrkNeiKYJcpGRwnfRiAeeyduHWEnq4tA4mtjvw,4047
72
72
  plain/models/backups/core.py,sha256=09IZUhBEe1Yej3PC8AidtkaI0c8tt7VnqGBCWK-WrFg,3318
73
- plain/models/fields/__init__.py,sha256=fZXZzsvFoIEte-0XSdKjYhkr3mqZ2NqjFD9KPU_HmxI,80319
73
+ plain/models/fields/__init__.py,sha256=b0qggzhZf89cXSwokr6rikWyF6kKPTuceIT3pI1Rz3s,80093
74
74
  plain/models/fields/json.py,sha256=OdGW4EYBSHQgkuugB-EiIXOqGstUqcMKUvOTOVUHqEQ,18049
75
75
  plain/models/fields/mixins.py,sha256=K_ocrSbb6pPuGwYZeqgzoZskwCIMFIB6IV3T5CrU5J0,1805
76
- plain/models/fields/related.py,sha256=v3MH00jy2dcqk8bnYGmABnXIr5OMJ413V9qQL0wej2s,60321
77
- plain/models/fields/related_descriptors.py,sha256=UKwyJi7jGGFO373nPSfX20xOIxo460Xm9Zf-qqZ7Sh8,38703
76
+ plain/models/fields/related.py,sha256=O2sMK-yAeos2AHQwB1ssy9FLQU3F-zyTvPmUYIm_Qj4,60083
77
+ plain/models/fields/related_descriptors.py,sha256=RD5764epSUbs-PFUAd_4xcZgbhLBBhgKuMy-5XMPbf8,38537
78
78
  plain/models/fields/related_lookups.py,sha256=AyAtd5iK_MMkvsQxsFznMCv36QtLoT9NMLLp0v4tDwQ,7785
79
79
  plain/models/fields/reverse_related.py,sha256=PGEFus3ov_0U3W8Ijj1MZlYoFrhUhFllZ8fpZLggvR8,11070
80
80
  plain/models/functions/__init__.py,sha256=aglCm_JtzDYk2KmxubDN_78CGG3JCfRWnfJ74Oj5YJ4,2658
@@ -107,14 +107,14 @@ plain/models/sql/__init__.py,sha256=FoRCcab-kh_XY8C4eldgLy9-zuk-M63Nyi9cFsYjclU,
107
107
  plain/models/sql/compiler.py,sha256=MrwiVKfWxk_fL_cwY1exiM7wbnasIfcfh_4-Q1XKnlA,84734
108
108
  plain/models/sql/constants.py,sha256=usb1LSh9WNGPsurWAGppDkV0wYJJg5GEegKibQdS718,533
109
109
  plain/models/sql/datastructures.py,sha256=FC88CVCukLyU621JrmKLBhmgvotEHgAhIOYfVvJpuR0,7084
110
- plain/models/sql/query.py,sha256=gsCBVo5l3yJusukX2xarD1pGoOD69M2W3X3YGx1YjgI,109508
110
+ plain/models/sql/query.py,sha256=jSnzBoM66dTR-4aJpO-T2ksOc5PSeIQ07ToPEzYj58U,108723
111
111
  plain/models/sql/subqueries.py,sha256=JkVjYuWyEBpSDFNMOH00RmXxe8V4cERjUQ_ypGepqyo,5847
112
112
  plain/models/sql/where.py,sha256=ezE9Clt2BmKo-I7ARsgqZ_aVA-1UdayCwr6ULSWZL6c,12635
113
113
  plain/models/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  plain/models/test/pytest.py,sha256=5mxY1MHqfCFJ7G39W1DuE0vCHfsfehQQmOyE6vI6caw,3323
115
115
  plain/models/test/utils.py,sha256=MxBNWoGMtwAtka7LbxWgilgzv7T5qqJL8ystF2SDJrs,345
116
- plain_models-0.34.3.dist-info/METADATA,sha256=mb93D4h-Sx5BSsRwbeCB6IEBAflmr_hINCHIiS9VqXA,1921
117
- plain_models-0.34.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
- plain_models-0.34.3.dist-info/entry_points.txt,sha256=IYJAW9MpL3PXyXFWmKmALagAGXC_5rzBn2eEGJlcV04,112
119
- plain_models-0.34.3.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
120
- plain_models-0.34.3.dist-info/RECORD,,
116
+ plain_models-0.35.0.dist-info/METADATA,sha256=v8A_EGrAuBGpAa_7thYuC1eim5Alt7jdfLskHWfUerg,1921
117
+ plain_models-0.35.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
+ plain_models-0.35.0.dist-info/entry_points.txt,sha256=IYJAW9MpL3PXyXFWmKmALagAGXC_5rzBn2eEGJlcV04,112
119
+ plain_models-0.35.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
120
+ plain_models-0.35.0.dist-info/RECORD,,