plain.models 0.38.0__py3-none-any.whl → 0.39.1__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 (40) hide show
  1. plain/models/CHANGELOG.md +31 -0
  2. plain/models/README.md +31 -0
  3. plain/models/__init__.py +2 -2
  4. plain/models/backends/base/creation.py +1 -1
  5. plain/models/backends/base/operations.py +1 -3
  6. plain/models/backends/base/schema.py +4 -8
  7. plain/models/backends/mysql/base.py +1 -3
  8. plain/models/backends/mysql/introspection.py +2 -6
  9. plain/models/backends/mysql/operations.py +2 -4
  10. plain/models/backends/postgresql/base.py +2 -6
  11. plain/models/backends/postgresql/introspection.py +2 -6
  12. plain/models/backends/postgresql/operations.py +1 -3
  13. plain/models/backends/postgresql/schema.py +2 -10
  14. plain/models/backends/sqlite3/base.py +2 -6
  15. plain/models/backends/sqlite3/introspection.py +2 -8
  16. plain/models/base.py +46 -74
  17. plain/models/constraints.py +3 -3
  18. plain/models/deletion.py +9 -9
  19. plain/models/fields/__init__.py +30 -104
  20. plain/models/fields/related.py +90 -343
  21. plain/models/fields/related_descriptors.py +14 -14
  22. plain/models/fields/related_lookups.py +2 -2
  23. plain/models/fields/reverse_related.py +6 -14
  24. plain/models/forms.py +14 -76
  25. plain/models/lookups.py +2 -2
  26. plain/models/migrations/autodetector.py +2 -25
  27. plain/models/migrations/operations/fields.py +0 -6
  28. plain/models/migrations/state.py +2 -26
  29. plain/models/migrations/utils.py +4 -14
  30. plain/models/options.py +4 -12
  31. plain/models/query.py +46 -54
  32. plain/models/query_utils.py +3 -5
  33. plain/models/sql/compiler.py +16 -18
  34. plain/models/sql/query.py +12 -11
  35. plain/models/sql/subqueries.py +10 -10
  36. {plain_models-0.38.0.dist-info → plain_models-0.39.1.dist-info}/METADATA +32 -1
  37. {plain_models-0.38.0.dist-info → plain_models-0.39.1.dist-info}/RECORD +40 -40
  38. {plain_models-0.38.0.dist-info → plain_models-0.39.1.dist-info}/WHEEL +0 -0
  39. {plain_models-0.38.0.dist-info → plain_models-0.39.1.dist-info}/entry_points.txt +0 -0
  40. {plain_models-0.38.0.dist-info → plain_models-0.39.1.dist-info}/licenses/LICENSE +0 -0
plain/models/deletion.py CHANGED
@@ -167,8 +167,8 @@ class Collector:
167
167
  if model in self.restricted_objects:
168
168
  objs = set(
169
169
  qs.filter(
170
- pk__in=[
171
- obj.pk
170
+ id__in=[
171
+ obj.id
172
172
  for objs in self.restricted_objects[model].values()
173
173
  for obj in objs
174
174
  ]
@@ -375,7 +375,7 @@ class Collector:
375
375
  def delete(self):
376
376
  # sort instance collections
377
377
  for model, instances in self.data.items():
378
- self.data[model] = sorted(instances, key=attrgetter("pk"))
378
+ self.data[model] = sorted(instances, key=attrgetter("id"))
379
379
 
380
380
  # if possible, bring the models in an order suitable for databases that
381
381
  # don't support transactions or cannot defer constraint checks until the
@@ -389,8 +389,8 @@ class Collector:
389
389
  instance = list(instances)[0]
390
390
  if self.can_fast_delete(instance):
391
391
  with transaction.mark_for_rollback_on_error():
392
- count = sql.DeleteQuery(model).delete_batch([instance.pk])
393
- setattr(instance, model._meta.pk.attname, None)
392
+ count = sql.DeleteQuery(model).delete_batch([instance.id])
393
+ setattr(instance, model._meta.get_field("id").attname, None)
394
394
  return count, {model._meta.label: count}
395
395
 
396
396
  with transaction.atomic(savepoint=False):
@@ -419,7 +419,7 @@ class Collector:
419
419
  model = objs[0].__class__
420
420
  query = sql.UpdateQuery(model)
421
421
  query.update_batch(
422
- list({obj.pk for obj in objs}), {field.name: value}
422
+ list({obj.id for obj in objs}), {field.name: value}
423
423
  )
424
424
 
425
425
  # reverse instance collections
@@ -429,12 +429,12 @@ class Collector:
429
429
  # delete instances
430
430
  for model, instances in self.data.items():
431
431
  query = sql.DeleteQuery(model)
432
- pk_list = [obj.pk for obj in instances]
433
- count = query.delete_batch(pk_list)
432
+ id_list = [obj.id for obj in instances]
433
+ count = query.delete_batch(id_list)
434
434
  if count:
435
435
  deleted_counter[model._meta.label] += count
436
436
 
437
437
  for model, instances in self.data.items():
438
438
  for instance in instances:
439
- setattr(instance, model._meta.pk.attname, None)
439
+ setattr(instance, model._meta.get_field("id").attname, None)
440
440
  return sum(deleted_counter.values()), dict(deleted_counter)
@@ -30,9 +30,8 @@ from plain.utils.itercompat import is_iterable
30
30
  from ..registry import models_registry
31
31
 
32
32
  __all__ = [
33
- "AutoField",
34
33
  "BLANK_CHOICE_DASH",
35
- "BigAutoField",
34
+ "PrimaryKeyField",
36
35
  "BigIntegerField",
37
36
  "BinaryField",
38
37
  "BooleanField",
@@ -54,7 +53,6 @@ __all__ = [
54
53
  "PositiveBigIntegerField",
55
54
  "PositiveIntegerField",
56
55
  "PositiveSmallIntegerField",
57
- "SmallAutoField",
58
56
  "SmallIntegerField",
59
57
  "TextField",
60
58
  "TimeField",
@@ -93,9 +91,7 @@ def _load_field(package_label, model_name, field_name):
93
91
  # "attname", except if db_column is specified.
94
92
  #
95
93
  # Code that introspects values, or does other dynamic things, should use
96
- # attname. For example, this gets the primary key value of object "obj":
97
- #
98
- # getattr(obj, opts.pk.attname)
94
+ # attname.
99
95
 
100
96
 
101
97
  def _empty(of_cls):
@@ -166,7 +162,6 @@ class Field(RegisterLookupMixin):
166
162
  def __init__(
167
163
  self,
168
164
  *,
169
- primary_key=False,
170
165
  max_length=None,
171
166
  required=True,
172
167
  allow_null=False,
@@ -174,13 +169,11 @@ class Field(RegisterLookupMixin):
174
169
  default=NOT_PROVIDED,
175
170
  choices=None,
176
171
  db_column=None,
177
- auto_created=False,
178
172
  validators=(),
179
173
  error_messages=None,
180
174
  db_comment=None,
181
175
  ):
182
176
  self.name = None # Set by set_attributes_from_name
183
- self.primary_key = primary_key
184
177
  self.max_length = max_length
185
178
  self.required, self.allow_null = required, allow_null
186
179
  self.remote_field = rel
@@ -195,15 +188,13 @@ class Field(RegisterLookupMixin):
195
188
  self.choices = choices
196
189
  self.db_column = db_column
197
190
  self.db_comment = db_comment
198
- self.auto_created = auto_created
191
+
192
+ self.primary_key = False
193
+ self.auto_created = False
199
194
 
200
195
  # Adjust the appropriate creation counter, and save our local copy.
201
- if auto_created:
202
- self.creation_counter = Field.auto_creation_counter
203
- Field.auto_creation_counter -= 1
204
- else:
205
- self.creation_counter = Field.creation_counter
206
- Field.creation_counter += 1
196
+ self.creation_counter = Field.creation_counter
197
+ Field.creation_counter += 1
207
198
 
208
199
  self._validators = list(validators) # Store for deconstruction later
209
200
 
@@ -241,7 +232,7 @@ class Field(RegisterLookupMixin):
241
232
  def _check_field_name(self):
242
233
  """
243
234
  Check if field name is valid, i.e. 1) does not end with an
244
- underscore, 2) does not contain "__" and 3) is not "pk".
235
+ underscore, 2) does not contain "__" and 3) is not "id".
245
236
  """
246
237
  if self.name.endswith("_"):
247
238
  return [
@@ -259,10 +250,10 @@ class Field(RegisterLookupMixin):
259
250
  id="fields.E002",
260
251
  )
261
252
  ]
262
- elif self.name == "pk":
253
+ elif self.name == "id":
263
254
  return [
264
255
  preflight.Error(
265
- "'pk' is a reserved word that cannot be used as a field name.",
256
+ "'id' is a reserved word that cannot be used as a field name.",
266
257
  obj=self,
267
258
  id="fields.E003",
268
259
  )
@@ -492,7 +483,6 @@ class Field(RegisterLookupMixin):
492
483
  # Short-form way of fetching all the default parameters
493
484
  keywords = {}
494
485
  possibles = {
495
- "primary_key": False,
496
486
  "max_length": None,
497
487
  "required": True,
498
488
  "allow_null": False,
@@ -500,7 +490,6 @@ class Field(RegisterLookupMixin):
500
490
  "choices": None,
501
491
  "db_column": None,
502
492
  "db_comment": None,
503
- "auto_created": False,
504
493
  "validators": [],
505
494
  "error_messages": None,
506
495
  }
@@ -615,9 +604,9 @@ class Field(RegisterLookupMixin):
615
604
  self.name,
616
605
  )
617
606
 
618
- def get_pk_value_on_save(self, instance):
607
+ def get_id_value_on_save(self, instance):
619
608
  """
620
- Hook to generate new PK values on save. This method is called when
609
+ Hook to generate new primary key values on save. This method is called when
621
610
  saving instances with no primary key value set. If this method returns
622
611
  something else than None, then the returned value is used when saving
623
612
  the new instance.
@@ -910,7 +899,7 @@ class Field(RegisterLookupMixin):
910
899
  choice_func = operator.attrgetter(
911
900
  self.remote_field.get_related_field().attname
912
901
  if hasattr(self.remote_field, "get_related_field")
913
- else "pk"
902
+ else "id"
914
903
  )
915
904
  qs = rel_model._default_manager.complex_filter(limit_choices_to)
916
905
  if ordering:
@@ -2234,36 +2223,28 @@ class UUIDField(Field):
2234
2223
  return value
2235
2224
 
2236
2225
 
2237
- class AutoFieldMixin:
2226
+ class PrimaryKeyField(BigIntegerField):
2238
2227
  db_returning = True
2239
2228
 
2240
- def __init__(self, *args, **kwargs):
2241
- kwargs["required"] = False
2242
- super().__init__(*args, **kwargs)
2229
+ def __init__(self):
2230
+ super().__init__(required=False)
2231
+ self.primary_key = True
2232
+ self.auto_created = True
2233
+ # Adjust creation counter for auto-created fields
2234
+ # We need to undo the counter increment from Field.__init__ and use the auto counter
2235
+ Field.creation_counter -= 1 # Undo the increment
2236
+ self.creation_counter = Field.auto_creation_counter
2237
+ Field.auto_creation_counter -= 1
2243
2238
 
2244
2239
  def check(self, **kwargs):
2245
- return [
2246
- *super().check(**kwargs),
2247
- *self._check_primary_key(),
2248
- ]
2249
-
2250
- def _check_primary_key(self):
2251
- if not self.primary_key:
2252
- return [
2253
- preflight.Error(
2254
- "AutoFields must set primary_key=True.",
2255
- obj=self,
2256
- id="fields.E100",
2257
- ),
2258
- ]
2259
- else:
2260
- return []
2240
+ errors = super().check(**kwargs)
2241
+ # Remove the E003 error for 'id' field name since PrimaryKeyField is allowed to use it
2242
+ errors = [e for e in errors if e.id != "fields.E003"]
2243
+ return errors
2261
2244
 
2262
2245
  def deconstruct(self):
2263
- name, path, args, kwargs = super().deconstruct()
2264
- del kwargs["required"]
2265
- kwargs["primary_key"] = True
2266
- return name, path, args, kwargs
2246
+ # PrimaryKeyField takes no parameters, so we return an empty kwargs dict
2247
+ return (self.name, "plain.models.PrimaryKeyField", [], {})
2267
2248
 
2268
2249
  def validate(self, value, model_instance):
2269
2250
  pass
@@ -2275,65 +2256,10 @@ class AutoFieldMixin:
2275
2256
  return value
2276
2257
 
2277
2258
  def contribute_to_class(self, cls, name, **kwargs):
2278
- if cls._meta.auto_field:
2279
- raise ValueError(
2280
- f"Model {cls._meta.label} can't have more than one auto-generated field."
2281
- )
2282
2259
  super().contribute_to_class(cls, name, **kwargs)
2283
- cls._meta.auto_field = self
2284
2260
 
2285
-
2286
- class AutoFieldMeta(type):
2287
- """
2288
- Metaclass to maintain backward inheritance compatibility for AutoField.
2289
-
2290
- It is intended that AutoFieldMixin become public API when it is possible to
2291
- create a non-integer automatically-generated field using column defaults
2292
- stored in the database.
2293
-
2294
- In many areas Plain also relies on using isinstance() to check for an
2295
- automatically-generated field as a subclass of AutoField. A new flag needs
2296
- to be implemented on Field to be used instead.
2297
-
2298
- When these issues have been addressed, this metaclass could be used to
2299
- deprecate inheritance from AutoField and use of isinstance() with AutoField
2300
- for detecting automatically-generated fields.
2301
- """
2302
-
2303
- @property
2304
- def _subclasses(self):
2305
- return (BigAutoField, SmallAutoField)
2306
-
2307
- def __instancecheck__(self, instance):
2308
- return isinstance(instance, self._subclasses) or super().__instancecheck__(
2309
- instance
2310
- )
2311
-
2312
- def __subclasscheck__(self, subclass):
2313
- return issubclass(subclass, self._subclasses) or super().__subclasscheck__(
2314
- subclass
2315
- )
2316
-
2317
-
2318
- class AutoField(AutoFieldMixin, IntegerField, metaclass=AutoFieldMeta):
2319
- def get_internal_type(self):
2320
- return "AutoField"
2321
-
2322
- def rel_db_type(self, connection):
2323
- return IntegerField().db_type(connection=connection)
2324
-
2325
-
2326
- class BigAutoField(AutoFieldMixin, BigIntegerField):
2327
2261
  def get_internal_type(self):
2328
- return "BigAutoField"
2262
+ return "PrimaryKeyField"
2329
2263
 
2330
2264
  def rel_db_type(self, connection):
2331
2265
  return BigIntegerField().db_type(connection=connection)
2332
-
2333
-
2334
- class SmallAutoField(AutoFieldMixin, SmallIntegerField):
2335
- def get_internal_type(self):
2336
- return "SmallAutoField"
2337
-
2338
- def rel_db_type(self, connection):
2339
- return SmallIntegerField().db_type(connection=connection)