diff options
Diffstat (limited to 'django/db/models/base.py')
| -rw-r--r-- | django/db/models/base.py | 841 |
1 files changed, 519 insertions, 322 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py index 37f6a3dd58..8127a9895a 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -9,28 +9,42 @@ from django.apps import apps from django.conf import settings from django.core import checks from django.core.exceptions import ( - NON_FIELD_ERRORS, FieldDoesNotExist, FieldError, MultipleObjectsReturned, - ObjectDoesNotExist, ValidationError, + NON_FIELD_ERRORS, + FieldDoesNotExist, + FieldError, + MultipleObjectsReturned, + ObjectDoesNotExist, + ValidationError, ) from django.db import ( - DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connection, - connections, router, transaction, -) -from django.db.models import ( - NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value, + DEFAULT_DB_ALIAS, + DJANGO_VERSION_PICKLE_KEY, + DatabaseError, + connection, + connections, + router, + transaction, ) +from django.db.models import NOT_PROVIDED, ExpressionWrapper, IntegerField, Max, Value from django.db.models.constants import LOOKUP_SEP from django.db.models.constraints import CheckConstraint, UniqueConstraint from django.db.models.deletion import CASCADE, Collector from django.db.models.fields.related import ( - ForeignObjectRel, OneToOneField, lazy_related_operation, resolve_relation, + ForeignObjectRel, + OneToOneField, + lazy_related_operation, + resolve_relation, ) from django.db.models.functions import Coalesce from django.db.models.manager import Manager from django.db.models.options import Options from django.db.models.query import F, Q from django.db.models.signals import ( - class_prepared, post_init, post_save, pre_init, pre_save, + class_prepared, + post_init, + post_save, + pre_init, + pre_save, ) from django.db.models.utils import make_model_tuple from django.utils.encoding import force_str @@ -41,10 +55,10 @@ from django.utils.translation import gettext_lazy as _ class Deferred: def __repr__(self): - return '<Deferred field>' + return "<Deferred field>" def __str__(self): - return '<Deferred field>' + return "<Deferred field>" DEFERRED = Deferred() @@ -58,19 +72,24 @@ def subclass_exception(name, bases, module, attached_to): that the returned exception class will be added as an attribute to the 'attached_to' class. """ - return type(name, bases, { - '__module__': module, - '__qualname__': '%s.%s' % (attached_to.__qualname__, name), - }) + return type( + name, + bases, + { + "__module__": module, + "__qualname__": "%s.%s" % (attached_to.__qualname__, name), + }, + ) def _has_contribute_to_class(value): # Only call contribute_to_class() if it's bound. - return not inspect.isclass(value) and hasattr(value, 'contribute_to_class') + return not inspect.isclass(value) and hasattr(value, "contribute_to_class") class ModelBase(type): """Metaclass for all models.""" + def __new__(cls, name, bases, attrs, **kwargs): super_new = super().__new__ @@ -81,12 +100,12 @@ class ModelBase(type): return super_new(cls, name, bases, attrs) # Create the class. - module = attrs.pop('__module__') - new_attrs = {'__module__': module} - classcell = attrs.pop('__classcell__', None) + module = attrs.pop("__module__") + new_attrs = {"__module__": module} + classcell = attrs.pop("__classcell__", None) if classcell is not None: - new_attrs['__classcell__'] = classcell - attr_meta = attrs.pop('Meta', None) + new_attrs["__classcell__"] = classcell + attr_meta = attrs.pop("Meta", None) # Pass all attrs without a (Django-specific) contribute_to_class() # method to type.__new__() so that they're properly initialized # (i.e. __set_name__()). @@ -98,16 +117,16 @@ class ModelBase(type): new_attrs[obj_name] = obj new_class = super_new(cls, name, bases, new_attrs, **kwargs) - abstract = getattr(attr_meta, 'abstract', False) - meta = attr_meta or getattr(new_class, 'Meta', None) - base_meta = getattr(new_class, '_meta', None) + abstract = getattr(attr_meta, "abstract", False) + meta = attr_meta or getattr(new_class, "Meta", None) + base_meta = getattr(new_class, "_meta", None) app_label = None # Look for an application configuration to attach the model to. app_config = apps.get_containing_app_config(module) - if getattr(meta, 'app_label', None) is None: + if getattr(meta, "app_label", None) is None: if app_config is None: if not abstract: raise RuntimeError( @@ -119,33 +138,43 @@ class ModelBase(type): else: app_label = app_config.label - new_class.add_to_class('_meta', Options(meta, app_label)) + new_class.add_to_class("_meta", Options(meta, app_label)) if not abstract: new_class.add_to_class( - 'DoesNotExist', + "DoesNotExist", subclass_exception( - 'DoesNotExist', + "DoesNotExist", tuple( - x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract - ) or (ObjectDoesNotExist,), + x.DoesNotExist + for x in parents + if hasattr(x, "_meta") and not x._meta.abstract + ) + or (ObjectDoesNotExist,), module, - attached_to=new_class)) + attached_to=new_class, + ), + ) new_class.add_to_class( - 'MultipleObjectsReturned', + "MultipleObjectsReturned", subclass_exception( - 'MultipleObjectsReturned', + "MultipleObjectsReturned", tuple( - x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract - ) or (MultipleObjectsReturned,), + x.MultipleObjectsReturned + for x in parents + if hasattr(x, "_meta") and not x._meta.abstract + ) + or (MultipleObjectsReturned,), module, - attached_to=new_class)) + attached_to=new_class, + ), + ) if base_meta and not base_meta.abstract: # Non-abstract child classes inherit some attributes from their # non-abstract parent (unless an ABC comes before it in the # method resolution order). - if not hasattr(meta, 'ordering'): + if not hasattr(meta, "ordering"): new_class._meta.ordering = base_meta.ordering - if not hasattr(meta, 'get_latest_by'): + if not hasattr(meta, "get_latest_by"): new_class._meta.get_latest_by = base_meta.get_latest_by is_proxy = new_class._meta.proxy @@ -153,7 +182,9 @@ class ModelBase(type): # If the model is a proxy, ensure that the base class # hasn't been swapped out. if is_proxy and base_meta and base_meta.swapped: - raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped)) + raise TypeError( + "%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped) + ) # Add remaining attributes (those with a contribute_to_class() method) # to the class. @@ -164,14 +195,14 @@ class ModelBase(type): new_fields = chain( new_class._meta.local_fields, new_class._meta.local_many_to_many, - new_class._meta.private_fields + new_class._meta.private_fields, ) field_names = {f.name for f in new_fields} # Basic setup for proxy models. if is_proxy: base = None - for parent in [kls for kls in parents if hasattr(kls, '_meta')]: + for parent in [kls for kls in parents if hasattr(kls, "_meta")]: if parent._meta.abstract: if parent._meta.fields: raise TypeError( @@ -183,9 +214,14 @@ class ModelBase(type): if base is None: base = parent elif parent._meta.concrete_model is not base._meta.concrete_model: - raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name) + raise TypeError( + "Proxy model '%s' has more than one non-abstract model base class." + % name + ) if base is None: - raise TypeError("Proxy model '%s' has no non-abstract model base class." % name) + raise TypeError( + "Proxy model '%s' has no non-abstract model base class." % name + ) new_class._meta.setup_proxy(base) new_class._meta.concrete_model = base._meta.concrete_model else: @@ -195,7 +231,7 @@ class ModelBase(type): parent_links = {} for base in reversed([new_class] + parents): # Conceptually equivalent to `if base is Model`. - if not hasattr(base, '_meta'): + if not hasattr(base, "_meta"): continue # Skip concrete parent classes. if base != new_class and not base._meta.abstract: @@ -210,7 +246,7 @@ class ModelBase(type): inherited_attributes = set() # Do the appropriate setup for any model parents. for base in new_class.mro(): - if base not in parents or not hasattr(base, '_meta'): + if base not in parents or not hasattr(base, "_meta"): # Things without _meta aren't functional models, so they're # uninteresting parents. inherited_attributes.update(base.__dict__) @@ -223,8 +259,9 @@ class ModelBase(type): for field in parent_fields: if field.name in field_names: raise FieldError( - 'Local field %r in class %r clashes with field of ' - 'the same name from base class %r.' % ( + "Local field %r in class %r clashes with field of " + "the same name from base class %r." + % ( field.name, name, base.__name__, @@ -239,7 +276,7 @@ class ModelBase(type): if base_key in parent_links: field = parent_links[base_key] elif not is_proxy: - attr_name = '%s_ptr' % base._meta.model_name + attr_name = "%s_ptr" % base._meta.model_name field = OneToOneField( base, on_delete=CASCADE, @@ -252,7 +289,8 @@ class ModelBase(type): raise FieldError( "Auto-generated field '%s' in class %r for " "parent_link to base class %r clashes with " - "declared field of the same name." % ( + "declared field of the same name." + % ( attr_name, name, base.__name__, @@ -271,9 +309,11 @@ class ModelBase(type): # Add fields from abstract base class if it wasn't overridden. for field in parent_fields: - if (field.name not in field_names and - field.name not in new_class.__dict__ and - field.name not in inherited_attributes): + if ( + field.name not in field_names + and field.name not in new_class.__dict__ + and field.name not in inherited_attributes + ): new_field = copy.deepcopy(field) new_class.add_to_class(field.name, new_field) # Replace parent links defined on this base by the new @@ -292,8 +332,9 @@ class ModelBase(type): if field.name in field_names: if not base._meta.abstract: raise FieldError( - 'Local field %r in class %r clashes with field of ' - 'the same name from base class %r.' % ( + "Local field %r in class %r clashes with field of " + "the same name from base class %r." + % ( field.name, name, base.__name__, @@ -307,7 +348,9 @@ class ModelBase(type): # Copy indexes so that index names are unique when models extend an # abstract model. - new_class._meta.indexes = [copy.deepcopy(idx) for idx in new_class._meta.indexes] + new_class._meta.indexes = [ + copy.deepcopy(idx) for idx in new_class._meta.indexes + ] if abstract: # Abstract base models can't be instantiated and don't appear in @@ -333,8 +376,12 @@ class ModelBase(type): opts._prepare(cls) if opts.order_with_respect_to: - cls.get_next_in_order = partialmethod(cls._get_next_or_previous_in_order, is_next=True) - cls.get_previous_in_order = partialmethod(cls._get_next_or_previous_in_order, is_next=False) + cls.get_next_in_order = partialmethod( + cls._get_next_or_previous_in_order, is_next=True + ) + cls.get_previous_in_order = partialmethod( + cls._get_next_or_previous_in_order, is_next=False + ) # Defer creating accessors on the foreign class until it has been # created and registered. If remote_field is None, we're ordering @@ -348,21 +395,26 @@ class ModelBase(type): # Give the class a docstring -- its definition. if cls.__doc__ is None: - cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join(f.name for f in opts.fields)) + cls.__doc__ = "%s(%s)" % ( + cls.__name__, + ", ".join(f.name for f in opts.fields), + ) - get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get(opts.label_lower) + get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get( + opts.label_lower + ) if get_absolute_url_override: - setattr(cls, 'get_absolute_url', get_absolute_url_override) + setattr(cls, "get_absolute_url", get_absolute_url_override) if not opts.managers: - if any(f.name == 'objects' for f in opts.fields): + if any(f.name == "objects" for f in opts.fields): raise ValueError( "Model %s must specify a custom Manager, because it has a " "field named 'objects'." % cls.__name__ ) manager = Manager() manager.auto_created = True - cls.add_to_class('objects', manager) + cls.add_to_class("objects", manager) # Set the name of _meta.indexes. This can't be done in # Options.contribute_to_class() because fields haven't been added to @@ -399,6 +451,7 @@ class ModelStateCacheDescriptor: class ModelState: """Store model instance state.""" + db = None # If true, uniqueness validation checks will consider this a new, unsaved # object. Necessary for correct validation of new instances of objects with @@ -410,19 +463,18 @@ class ModelState: def __getstate__(self): state = self.__dict__.copy() - if 'fields_cache' in state: - state['fields_cache'] = self.fields_cache.copy() + if "fields_cache" in state: + state["fields_cache"] = self.fields_cache.copy() # Manager instances stored in related_managers_cache won't necessarily # be deserializable if they were dynamically created via an inner # scope, e.g. create_forward_many_to_many_manager() and # create_generic_related_manager(). - if 'related_managers_cache' in state: - state['related_managers_cache'] = {} + if "related_managers_cache" in state: + state["related_managers_cache"] = {} return state class Model(metaclass=ModelBase): - def __init__(self, *args, **kwargs): # Alias some things as locals to avoid repeat global lookups cls = self.__class__ @@ -430,7 +482,7 @@ class Model(metaclass=ModelBase): _setattr = setattr _DEFERRED = DEFERRED if opts.abstract: - raise TypeError('Abstract models cannot be instantiated.') + raise TypeError("Abstract models cannot be instantiated.") pre_init.send(sender=cls, args=args, kwargs=kwargs) @@ -529,10 +581,10 @@ class Model(metaclass=ModelBase): if value is not _DEFERRED: _setattr(self, prop, value) if unexpected: - unexpected_names = ', '.join(repr(n) for n in unexpected) + unexpected_names = ", ".join(repr(n) for n in unexpected) raise TypeError( - f'{cls.__name__}() got unexpected keyword arguments: ' - f'{unexpected_names}' + f"{cls.__name__}() got unexpected keyword arguments: " + f"{unexpected_names}" ) super().__init__() post_init.send(sender=cls, instance=self) @@ -551,10 +603,10 @@ class Model(metaclass=ModelBase): return new def __repr__(self): - return '<%s: %s>' % (self.__class__.__name__, self) + return "<%s: %s>" % (self.__class__.__name__, self) def __str__(self): - return '%s object (%s)' % (self.__class__.__name__, self.pk) + return "%s object (%s)" % (self.__class__.__name__, self.pk) def __eq__(self, other): if not isinstance(other, Model): @@ -580,7 +632,7 @@ class Model(metaclass=ModelBase): def __getstate__(self): """Hook to allow choosing the attributes to pickle.""" state = self.__dict__.copy() - state['_state'] = copy.copy(state['_state']) + state["_state"] = copy.copy(state["_state"]) # memoryview cannot be pickled, so cast it to bytes and store # separately. _memoryview_attrs = [] @@ -588,7 +640,7 @@ class Model(metaclass=ModelBase): if isinstance(value, memoryview): _memoryview_attrs.append((attr, bytes(value))) if _memoryview_attrs: - state['_memoryview_attrs'] = _memoryview_attrs + state["_memoryview_attrs"] = _memoryview_attrs for attr, value in _memoryview_attrs: state.pop(attr) return state @@ -610,8 +662,8 @@ class Model(metaclass=ModelBase): RuntimeWarning, stacklevel=2, ) - if '_memoryview_attrs' in state: - for attr, value in state.pop('_memoryview_attrs'): + if "_memoryview_attrs" in state: + for attr, value in state.pop("_memoryview_attrs"): state[attr] = memoryview(value) self.__dict__.update(state) @@ -632,7 +684,8 @@ class Model(metaclass=ModelBase): Return a set containing names of deferred fields for this instance. """ return { - f.attname for f in self._meta.concrete_fields + f.attname + for f in self._meta.concrete_fields if f.attname not in self.__dict__ } @@ -654,7 +707,7 @@ class Model(metaclass=ModelBase): if fields is None: self._prefetched_objects_cache = {} else: - prefetched_objects_cache = getattr(self, '_prefetched_objects_cache', ()) + prefetched_objects_cache = getattr(self, "_prefetched_objects_cache", ()) for field in fields: if field in prefetched_objects_cache: del prefetched_objects_cache[field] @@ -664,10 +717,13 @@ class Model(metaclass=ModelBase): if any(LOOKUP_SEP in f for f in fields): raise ValueError( 'Found "%s" in fields argument. Relations and transforms ' - 'are not allowed in fields.' % LOOKUP_SEP) + "are not allowed in fields." % LOOKUP_SEP + ) - hints = {'instance': self} - db_instance_qs = self.__class__._base_manager.db_manager(using, hints=hints).filter(pk=self.pk) + hints = {"instance": self} + db_instance_qs = self.__class__._base_manager.db_manager( + using, hints=hints + ).filter(pk=self.pk) # Use provided fields, if not set then reload all non-deferred fields. deferred_fields = self.get_deferred_fields() @@ -675,8 +731,11 @@ class Model(metaclass=ModelBase): fields = list(fields) db_instance_qs = db_instance_qs.only(*fields) elif deferred_fields: - fields = [f.attname for f in self._meta.concrete_fields - if f.attname not in deferred_fields] + fields = [ + f.attname + for f in self._meta.concrete_fields + if f.attname not in deferred_fields + ] db_instance_qs = db_instance_qs.only(*fields) db_instance = db_instance_qs.get() @@ -714,8 +773,9 @@ class Model(metaclass=ModelBase): return getattr(self, field_name) return getattr(self, field.attname) - def save(self, force_insert=False, force_update=False, using=None, - update_fields=None): + def save( + self, force_insert=False, force_update=False, using=None, update_fields=None + ): """ Save the current instance. Override this in a subclass if you want to control the saving process. @@ -724,7 +784,7 @@ class Model(metaclass=ModelBase): that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ - self._prepare_related_fields_for_save(operation_name='save') + self._prepare_related_fields_for_save(operation_name="save") using = using or router.db_for_write(self.__class__, instance=self) if force_insert and (force_update or update_fields): @@ -752,9 +812,9 @@ class Model(metaclass=ModelBase): if non_model_fields: raise ValueError( - 'The following fields do not exist in this model, are m2m ' - 'fields, or are non-concrete fields: %s' - % ', '.join(non_model_fields) + "The following fields do not exist in this model, are m2m " + "fields, or are non-concrete fields: %s" + % ", ".join(non_model_fields) ) # If saving to the same database, and this model is deferred, then @@ -762,18 +822,29 @@ class Model(metaclass=ModelBase): elif not force_insert and deferred_fields and using == self._state.db: field_names = set() for field in self._meta.concrete_fields: - if not field.primary_key and not hasattr(field, 'through'): + if not field.primary_key and not hasattr(field, "through"): field_names.add(field.attname) loaded_fields = field_names.difference(deferred_fields) if loaded_fields: update_fields = frozenset(loaded_fields) - self.save_base(using=using, force_insert=force_insert, - force_update=force_update, update_fields=update_fields) + self.save_base( + using=using, + force_insert=force_insert, + force_update=force_update, + update_fields=update_fields, + ) + save.alters_data = True - def save_base(self, raw=False, force_insert=False, - force_update=False, using=None, update_fields=None): + def save_base( + self, + raw=False, + force_insert=False, + force_update=False, + using=None, + update_fields=None, + ): """ Handle the parts of saving which should be done only once per save, yet need to be done in raw saves, too. This includes some sanity @@ -793,7 +864,10 @@ class Model(metaclass=ModelBase): meta = cls._meta if not meta.auto_created: pre_save.send( - sender=origin, instance=self, raw=raw, using=using, + sender=origin, + instance=self, + raw=raw, + using=using, update_fields=update_fields, ) # A transaction isn't needed if one query is issued. @@ -806,8 +880,12 @@ class Model(metaclass=ModelBase): if not raw: parent_inserted = self._save_parents(cls, using, update_fields) updated = self._save_table( - raw, cls, force_insert or parent_inserted, - force_update, using, update_fields, + raw, + cls, + force_insert or parent_inserted, + force_update, + using, + update_fields, ) # Store the database on which the object was saved self._state.db = using @@ -817,8 +895,12 @@ class Model(metaclass=ModelBase): # Signal that the save is complete if not meta.auto_created: post_save.send( - sender=origin, instance=self, created=(not updated), - update_fields=update_fields, raw=raw, using=using, + sender=origin, + instance=self, + created=(not updated), + update_fields=update_fields, + raw=raw, + using=using, ) save_base.alters_data = True @@ -829,12 +911,19 @@ class Model(metaclass=ModelBase): inserted = False for parent, field in meta.parents.items(): # Make sure the link fields are synced between parent and self. - if (field and getattr(self, parent._meta.pk.attname) is None and - getattr(self, field.attname) is not None): + if ( + field + and getattr(self, parent._meta.pk.attname) is None + and getattr(self, field.attname) is not None + ): setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) - parent_inserted = self._save_parents(cls=parent, using=using, update_fields=update_fields) + parent_inserted = self._save_parents( + cls=parent, using=using, update_fields=update_fields + ) updated = self._save_table( - cls=parent, using=using, update_fields=update_fields, + cls=parent, + using=using, + update_fields=update_fields, force_insert=parent_inserted, ) if not updated: @@ -851,8 +940,15 @@ class Model(metaclass=ModelBase): field.delete_cached_value(self) return inserted - def _save_table(self, raw=False, cls=None, force_insert=False, - force_update=False, using=None, update_fields=None): + def _save_table( + self, + raw=False, + cls=None, + force_insert=False, + force_update=False, + using=None, + update_fields=None, + ): """ Do the heavy-lifting involved in saving. Update or insert the data for a single table. @@ -861,8 +957,11 @@ class Model(metaclass=ModelBase): non_pks = [f for f in meta.local_concrete_fields if not f.primary_key] if update_fields: - non_pks = [f for f in non_pks - if f.name in update_fields or f.attname in update_fields] + non_pks = [ + f + for f in non_pks + if f.name in update_fields or f.attname in update_fields + ] pk_val = self._get_pk_val(meta) if pk_val is None: @@ -874,21 +973,28 @@ class Model(metaclass=ModelBase): updated = False # Skip an UPDATE when adding an instance and primary key has a default. if ( - not raw and - not force_insert and - self._state.adding and - meta.pk.default and - meta.pk.default is not NOT_PROVIDED + not raw + and not force_insert + and self._state.adding + and meta.pk.default + and meta.pk.default is not NOT_PROVIDED ): force_insert = True # If possible, try an UPDATE. If that doesn't update anything, do an INSERT. if pk_set and not force_insert: base_qs = cls._base_manager.using(using) - values = [(f, None, (getattr(self, f.attname) if raw else f.pre_save(self, False))) - for f in non_pks] + values = [ + ( + f, + None, + (getattr(self, f.attname) if raw else f.pre_save(self, False)), + ) + for f in non_pks + ] forced_update = update_fields or force_update - updated = self._do_update(base_qs, using, pk_val, values, update_fields, - forced_update) + updated = self._do_update( + base_qs, using, pk_val, values, update_fields, forced_update + ) if force_update and not updated: raise DatabaseError("Forced update did not affect any rows.") if update_fields and not updated: @@ -899,18 +1005,26 @@ class Model(metaclass=ModelBase): # autopopulate the _order field field = meta.order_with_respect_to filter_args = field.get_filter_kwargs_for_object(self) - self._order = cls._base_manager.using(using).filter(**filter_args).aggregate( - _order__max=Coalesce( - ExpressionWrapper(Max('_order') + Value(1), output_field=IntegerField()), - Value(0), - ), - )['_order__max'] + self._order = ( + cls._base_manager.using(using) + .filter(**filter_args) + .aggregate( + _order__max=Coalesce( + ExpressionWrapper( + Max("_order") + Value(1), output_field=IntegerField() + ), + Value(0), + ), + )["_order__max"] + ) fields = meta.local_concrete_fields if not pk_set: fields = [f for f in fields if f is not meta.auto_field] returning_fields = meta.db_returning_fields - results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw) + results = self._do_insert( + cls._base_manager, using, fields, returning_fields, raw + ) if results: for value, field in zip(results[0], returning_fields): setattr(self, field.attname, value) @@ -931,7 +1045,8 @@ class Model(metaclass=ModelBase): return update_fields is not None or filtered.exists() if self._meta.select_on_save and not forced_update: return ( - filtered.exists() and + filtered.exists() + and # It may happen that the object is deleted from the DB right after # this check, causing the subsequent UPDATE to return zero matching # rows. The same result can occur in some rare cases when the @@ -949,8 +1064,11 @@ class Model(metaclass=ModelBase): return the newly created data for the model. """ return manager._insert( - [self], fields=fields, returning_fields=returning_fields, - using=using, raw=raw, + [self], + fields=fields, + returning_fields=returning_fields, + using=using, + raw=raw, ) def _prepare_related_fields_for_save(self, operation_name, fields=None): @@ -986,7 +1104,9 @@ class Model(metaclass=ModelBase): setattr(self, field.attname, obj.pk) # If the relationship's pk/to_field was changed, clear the # cached relationship. - if getattr(obj, field.target_field.attname) != getattr(self, field.attname): + if getattr(obj, field.target_field.attname) != getattr( + self, field.attname + ): field.delete_cached_value(self) def delete(self, using=None, keep_parents=False): @@ -1006,42 +1126,59 @@ class Model(metaclass=ModelBase): value = getattr(self, field.attname) choices_dict = dict(make_hashable(field.flatchoices)) # force_str() to coerce lazy strings. - return force_str(choices_dict.get(make_hashable(value), value), strings_only=True) + return force_str( + choices_dict.get(make_hashable(value), value), strings_only=True + ) def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs): if not self.pk: raise ValueError("get_next/get_previous cannot be used on unsaved objects.") - op = 'gt' if is_next else 'lt' - order = '' if is_next else '-' + op = "gt" if is_next else "lt" + order = "" if is_next else "-" param = getattr(self, field.attname) - q = Q((field.name, param), (f'pk__{op}', self.pk), _connector=Q.AND) - q = Q(q, (f'{field.name}__{op}', param), _connector=Q.OR) - qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by( - '%s%s' % (order, field.name), '%spk' % order + q = Q((field.name, param), (f"pk__{op}", self.pk), _connector=Q.AND) + q = Q(q, (f"{field.name}__{op}", param), _connector=Q.OR) + qs = ( + self.__class__._default_manager.using(self._state.db) + .filter(**kwargs) + .filter(q) + .order_by("%s%s" % (order, field.name), "%spk" % order) ) try: return qs[0] except IndexError: - raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name) + raise self.DoesNotExist( + "%s matching query does not exist." % self.__class__._meta.object_name + ) def _get_next_or_previous_in_order(self, is_next): cachename = "__%s_order_cache" % is_next if not hasattr(self, cachename): - op = 'gt' if is_next else 'lt' - order = '_order' if is_next else '-_order' + op = "gt" if is_next else "lt" + order = "_order" if is_next else "-_order" order_field = self._meta.order_with_respect_to filter_args = order_field.get_filter_kwargs_for_object(self) - obj = self.__class__._default_manager.filter(**filter_args).filter(**{ - '_order__%s' % op: self.__class__._default_manager.values('_order').filter(**{ - self._meta.pk.name: self.pk - }) - }).order_by(order)[:1].get() + obj = ( + self.__class__._default_manager.filter(**filter_args) + .filter( + **{ + "_order__%s" + % op: self.__class__._default_manager.values("_order").filter( + **{self._meta.pk.name: self.pk} + ) + } + ) + .order_by(order)[:1] + .get() + ) setattr(self, cachename, obj) return getattr(self, cachename) def prepare_database_save(self, field): if self.pk is None: - raise ValueError("Unsaved model instance %r cannot be used in an ORM query." % self) + raise ValueError( + "Unsaved model instance %r cannot be used in an ORM query." % self + ) return getattr(self, field.remote_field.get_related_field().attname) def clean(self): @@ -1085,7 +1222,9 @@ class Model(metaclass=ModelBase): constraints = [(self.__class__, self._meta.total_unique_constraints)] for parent_class in self._meta.get_parent_list(): if parent_class._meta.unique_together: - unique_togethers.append((parent_class, parent_class._meta.unique_together)) + unique_togethers.append( + (parent_class, parent_class._meta.unique_together) + ) if parent_class._meta.total_unique_constraints: constraints.append( (parent_class, parent_class._meta.total_unique_constraints) @@ -1120,11 +1259,11 @@ class Model(metaclass=ModelBase): if f.unique: unique_checks.append((model_class, (name,))) if f.unique_for_date and f.unique_for_date not in exclude: - date_checks.append((model_class, 'date', name, f.unique_for_date)) + date_checks.append((model_class, "date", name, f.unique_for_date)) if f.unique_for_year and f.unique_for_year not in exclude: - date_checks.append((model_class, 'year', name, f.unique_for_year)) + date_checks.append((model_class, "year", name, f.unique_for_year)) if f.unique_for_month and f.unique_for_month not in exclude: - date_checks.append((model_class, 'month', name, f.unique_for_month)) + date_checks.append((model_class, "month", name, f.unique_for_month)) return unique_checks, date_checks def _perform_unique_checks(self, unique_checks): @@ -1139,8 +1278,10 @@ class Model(metaclass=ModelBase): f = self._meta.get_field(field_name) lookup_value = getattr(self, f.attname) # TODO: Handle multiple backends with different feature flags. - if (lookup_value is None or - (lookup_value == '' and connection.features.interprets_empty_strings_as_nulls)): + if lookup_value is None or ( + lookup_value == "" + and connection.features.interprets_empty_strings_as_nulls + ): # no value, skip the lookup continue if f.primary_key and not self._state.adding: @@ -1168,7 +1309,9 @@ class Model(metaclass=ModelBase): key = unique_check[0] else: key = NON_FIELD_ERRORS - errors.setdefault(key, []).append(self.unique_error_message(model_class, unique_check)) + errors.setdefault(key, []).append( + self.unique_error_message(model_class, unique_check) + ) return errors @@ -1181,12 +1324,14 @@ class Model(metaclass=ModelBase): date = getattr(self, unique_for) if date is None: continue - if lookup_type == 'date': - lookup_kwargs['%s__day' % unique_for] = date.day - lookup_kwargs['%s__month' % unique_for] = date.month - lookup_kwargs['%s__year' % unique_for] = date.year + if lookup_type == "date": + lookup_kwargs["%s__day" % unique_for] = date.day + lookup_kwargs["%s__month" % unique_for] = date.month + lookup_kwargs["%s__year" % unique_for] = date.year else: - lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(date, lookup_type) + lookup_kwargs["%s__%s" % (unique_for, lookup_type)] = getattr( + date, lookup_type + ) lookup_kwargs[field] = getattr(self, field) qs = model_class._default_manager.filter(**lookup_kwargs) @@ -1205,46 +1350,48 @@ class Model(metaclass=ModelBase): opts = self._meta field = opts.get_field(field_name) return ValidationError( - message=field.error_messages['unique_for_date'], - code='unique_for_date', + message=field.error_messages["unique_for_date"], + code="unique_for_date", params={ - 'model': self, - 'model_name': capfirst(opts.verbose_name), - 'lookup_type': lookup_type, - 'field': field_name, - 'field_label': capfirst(field.verbose_name), - 'date_field': unique_for, - 'date_field_label': capfirst(opts.get_field(unique_for).verbose_name), - } + "model": self, + "model_name": capfirst(opts.verbose_name), + "lookup_type": lookup_type, + "field": field_name, + "field_label": capfirst(field.verbose_name), + "date_field": unique_for, + "date_field_label": capfirst(opts.get_field(unique_for).verbose_name), + }, ) def unique_error_message(self, model_class, unique_check): opts = model_class._meta params = { - 'model': self, - 'model_class': model_class, - 'model_name': capfirst(opts.verbose_name), - 'unique_check': unique_check, + "model": self, + "model_class": model_class, + "model_name": capfirst(opts.verbose_name), + "unique_check": unique_check, } # A unique field if len(unique_check) == 1: field = opts.get_field(unique_check[0]) - params['field_label'] = capfirst(field.verbose_name) + params["field_label"] = capfirst(field.verbose_name) return ValidationError( - message=field.error_messages['unique'], - code='unique', + message=field.error_messages["unique"], + code="unique", params=params, ) # unique_together else: - field_labels = [capfirst(opts.get_field(f).verbose_name) for f in unique_check] - params['field_labels'] = get_text_list(field_labels, _('and')) + field_labels = [ + capfirst(opts.get_field(f).verbose_name) for f in unique_check + ] + params["field_labels"] = get_text_list(field_labels, _("and")) return ValidationError( message=_("%(model_name)s with this %(field_labels)s already exists."), - code='unique_together', + code="unique_together", params=params, ) @@ -1311,9 +1458,13 @@ class Model(metaclass=ModelBase): @classmethod def check(cls, **kwargs): - errors = [*cls._check_swappable(), *cls._check_model(), *cls._check_managers(**kwargs)] + errors = [ + *cls._check_swappable(), + *cls._check_model(), + *cls._check_managers(**kwargs), + ] if not cls._meta.swapped: - databases = kwargs.get('databases') or [] + databases = kwargs.get("databases") or [] errors += [ *cls._check_fields(**kwargs), *cls._check_m2m_through_same_relationship(), @@ -1345,16 +1496,17 @@ class Model(metaclass=ModelBase): @classmethod def _check_default_pk(cls): if ( - not cls._meta.abstract and - cls._meta.pk.auto_created and + not cls._meta.abstract + and cls._meta.pk.auto_created + and # Inherited PKs are checked in parents models. not ( - isinstance(cls._meta.pk, OneToOneField) and - cls._meta.pk.remote_field.parent_link - ) and - not settings.is_overridden('DEFAULT_AUTO_FIELD') and - cls._meta.app_config and - not cls._meta.app_config._is_default_auto_field_overridden + isinstance(cls._meta.pk, OneToOneField) + and cls._meta.pk.remote_field.parent_link + ) + and not settings.is_overridden("DEFAULT_AUTO_FIELD") + and cls._meta.app_config + and not cls._meta.app_config._is_default_auto_field_overridden ): return [ checks.Warning( @@ -1368,7 +1520,7 @@ class Model(metaclass=ModelBase): f"of AutoField, e.g. 'django.db.models.BigAutoField'." ), obj=cls, - id='models.W042', + id="models.W042", ), ] return [] @@ -1383,19 +1535,19 @@ class Model(metaclass=ModelBase): except ValueError: errors.append( checks.Error( - "'%s' is not of the form 'app_label.app_name'." % cls._meta.swappable, - id='models.E001', + "'%s' is not of the form 'app_label.app_name'." + % cls._meta.swappable, + id="models.E001", ) ) except LookupError: - app_label, model_name = cls._meta.swapped.split('.') + app_label, model_name = cls._meta.swapped.split(".") errors.append( checks.Error( "'%s' references '%s.%s', which has not been " - "installed, or is abstract." % ( - cls._meta.swappable, app_label, model_name - ), - id='models.E002', + "installed, or is abstract." + % (cls._meta.swappable, app_label, model_name), + id="models.E002", ) ) return errors @@ -1408,7 +1560,7 @@ class Model(metaclass=ModelBase): errors.append( checks.Error( "Proxy model '%s' contains model fields." % cls.__name__, - id='models.E017', + id="models.E017", ) ) return errors @@ -1433,8 +1585,7 @@ class Model(metaclass=ModelBase): @classmethod def _check_m2m_through_same_relationship(cls): - """ Check if no relationship model is used by more than one m2m field. - """ + """Check if no relationship model is used by more than one m2m field.""" errors = [] seen_intermediary_signatures = [] @@ -1448,15 +1599,20 @@ class Model(metaclass=ModelBase): fields = (f for f in fields if isinstance(f.remote_field.through, ModelBase)) for f in fields: - signature = (f.remote_field.model, cls, f.remote_field.through, f.remote_field.through_fields) + signature = ( + f.remote_field.model, + cls, + f.remote_field.through, + f.remote_field.through_fields, + ) if signature in seen_intermediary_signatures: errors.append( checks.Error( "The model has two identical many-to-many relations " - "through the intermediate model '%s'." % - f.remote_field.through._meta.label, + "through the intermediate model '%s'." + % f.remote_field.through._meta.label, obj=cls, - id='models.E003', + id="models.E003", ) ) else: @@ -1466,15 +1622,17 @@ class Model(metaclass=ModelBase): @classmethod def _check_id_field(cls): """Check if `id` field is a primary key.""" - fields = [f for f in cls._meta.local_fields if f.name == 'id' and f != cls._meta.pk] + fields = [ + f for f in cls._meta.local_fields if f.name == "id" and f != cls._meta.pk + ] # fields is empty or consists of the invalid "id" field - if fields and not fields[0].primary_key and cls._meta.pk.name == 'id': + if fields and not fields[0].primary_key and cls._meta.pk.name == "id": return [ checks.Error( "'id' can only be used as a field name if the field also " "sets 'primary_key=True'.", obj=cls, - id='models.E004', + id="models.E004", ) ] else: @@ -1495,12 +1653,10 @@ class Model(metaclass=ModelBase): checks.Error( "The field '%s' from parent model " "'%s' clashes with the field '%s' " - "from parent model '%s'." % ( - clash.name, clash.model._meta, - f.name, f.model._meta - ), + "from parent model '%s'." + % (clash.name, clash.model._meta, f.name, f.model._meta), obj=cls, - id='models.E005', + id="models.E005", ) ) used_fields[f.name] = f @@ -1520,16 +1676,16 @@ class Model(metaclass=ModelBase): # field "id" and automatically added unique field "id", both # defined at the same model. This special case is considered in # _check_id_field and here we ignore it. - id_conflict = f.name == "id" and clash and clash.name == "id" and clash.model == cls + id_conflict = ( + f.name == "id" and clash and clash.name == "id" and clash.model == cls + ) if clash and not id_conflict: errors.append( checks.Error( "The field '%s' clashes with the field '%s' " - "from model '%s'." % ( - f.name, clash.name, clash.model._meta - ), + "from model '%s'." % (f.name, clash.name, clash.model._meta), obj=f, - id='models.E006', + id="models.E006", ) ) used_fields[f.name] = f @@ -1554,7 +1710,7 @@ class Model(metaclass=ModelBase): "another field." % (f.name, column_name), hint="Specify a 'db_column' for the field.", obj=cls, - id='models.E007' + id="models.E007", ) ) else: @@ -1566,13 +1722,13 @@ class Model(metaclass=ModelBase): def _check_model_name_db_lookup_clashes(cls): errors = [] model_name = cls.__name__ - if model_name.startswith('_') or model_name.endswith('_'): + if model_name.startswith("_") or model_name.endswith("_"): errors.append( checks.Error( "The model name '%s' cannot start or end with an underscore " "as it collides with the query lookup syntax." % model_name, obj=cls, - id='models.E023' + id="models.E023", ) ) elif LOOKUP_SEP in model_name: @@ -1581,7 +1737,7 @@ class Model(metaclass=ModelBase): "The model name '%s' cannot contain double underscores as " "it collides with the query lookup syntax." % model_name, obj=cls, - id='models.E024' + id="models.E024", ) ) return errors @@ -1591,7 +1747,8 @@ class Model(metaclass=ModelBase): errors = [] property_names = cls._meta._property_names related_field_accessors = ( - f.get_attname() for f in cls._meta._get_fields(reverse=False) + f.get_attname() + for f in cls._meta._get_fields(reverse=False) if f.is_relation and f.related_model is not None ) for accessor in related_field_accessors: @@ -1601,7 +1758,7 @@ class Model(metaclass=ModelBase): "The property '%s' clashes with a related field " "accessor." % accessor, obj=cls, - id='models.E025', + id="models.E025", ) ) return errors @@ -1615,7 +1772,7 @@ class Model(metaclass=ModelBase): "The model cannot have more than one field with " "'primary_key=True'.", obj=cls, - id='models.E026', + id="models.E026", ) ) return errors @@ -1628,16 +1785,18 @@ class Model(metaclass=ModelBase): checks.Error( "'index_together' must be a list or tuple.", obj=cls, - id='models.E008', + id="models.E008", ) ] - elif any(not isinstance(fields, (tuple, list)) for fields in cls._meta.index_together): + elif any( + not isinstance(fields, (tuple, list)) for fields in cls._meta.index_together + ): return [ checks.Error( "All 'index_together' elements must be lists or tuples.", obj=cls, - id='models.E009', + id="models.E009", ) ] @@ -1655,16 +1814,19 @@ class Model(metaclass=ModelBase): checks.Error( "'unique_together' must be a list or tuple.", obj=cls, - id='models.E010', + id="models.E010", ) ] - elif any(not isinstance(fields, (tuple, list)) for fields in cls._meta.unique_together): + elif any( + not isinstance(fields, (tuple, list)) + for fields in cls._meta.unique_together + ): return [ checks.Error( "All 'unique_together' elements must be lists or tuples.", obj=cls, - id='models.E011', + id="models.E011", ) ] @@ -1682,13 +1844,13 @@ class Model(metaclass=ModelBase): for index in cls._meta.indexes: # Index name can't start with an underscore or a number, restricted # for cross-database compatibility with Oracle. - if index.name[0] == '_' or index.name[0].isdigit(): + if index.name[0] == "_" or index.name[0].isdigit(): errors.append( checks.Error( "The index name '%s' cannot start with an underscore " "or a number." % index.name, obj=cls, - id='models.E033', + id="models.E033", ), ) if len(index.name) > index.max_name_length: @@ -1697,7 +1859,7 @@ class Model(metaclass=ModelBase): "The index name '%s' cannot be longer than %d " "characters." % (index.name, index.max_name_length), obj=cls, - id='models.E034', + id="models.E034", ), ) if index.contains_expressions: @@ -1710,57 +1872,59 @@ class Model(metaclass=ModelBase): continue connection = connections[db] if not ( - connection.features.supports_partial_indexes or - 'supports_partial_indexes' in cls._meta.required_db_features + connection.features.supports_partial_indexes + or "supports_partial_indexes" in cls._meta.required_db_features ) and any(index.condition is not None for index in cls._meta.indexes): errors.append( checks.Warning( - '%s does not support indexes with conditions.' + "%s does not support indexes with conditions." % connection.display_name, hint=( "Conditions will be ignored. Silence this warning " "if you don't care about it." ), obj=cls, - id='models.W037', + id="models.W037", ) ) if not ( - connection.features.supports_covering_indexes or - 'supports_covering_indexes' in cls._meta.required_db_features + connection.features.supports_covering_indexes + or "supports_covering_indexes" in cls._meta.required_db_features ) and any(index.include for index in cls._meta.indexes): errors.append( checks.Warning( - '%s does not support indexes with non-key columns.' + "%s does not support indexes with non-key columns." % connection.display_name, hint=( "Non-key columns will be ignored. Silence this " "warning if you don't care about it." ), obj=cls, - id='models.W040', + id="models.W040", ) ) if not ( - connection.features.supports_expression_indexes or - 'supports_expression_indexes' in cls._meta.required_db_features + connection.features.supports_expression_indexes + or "supports_expression_indexes" in cls._meta.required_db_features ) and any(index.contains_expressions for index in cls._meta.indexes): errors.append( checks.Warning( - '%s does not support indexes on expressions.' + "%s does not support indexes on expressions." % connection.display_name, hint=( "An index won't be created. Silence this warning " "if you don't care about it." ), obj=cls, - id='models.W043', + id="models.W043", ) ) - fields = [field for index in cls._meta.indexes for field, _ in index.fields_orders] + fields = [ + field for index in cls._meta.indexes for field, _ in index.fields_orders + ] fields += [include for index in cls._meta.indexes for include in index.include] fields += references - errors.extend(cls._check_local_fields(fields, 'indexes')) + errors.extend(cls._check_local_fields(fields, "indexes")) return errors @classmethod @@ -1772,7 +1936,7 @@ class Model(metaclass=ModelBase): forward_fields_map = {} for field in cls._meta._get_fields(reverse=False): forward_fields_map[field.name] = field - if hasattr(field, 'attname'): + if hasattr(field, "attname"): forward_fields_map[field.attname] = field errors = [] @@ -1782,11 +1946,13 @@ class Model(metaclass=ModelBase): except KeyError: errors.append( checks.Error( - "'%s' refers to the nonexistent field '%s'." % ( - option, field_name, + "'%s' refers to the nonexistent field '%s'." + % ( + option, + field_name, ), obj=cls, - id='models.E012', + id="models.E012", ) ) else: @@ -1794,11 +1960,14 @@ class Model(metaclass=ModelBase): errors.append( checks.Error( "'%s' refers to a ManyToManyField '%s', but " - "ManyToManyFields are not permitted in '%s'." % ( - option, field_name, option, + "ManyToManyFields are not permitted in '%s'." + % ( + option, + field_name, + option, ), obj=cls, - id='models.E013', + id="models.E013", ) ) elif field not in cls._meta.local_fields: @@ -1808,7 +1977,7 @@ class Model(metaclass=ModelBase): % (option, field_name, cls._meta.object_name), hint="This issue may be caused by multi-table inheritance.", obj=cls, - id='models.E016', + id="models.E016", ) ) return errors @@ -1824,7 +1993,7 @@ class Model(metaclass=ModelBase): checks.Error( "'ordering' and 'order_with_respect_to' cannot be used together.", obj=cls, - id='models.E021', + id="models.E021", ), ] @@ -1836,7 +2005,7 @@ class Model(metaclass=ModelBase): checks.Error( "'ordering' must be a tuple or list (even if you want to order by only one field).", obj=cls, - id='models.E014', + id="models.E014", ) ] @@ -1844,10 +2013,10 @@ class Model(metaclass=ModelBase): fields = cls._meta.ordering # Skip expressions and '?' fields. - fields = (f for f in fields if isinstance(f, str) and f != '?') + fields = (f for f in fields if isinstance(f, str) and f != "?") # Convert "-field" to "field". - fields = ((f[1:] if f.startswith('-') else f) for f in fields) + fields = ((f[1:] if f.startswith("-") else f) for f in fields) # Separate related fields and non-related fields. _fields = [] @@ -1866,7 +2035,7 @@ class Model(metaclass=ModelBase): for part in field.split(LOOKUP_SEP): try: # pk is an alias that won't be found by opts.get_field. - if part == 'pk': + if part == "pk": fld = _cls._meta.pk else: fld = _cls._meta.get_field(part) @@ -1883,13 +2052,13 @@ class Model(metaclass=ModelBase): "'ordering' refers to the nonexistent field, " "related field, or lookup '%s'." % field, obj=cls, - id='models.E015', + id="models.E015", ) ) # Skip ordering on pk. This is always a valid order_by field # but is an alias and therefore won't be found by opts.get_field. - fields = {f for f in fields if f != 'pk'} + fields = {f for f in fields if f != "pk"} # Check for invalid or nonexistent fields in ordering. invalid_fields = [] @@ -1897,10 +2066,14 @@ class Model(metaclass=ModelBase): # Any field name that is not present in field_names does not exist. # Also, ordering by m2m fields is not allowed. opts = cls._meta - valid_fields = set(chain.from_iterable( - (f.name, f.attname) if not (f.auto_created and not f.concrete) else (f.field.related_query_name(),) - for f in chain(opts.fields, opts.related_objects) - )) + valid_fields = set( + chain.from_iterable( + (f.name, f.attname) + if not (f.auto_created and not f.concrete) + else (f.field.related_query_name(),) + for f in chain(opts.fields, opts.related_objects) + ) + ) invalid_fields.extend(fields - valid_fields) @@ -1910,7 +2083,7 @@ class Model(metaclass=ModelBase): "'ordering' refers to the nonexistent field, related " "field, or lookup '%s'." % invalid_field, obj=cls, - id='models.E015', + id="models.E015", ) ) return errors @@ -1952,7 +2125,11 @@ class Model(metaclass=ModelBase): # Check if auto-generated name for the field is too long # for the database. - if f.db_column is None and column_name is not None and len(column_name) > allowed_len: + if ( + f.db_column is None + and column_name is not None + and len(column_name) > allowed_len + ): errors.append( checks.Error( 'Autogenerated column name too long for field "%s". ' @@ -1960,7 +2137,7 @@ class Model(metaclass=ModelBase): % (column_name, allowed_len, db_alias), hint="Set the column name manually using 'db_column'.", obj=cls, - id='models.E018', + id="models.E018", ) ) @@ -1973,10 +2150,14 @@ class Model(metaclass=ModelBase): # for the database. for m2m in f.remote_field.through._meta.local_fields: _, rel_name = m2m.get_attname_column() - if m2m.db_column is None and rel_name is not None and len(rel_name) > allowed_len: + if ( + m2m.db_column is None + and rel_name is not None + and len(rel_name) > allowed_len + ): errors.append( checks.Error( - 'Autogenerated column name too long for M2M field ' + "Autogenerated column name too long for M2M field " '"%s". Maximum length is "%s" for database "%s".' % (rel_name, allowed_len, db_alias), hint=( @@ -1984,7 +2165,7 @@ class Model(metaclass=ModelBase): "M2M and then set column_name using 'db_column'." ), obj=cls, - id='models.E019', + id="models.E019", ) ) @@ -2002,7 +2183,7 @@ class Model(metaclass=ModelBase): yield from cls._get_expr_references(child) elif isinstance(expr, F): yield tuple(expr.name.split(LOOKUP_SEP)) - elif hasattr(expr, 'get_source_expressions'): + elif hasattr(expr, "get_source_expressions"): for src_expr in expr.get_source_expressions(): yield from cls._get_expr_references(src_expr) @@ -2014,132 +2195,145 @@ class Model(metaclass=ModelBase): continue connection = connections[db] if not ( - connection.features.supports_table_check_constraints or - 'supports_table_check_constraints' in cls._meta.required_db_features + connection.features.supports_table_check_constraints + or "supports_table_check_constraints" in cls._meta.required_db_features ) and any( isinstance(constraint, CheckConstraint) for constraint in cls._meta.constraints ): errors.append( checks.Warning( - '%s does not support check constraints.' % connection.display_name, + "%s does not support check constraints." + % connection.display_name, hint=( "A constraint won't be created. Silence this " "warning if you don't care about it." ), obj=cls, - id='models.W027', + id="models.W027", ) ) if not ( - connection.features.supports_partial_indexes or - 'supports_partial_indexes' in cls._meta.required_db_features + connection.features.supports_partial_indexes + or "supports_partial_indexes" in cls._meta.required_db_features ) and any( - isinstance(constraint, UniqueConstraint) and constraint.condition is not None + isinstance(constraint, UniqueConstraint) + and constraint.condition is not None for constraint in cls._meta.constraints ): errors.append( checks.Warning( - '%s does not support unique constraints with ' - 'conditions.' % connection.display_name, + "%s does not support unique constraints with " + "conditions." % connection.display_name, hint=( "A constraint won't be created. Silence this " "warning if you don't care about it." ), obj=cls, - id='models.W036', + id="models.W036", ) ) if not ( - connection.features.supports_deferrable_unique_constraints or - 'supports_deferrable_unique_constraints' in cls._meta.required_db_features + connection.features.supports_deferrable_unique_constraints + or "supports_deferrable_unique_constraints" + in cls._meta.required_db_features ) and any( - isinstance(constraint, UniqueConstraint) and constraint.deferrable is not None + isinstance(constraint, UniqueConstraint) + and constraint.deferrable is not None for constraint in cls._meta.constraints ): errors.append( checks.Warning( - '%s does not support deferrable unique constraints.' + "%s does not support deferrable unique constraints." % connection.display_name, hint=( "A constraint won't be created. Silence this " "warning if you don't care about it." ), obj=cls, - id='models.W038', + id="models.W038", ) ) if not ( - connection.features.supports_covering_indexes or - 'supports_covering_indexes' in cls._meta.required_db_features + connection.features.supports_covering_indexes + or "supports_covering_indexes" in cls._meta.required_db_features ) and any( isinstance(constraint, UniqueConstraint) and constraint.include for constraint in cls._meta.constraints ): errors.append( checks.Warning( - '%s does not support unique constraints with non-key ' - 'columns.' % connection.display_name, + "%s does not support unique constraints with non-key " + "columns." % connection.display_name, hint=( "A constraint won't be created. Silence this " "warning if you don't care about it." ), obj=cls, - id='models.W039', + id="models.W039", ) ) if not ( - connection.features.supports_expression_indexes or - 'supports_expression_indexes' in cls._meta.required_db_features + connection.features.supports_expression_indexes + or "supports_expression_indexes" in cls._meta.required_db_features ) and any( - isinstance(constraint, UniqueConstraint) and constraint.contains_expressions + isinstance(constraint, UniqueConstraint) + and constraint.contains_expressions for constraint in cls._meta.constraints ): errors.append( checks.Warning( - '%s does not support unique constraints on ' - 'expressions.' % connection.display_name, + "%s does not support unique constraints on " + "expressions." % connection.display_name, hint=( "A constraint won't be created. Silence this " "warning if you don't care about it." ), obj=cls, - id='models.W044', + id="models.W044", ) ) - fields = set(chain.from_iterable( - (*constraint.fields, *constraint.include) - for constraint in cls._meta.constraints if isinstance(constraint, UniqueConstraint) - )) + fields = set( + chain.from_iterable( + (*constraint.fields, *constraint.include) + for constraint in cls._meta.constraints + if isinstance(constraint, UniqueConstraint) + ) + ) references = set() for constraint in cls._meta.constraints: if isinstance(constraint, UniqueConstraint): if ( - connection.features.supports_partial_indexes or - 'supports_partial_indexes' not in cls._meta.required_db_features + connection.features.supports_partial_indexes + or "supports_partial_indexes" + not in cls._meta.required_db_features ) and isinstance(constraint.condition, Q): - references.update(cls._get_expr_references(constraint.condition)) + references.update( + cls._get_expr_references(constraint.condition) + ) if ( - connection.features.supports_expression_indexes or - 'supports_expression_indexes' not in cls._meta.required_db_features + connection.features.supports_expression_indexes + or "supports_expression_indexes" + not in cls._meta.required_db_features ) and constraint.contains_expressions: for expression in constraint.expressions: references.update(cls._get_expr_references(expression)) elif isinstance(constraint, CheckConstraint): if ( - connection.features.supports_table_check_constraints or - 'supports_table_check_constraints' not in cls._meta.required_db_features + connection.features.supports_table_check_constraints + or "supports_table_check_constraints" + not in cls._meta.required_db_features ) and isinstance(constraint.check, Q): references.update(cls._get_expr_references(constraint.check)) for field_name, *lookups in references: # pk is an alias that won't be found by opts.get_field. - if field_name != 'pk': + if field_name != "pk": fields.add(field_name) if not lookups: # If it has no lookups it cannot result in a JOIN. continue try: - if field_name == 'pk': + if field_name == "pk": field = cls._meta.pk else: field = cls._meta.get_field(field_name) @@ -2150,20 +2344,20 @@ class Model(metaclass=ModelBase): # JOIN must happen at the first lookup. first_lookup = lookups[0] if ( - hasattr(field, 'get_transform') and - hasattr(field, 'get_lookup') and - field.get_transform(first_lookup) is None and - field.get_lookup(first_lookup) is None + hasattr(field, "get_transform") + and hasattr(field, "get_lookup") + and field.get_transform(first_lookup) is None + and field.get_lookup(first_lookup) is None ): errors.append( checks.Error( "'constraints' refers to the joined field '%s'." % LOOKUP_SEP.join([field_name] + lookups), obj=cls, - id='models.E041', + id="models.E041", ) ) - errors.extend(cls._check_local_fields(fields, 'constraints')) + errors.extend(cls._check_local_fields(fields, "constraints")) return errors @@ -2173,14 +2367,16 @@ class Model(metaclass=ModelBase): # ORDERING METHODS ######################### + def method_set_order(self, ordered_obj, id_list, using=None): if using is None: using = DEFAULT_DB_ALIAS order_wrt = ordered_obj._meta.order_with_respect_to filter_args = order_wrt.get_forward_related_filter(self) - ordered_obj.objects.db_manager(using).filter(**filter_args).bulk_update([ - ordered_obj(pk=pk, _order=order) for order, pk in enumerate(id_list) - ], ['_order']) + ordered_obj.objects.db_manager(using).filter(**filter_args).bulk_update( + [ordered_obj(pk=pk, _order=order) for order, pk in enumerate(id_list)], + ["_order"], + ) def method_get_order(self, ordered_obj): @@ -2193,15 +2389,16 @@ def method_get_order(self, ordered_obj): def make_foreign_order_accessors(model, related_model): setattr( related_model, - 'get_%s_order' % model.__name__.lower(), - partialmethod(method_get_order, model) + "get_%s_order" % model.__name__.lower(), + partialmethod(method_get_order, model), ) setattr( related_model, - 'set_%s_order' % model.__name__.lower(), - partialmethod(method_set_order, model) + "set_%s_order" % model.__name__.lower(), + partialmethod(method_set_order, model), ) + ######## # MISC # ######## |
