plain.models 0.47.0__py3-none-any.whl → 0.49.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.
Files changed (34) hide show
  1. plain/models/CHANGELOG.md +27 -0
  2. plain/models/__init__.py +0 -3
  3. plain/models/aggregates.py +1 -1
  4. plain/models/backends/base/creation.py +1 -0
  5. plain/models/backends/mysql/compiler.py +1 -1
  6. plain/models/backends/sqlite3/operations.py +1 -1
  7. plain/models/base.py +34 -94
  8. plain/models/cli.py +66 -0
  9. plain/models/constraints.py +2 -1
  10. plain/models/deletion.py +3 -5
  11. plain/models/exceptions.py +108 -0
  12. plain/models/expressions.py +1 -1
  13. plain/models/fields/__init__.py +3 -41
  14. plain/models/fields/related.py +15 -22
  15. plain/models/fields/related_descriptors.py +1 -3
  16. plain/models/fields/related_managers.py +1 -1
  17. plain/models/fields/reverse_related.py +3 -5
  18. plain/models/forms.py +1 -1
  19. plain/models/lookups.py +1 -1
  20. plain/models/migrations/executor.py +29 -27
  21. plain/models/migrations/state.py +1 -1
  22. plain/models/options.py +4 -16
  23. plain/models/query.py +12 -10
  24. plain/models/query_utils.py +2 -2
  25. plain/models/sql/compiler.py +6 -6
  26. plain/models/sql/datastructures.py +1 -1
  27. plain/models/sql/query.py +4 -4
  28. plain/models/sql/subqueries.py +3 -3
  29. plain/models/sql/where.py +1 -1
  30. {plain_models-0.47.0.dist-info → plain_models-0.49.0.dist-info}/METADATA +1 -1
  31. {plain_models-0.47.0.dist-info → plain_models-0.49.0.dist-info}/RECORD +34 -34
  32. {plain_models-0.47.0.dist-info → plain_models-0.49.0.dist-info}/WHEEL +0 -0
  33. {plain_models-0.47.0.dist-info → plain_models-0.49.0.dist-info}/entry_points.txt +0 -0
  34. {plain_models-0.47.0.dist-info → plain_models-0.49.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,6 +3,7 @@ from functools import cached_property, partial
3
3
  from plain import exceptions
4
4
  from plain.models.constants import LOOKUP_SEP
5
5
  from plain.models.deletion import SET_DEFAULT, SET_NULL
6
+ from plain.models.exceptions import FieldDoesNotExist, FieldError
6
7
  from plain.models.query_utils import PathInfo, Q
7
8
  from plain.models.utils import make_model_tuple
8
9
  from plain.preflight import PreflightResult
@@ -303,8 +304,8 @@ class RelatedField(FieldCacheMixin, Field):
303
304
  # columns from another table.
304
305
  return None
305
306
 
306
- def contribute_to_class(self, cls, name, private_only=False, **kwargs):
307
- super().contribute_to_class(cls, name, private_only=private_only, **kwargs)
307
+ def contribute_to_class(self, cls, name):
308
+ super().contribute_to_class(cls, name)
308
309
 
309
310
  self.opts = cls._meta
310
311
 
@@ -407,7 +408,7 @@ class RelatedField(FieldCacheMixin, Field):
407
408
  """
408
409
  target_fields = self.path_infos[-1].target_fields
409
410
  if len(target_fields) > 1:
410
- raise exceptions.FieldError(
411
+ raise FieldError(
411
412
  "The relation has multiple target fields, but only single target field "
412
413
  "was asked for"
413
414
  )
@@ -582,15 +583,15 @@ class ForeignKey(RelatedField):
582
583
  def reverse_path_infos(self):
583
584
  return self.get_reverse_path_info()
584
585
 
585
- def contribute_to_class(self, cls, name, private_only=False, **kwargs):
586
- super().contribute_to_class(cls, name, private_only=private_only, **kwargs)
586
+ def contribute_to_class(self, cls, name):
587
+ super().contribute_to_class(cls, name)
587
588
  setattr(cls, self.name, self.forward_related_accessor_class(self))
588
589
 
589
590
  def contribute_to_related_class(self, cls, related):
590
591
  # Internal FK's - i.e., those with a related name ending with '+'
591
592
  if not self.remote_field.is_hidden():
592
593
  setattr(
593
- cls._meta.concrete_model,
594
+ cls,
594
595
  related.get_accessor_name(),
595
596
  self.related_accessor_class(related),
596
597
  )
@@ -696,13 +697,10 @@ class ForeignKey(RelatedField):
696
697
  related_fields = [(from_field, to_field)]
697
698
 
698
699
  for from_field, to_field in related_fields:
699
- if (
700
- to_field
701
- and to_field.model != self.remote_field.model._meta.concrete_model
702
- ):
703
- raise exceptions.FieldError(
700
+ if to_field and to_field.model != self.remote_field.model:
701
+ raise FieldError(
704
702
  f"'{self.model._meta.label}.{self.name}' refers to field '{to_field.name}' which is not local to model "
705
- f"'{self.remote_field.model._meta.concrete_model._meta.label}'."
703
+ f"'{self.remote_field.model._meta.label}'."
706
704
  )
707
705
  return related_fields
708
706
 
@@ -1075,7 +1073,7 @@ class ManyToManyField(RelatedField):
1075
1073
 
1076
1074
  try:
1077
1075
  field = through._meta.get_field(field_name)
1078
- except exceptions.FieldDoesNotExist:
1076
+ except FieldDoesNotExist:
1079
1077
  errors.append(
1080
1078
  PreflightResult(
1081
1079
  fix=f"The intermediary model '{qualified_model_name}' has no field '{field_name}'. {fix}",
@@ -1109,13 +1107,8 @@ class ManyToManyField(RelatedField):
1109
1107
  }
1110
1108
  m2m_db_table = self.m2m_db_table()
1111
1109
  model = registered_tables.get(m2m_db_table)
1112
- # The second condition allows multiple m2m relations on a model if
1113
- # some point to a through model that proxies another through model.
1114
- if (
1115
- model
1116
- and model._meta.concrete_model
1117
- != self.remote_field.through._meta.concrete_model
1118
- ):
1110
+ # Check if there's already a m2m field using the same through model.
1111
+ if model and model != self.remote_field.through:
1119
1112
  clashing_obj = model._meta.label
1120
1113
  return [
1121
1114
  PreflightResult(
@@ -1245,8 +1238,8 @@ class ManyToManyField(RelatedField):
1245
1238
  break
1246
1239
  return getattr(self, cache_attr)
1247
1240
 
1248
- def contribute_to_class(self, cls, name, **kwargs):
1249
- super().contribute_to_class(cls, name, **kwargs)
1241
+ def contribute_to_class(self, cls, name):
1242
+ super().contribute_to_class(cls, name)
1250
1243
 
1251
1244
  def resolve_through_model(_, model, field):
1252
1245
  field.remote_field.through = model
@@ -208,9 +208,7 @@ class ForwardManyToOneDescriptor:
208
208
  value = value if value else None
209
209
 
210
210
  # An object must be an instance of the related class.
211
- if value is not None and not isinstance(
212
- value, self.field.remote_field.model._meta.concrete_model
213
- ):
211
+ if value is not None and not isinstance(value, self.field.remote_field.model):
214
212
  raise ValueError(
215
213
  f'Cannot assign "{value!r}": "{instance._meta.object_name}.{self.field.name}" must be a "{self.field.remote_field.model._meta.object_name}" instance.'
216
214
  )
@@ -80,7 +80,7 @@ class ReverseManyToOneManager(BaseRelatedManager):
80
80
  """
81
81
  Filter the queryset for the instance this manager is bound to.
82
82
  """
83
- from plain.exceptions import FieldError
83
+ from plain.models.exceptions import FieldError
84
84
 
85
85
  queryset._defer_next_filter = True
86
86
  queryset = queryset.filter(**self.core_filters)
@@ -11,7 +11,7 @@ they're the closest concept currently available.
11
11
 
12
12
  from functools import cached_property
13
13
 
14
- from plain import exceptions
14
+ from plain.models.exceptions import FieldDoesNotExist, FieldError
15
15
  from plain.utils.hashable import make_hashable
16
16
 
17
17
  from . import BLANK_CHOICE_DASH
@@ -79,9 +79,7 @@ class ForeignObjectRel(FieldCacheMixin):
79
79
  """
80
80
  target_fields = self.path_infos[-1].target_fields
81
81
  if len(target_fields) > 1:
82
- raise exceptions.FieldError(
83
- "Can't use target_field for multicolumn relations."
84
- )
82
+ raise FieldError("Can't use target_field for multicolumn relations.")
85
83
  return target_fields[0]
86
84
 
87
85
  @cached_property
@@ -274,7 +272,7 @@ class ManyToOneRel(ForeignObjectRel):
274
272
  """
275
273
  field = self.model._meta.get_field("id")
276
274
  if not field.concrete:
277
- raise exceptions.FieldDoesNotExist("No related field named 'id'")
275
+ raise FieldDoesNotExist("No related field named 'id'")
278
276
  return field
279
277
 
280
278
  def set_field_name(self):
plain/models/forms.py CHANGED
@@ -7,13 +7,13 @@ from itertools import chain
7
7
 
8
8
  from plain.exceptions import (
9
9
  NON_FIELD_ERRORS,
10
- FieldError,
11
10
  ImproperlyConfigured,
12
11
  ValidationError,
13
12
  )
14
13
  from plain.forms import fields
15
14
  from plain.forms.fields import ChoiceField, Field
16
15
  from plain.forms.forms import BaseForm, DeclarativeFieldsMetaclass
16
+ from plain.models.exceptions import FieldError
17
17
 
18
18
  __all__ = (
19
19
  "ModelForm",
plain/models/lookups.py CHANGED
@@ -2,7 +2,7 @@ import itertools
2
2
  import math
3
3
  from functools import cached_property
4
4
 
5
- from plain.exceptions import EmptyResultSet, FullResultSet
5
+ from plain.models.exceptions import EmptyResultSet, FullResultSet
6
6
  from plain.models.expressions import Expression, Func, Value
7
7
  from plain.models.fields import (
8
8
  BooleanField,
@@ -1,3 +1,6 @@
1
+ from contextlib import nullcontext
2
+
3
+ from ..transaction import atomic
1
4
  from .loader import MigrationLoader
2
5
  from .recorder import MigrationRecorder
3
6
  from .state import ProjectState
@@ -52,12 +55,14 @@ class MigrationExecutor:
52
55
  migration.mutate_state(state, preserve=False)
53
56
  return state
54
57
 
55
- def migrate(self, targets, plan=None, state=None, fake=False):
58
+ def migrate(self, targets, plan=None, state=None, fake=False, atomic_batch=False):
56
59
  """
57
60
  Migrate the database up to the given targets.
58
61
 
59
62
  Plain first needs to create all project states before a migration is
60
63
  (un)applied and in a second step run all the database operations.
64
+
65
+ atomic_batch: Whether to run all migrations in a single transaction.
61
66
  """
62
67
  # The plain_migrations table must be present to record applied
63
68
  # migrations, but don't create it if there are no migrations to apply.
@@ -82,34 +87,31 @@ class MigrationExecutor:
82
87
  if state is None:
83
88
  # The resulting state should still include applied migrations.
84
89
  state = self._create_project_state(with_applied_migrations=True)
85
- state = self._migrate_all_forwards(state, plan, full_plan, fake=fake)
86
-
87
- self.check_replacements()
88
90
 
89
- return state
91
+ migrations_to_run = set(plan)
92
+
93
+ # Choose context manager based on atomic_batch
94
+ batch_context = atomic if (atomic_batch and len(plan) > 1) else nullcontext
95
+
96
+ with batch_context():
97
+ for migration in full_plan:
98
+ if not migrations_to_run:
99
+ # We remove every migration that we applied from these sets so
100
+ # that we can bail out once the last migration has been applied
101
+ # and don't always run until the very end of the migration
102
+ # process.
103
+ break
104
+ if migration in migrations_to_run:
105
+ if "models_registry" not in state.__dict__:
106
+ if self.progress_callback:
107
+ self.progress_callback("render_start")
108
+ state.models_registry # Render all -- performance critical
109
+ if self.progress_callback:
110
+ self.progress_callback("render_success")
111
+ state = self.apply_migration(state, migration, fake=fake)
112
+ migrations_to_run.remove(migration)
90
113
 
91
- def _migrate_all_forwards(self, state, plan, full_plan, fake):
92
- """
93
- Take a list of 2-tuples of the form (migration instance, False) and
94
- apply them in the order they occur in the full_plan.
95
- """
96
- migrations_to_run = set(plan)
97
- for migration in full_plan:
98
- if not migrations_to_run:
99
- # We remove every migration that we applied from these sets so
100
- # that we can bail out once the last migration has been applied
101
- # and don't always run until the very end of the migration
102
- # process.
103
- break
104
- if migration in migrations_to_run:
105
- if "models_registry" not in state.__dict__:
106
- if self.progress_callback:
107
- self.progress_callback("render_start")
108
- state.models_registry # Render all -- performance critical
109
- if self.progress_callback:
110
- self.progress_callback("render_success")
111
- state = self.apply_migration(state, migration, fake=fake)
112
- migrations_to_run.remove(migration)
114
+ self.check_replacements()
113
115
 
114
116
  return state
115
117
 
@@ -4,7 +4,7 @@ from contextlib import contextmanager
4
4
  from functools import cached_property, partial
5
5
 
6
6
  from plain import models
7
- from plain.exceptions import FieldDoesNotExist
7
+ from plain.models.exceptions import FieldDoesNotExist
8
8
  from plain.models.fields import NOT_PROVIDED
9
9
  from plain.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT
10
10
  from plain.models.migrations.utils import field_is_referenced, get_references
plain/models/options.py CHANGED
@@ -3,11 +3,10 @@ import inspect
3
3
  from collections import defaultdict
4
4
  from functools import cached_property
5
5
 
6
- from plain.exceptions import FieldDoesNotExist
7
6
  from plain.models import models_registry
8
7
  from plain.models.constraints import UniqueConstraint
9
8
  from plain.models.db import db_connection
10
- from plain.models.fields import PrimaryKeyField
9
+ from plain.models.exceptions import FieldDoesNotExist
11
10
  from plain.models.query import QuerySet
12
11
  from plain.utils.datastructures import ImmutableList
13
12
 
@@ -70,11 +69,6 @@ class Options:
70
69
  self.required_db_vendor = None
71
70
  self.meta = meta
72
71
 
73
- # For any non-abstract class, the concrete class is the model
74
- # in the end of the proxy_for_model chain. In particular, for
75
- # concrete models, the concrete_model is always the class itself.
76
- self.concrete_model = None
77
-
78
72
  # List of all lookups defined in ForeignKey 'limit_choices_to' options
79
73
  # from *other* models. Needed for some admin checks. Internal use only.
80
74
  self.related_fkey_lookups = []
@@ -156,11 +150,7 @@ class Options:
156
150
  new_objs.append(obj)
157
151
  return new_objs
158
152
 
159
- def _prepare(self, model):
160
- if not any(f.name == "id" for f in self.local_fields):
161
- model.add_to_class("id", PrimaryKeyField())
162
-
163
- def add_field(self, field, private=False):
153
+ def add_field(self, field):
164
154
  # Insert the given field in the order in which it was created, using
165
155
  # the "creation_counter" attribute of the field.
166
156
  # Move many-to-many related fields from self.fields into
@@ -417,7 +407,7 @@ class Options:
417
407
  )
418
408
  for f in fields_with_relations:
419
409
  if not isinstance(f.remote_field.model, str):
420
- remote_label = f.remote_field.model._meta.concrete_model._meta.label
410
+ remote_label = f.remote_field.model._meta.label
421
411
  related_objects_graph[remote_label].append(f)
422
412
 
423
413
  for model in all_models:
@@ -426,9 +416,7 @@ class Options:
426
416
  # __dict__ takes precedence over a data descriptor (such as
427
417
  # @cached_property). This means that the _meta._relation_tree is
428
418
  # only called if related_objects is not in __dict__.
429
- related_objects = related_objects_graph[
430
- model._meta.concrete_model._meta.label
431
- ]
419
+ related_objects = related_objects_graph[model._meta.label]
432
420
  model._meta.__dict__["_relation_tree"] = related_objects
433
421
  # It seems it is possible that self is not in all_models, so guard
434
422
  # against that with default for get().
plain/models/query.py CHANGED
@@ -9,7 +9,6 @@ from functools import cached_property
9
9
  from itertools import chain, islice
10
10
 
11
11
  import plain.runtime
12
- from plain import exceptions
13
12
  from plain.exceptions import ValidationError
14
13
  from plain.models import (
15
14
  sql,
@@ -22,6 +21,11 @@ from plain.models.db import (
22
21
  NotSupportedError,
23
22
  db_connection,
24
23
  )
24
+ from plain.models.exceptions import (
25
+ FieldDoesNotExist,
26
+ FieldError,
27
+ ObjectDoesNotExist,
28
+ )
25
29
  from plain.models.expressions import Case, F, Value, When
26
30
  from plain.models.fields import (
27
31
  DateField,
@@ -129,9 +133,7 @@ class RawModelIterable(BaseIterable):
129
133
  ) = self.queryset.resolve_model_init_order()
130
134
  model_cls = self.queryset.model
131
135
  if "id" not in model_init_names:
132
- raise exceptions.FieldDoesNotExist(
133
- "Raw query must include the primary key"
134
- )
136
+ raise FieldDoesNotExist("Raw query must include the primary key")
135
137
  fields = [self.queryset.model_fields.get(c) for c in self.queryset.columns]
136
138
  converters = compiler.get_converters(
137
139
  [f.get_col(f.model._meta.db_table) if f else None for f in fields]
@@ -850,12 +852,12 @@ class QuerySet:
850
852
  for param in params:
851
853
  try:
852
854
  self.model._meta.get_field(param)
853
- except exceptions.FieldDoesNotExist:
855
+ except FieldDoesNotExist:
854
856
  # It's okay to use a model's property if it has a setter.
855
857
  if not (param in property_names and getattr(self.model, param).fset):
856
858
  invalid_params.append(param)
857
859
  if invalid_params:
858
- raise exceptions.FieldError(
860
+ raise FieldError(
859
861
  "Invalid field name(s) for model {}: '{}'.".format(
860
862
  self.model._meta.object_name,
861
863
  "', '".join(sorted(invalid_params)),
@@ -982,7 +984,7 @@ class QuerySet:
982
984
  descending = True
983
985
  if annotation := query.annotations.get(alias):
984
986
  if getattr(annotation, "contains_aggregate", False):
985
- raise exceptions.FieldError(
987
+ raise FieldError(
986
988
  f"Cannot update when ordering by an aggregate: {annotation}"
987
989
  )
988
990
  if descending:
@@ -1034,7 +1036,7 @@ class QuerySet:
1034
1036
  "Cannot call QuerySet.contains() after .values() or .values_list()."
1035
1037
  )
1036
1038
  try:
1037
- if obj._meta.concrete_model != self.model._meta.concrete_model:
1039
+ if obj.__class__ != self.model:
1038
1040
  return False
1039
1041
  except AttributeError:
1040
1042
  raise TypeError("'obj' must be a model instance.")
@@ -2039,7 +2041,7 @@ def prefetch_related_objects(model_instances, *related_lookups):
2039
2041
  else:
2040
2042
  try:
2041
2043
  new_obj = getattr(obj, through_attr)
2042
- except exceptions.ObjectDoesNotExist:
2044
+ except ObjectDoesNotExist:
2043
2045
  continue
2044
2046
  if new_obj is None:
2045
2047
  continue
@@ -2174,7 +2176,7 @@ def prefetch_one_level(instances, prefetcher, lookup, level):
2174
2176
  model = instances[0].__class__
2175
2177
  try:
2176
2178
  model._meta.get_field(to_attr)
2177
- except exceptions.FieldDoesNotExist:
2179
+ except FieldDoesNotExist:
2178
2180
  pass
2179
2181
  else:
2180
2182
  msg = "to_attr={} conflicts with a field on the {} model."
@@ -11,9 +11,9 @@ import inspect
11
11
  import logging
12
12
  from collections import namedtuple
13
13
 
14
- from plain.exceptions import FieldError
15
14
  from plain.models.constants import LOOKUP_SEP
16
15
  from plain.models.db import DatabaseError, db_connection
16
+ from plain.models.exceptions import FieldError
17
17
  from plain.utils import tree
18
18
 
19
19
  logger = logging.getLogger("plain.models")
@@ -355,7 +355,7 @@ def check_rel_lookup_compatibility(model, target_opts, field):
355
355
  """
356
356
 
357
357
  def check(opts):
358
- return model._meta.concrete_model == opts.concrete_model
358
+ return model == opts.model
359
359
 
360
360
  # If the field is a primary key, then doing a query against the field's
361
361
  # model is ok, too. Consider the case:
@@ -4,9 +4,9 @@ import re
4
4
  from functools import cached_property, partial
5
5
  from itertools import chain
6
6
 
7
- from plain.exceptions import EmptyResultSet, FieldError, FullResultSet
8
7
  from plain.models.constants import LOOKUP_SEP
9
8
  from plain.models.db import DatabaseError, NotSupportedError
9
+ from plain.models.exceptions import EmptyResultSet, FieldError, FullResultSet
10
10
  from plain.models.expressions import F, OrderBy, RawSQL, Ref, Value
11
11
  from plain.models.functions import Cast, Random
12
12
  from plain.models.lookups import Lookup
@@ -940,9 +940,9 @@ class SQLCompiler:
940
940
  start_alias = start_alias or self.query.get_initial_alias()
941
941
 
942
942
  for field in opts.concrete_fields:
943
- model = field.model._meta.concrete_model
944
- # A proxy model will have a different model and concrete_model. We
945
- # will assign None if the field belongs to this model.
943
+ model = field.model
944
+ # Since we no longer have proxy models or inheritance,
945
+ # the field's model should always be the same as opts.model.
946
946
  if model == opts.model:
947
947
  model = None
948
948
  if select_mask and field not in select_mask:
@@ -1331,9 +1331,9 @@ class SQLCompiler:
1331
1331
  select_fields is filled recursively, so it also contains fields
1332
1332
  from the parent models.
1333
1333
  """
1334
- concrete_model = klass_info["model"]._meta.concrete_model
1334
+ model = klass_info["model"]
1335
1335
  for select_index in klass_info["select_fields"]:
1336
- if self.select[select_index][0].target.model == concrete_model:
1336
+ if self.select[select_index][0].target.model == model:
1337
1337
  return self.select[select_index][0]
1338
1338
 
1339
1339
  def _get_field_choices():
@@ -3,7 +3,7 @@ Useful auxiliary data structures for query construction. Not useful outside
3
3
  the SQL domain.
4
4
  """
5
5
 
6
- from plain.exceptions import FullResultSet
6
+ from plain.models.exceptions import FullResultSet
7
7
  from plain.models.sql.constants import INNER, LOUTER
8
8
 
9
9
 
plain/models/sql/query.py CHANGED
@@ -17,10 +17,10 @@ from functools import cached_property
17
17
  from itertools import chain, count, product
18
18
  from string import ascii_uppercase
19
19
 
20
- from plain.exceptions import FieldDoesNotExist, FieldError
21
20
  from plain.models.aggregates import Count
22
21
  from plain.models.constants import LOOKUP_SEP
23
22
  from plain.models.db import NotSupportedError, db_connection
23
+ from plain.models.exceptions import FieldDoesNotExist, FieldError
24
24
  from plain.models.expressions import (
25
25
  BaseExpression,
26
26
  Col,
@@ -706,7 +706,7 @@ class Query(BaseExpression):
706
706
  if not field.is_relation:
707
707
  raise FieldError(next(iter(field_mask)))
708
708
  field_select_mask = select_mask.setdefault(field, {})
709
- related_model = field.remote_field.model._meta.concrete_model
709
+ related_model = field.remote_field.model
710
710
  self._get_defer_select_mask(
711
711
  related_model._meta, field_mask, field_select_mask
712
712
  )
@@ -721,7 +721,7 @@ class Query(BaseExpression):
721
721
  else:
722
722
  field = opts.get_field(field_name).field
723
723
  field_select_mask = select_mask.setdefault(field, {})
724
- related_model = field.model._meta.concrete_model
724
+ related_model = field.model
725
725
  self._get_defer_select_mask(
726
726
  related_model._meta, field_mask, field_select_mask
727
727
  )
@@ -738,7 +738,7 @@ class Query(BaseExpression):
738
738
  if field_mask:
739
739
  if not field.is_relation:
740
740
  raise FieldError(next(iter(field_mask)))
741
- related_model = field.remote_field.model._meta.concrete_model
741
+ related_model = field.remote_field.model
742
742
  self._get_only_select_mask(
743
743
  related_model._meta, field_mask, field_select_mask
744
744
  )
@@ -2,7 +2,7 @@
2
2
  Query subclasses which provide extra functionality beyond simple data retrieval.
3
3
  """
4
4
 
5
- from plain.exceptions import FieldError
5
+ from plain.models.exceptions import FieldError
6
6
  from plain.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE, NO_RESULTS
7
7
  from plain.models.sql.query import Query
8
8
 
@@ -87,13 +87,13 @@ class UpdateQuery(Query):
87
87
  direct = (
88
88
  not (field.auto_created and not field.concrete) or not field.concrete
89
89
  )
90
- model = field.model._meta.concrete_model
90
+ model = field.model
91
91
  if not direct or (field.is_relation and field.many_to_many):
92
92
  raise FieldError(
93
93
  f"Cannot update model field {field!r} (only non-relations and "
94
94
  "foreign keys permitted)."
95
95
  )
96
- if model is not self.get_meta().concrete_model:
96
+ if model is not self.get_meta().model:
97
97
  self.add_related_update(model, field, val)
98
98
  continue
99
99
  values_seq.append((field, model, val))
plain/models/sql/where.py CHANGED
@@ -5,7 +5,7 @@ Code to manage the creation and SQL rendering of 'where' constraints.
5
5
  import operator
6
6
  from functools import cached_property, reduce
7
7
 
8
- from plain.exceptions import EmptyResultSet, FullResultSet
8
+ from plain.models.exceptions import EmptyResultSet, FullResultSet
9
9
  from plain.models.expressions import Case, When
10
10
  from plain.models.lookups import Exact
11
11
  from plain.utils import tree
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.models
3
- Version: 0.47.0
3
+ Version: 0.49.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