Ticket #16385: 2885579c6f8a2f41422788b0abdee3da816144f8.diff

File 2885579c6f8a2f41422788b0abdee3da816144f8.diff, 6.6 KB (added by jonash, 4 years ago)

Adds a possibility to disable exists() checks in save().

  • django/db/backends/__init__.py

    diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
    index 01144eb..2e24f73 100644
    a b class BaseDatabaseFeatures(object): 
    281281    allow_sliced_subqueries = True
    282282
    283283    supports_joins = True
     284    distinguishes_insert_from_update = True
    284285    supports_select_related = True
    285286
    286287    # Does the default test database allow multiple connections?
  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    index 286f9b0..c0238b6 100644
    a b class Model(object): 
    273273    _deferred = False
    274274
    275275    def __init__(self, *args, **kwargs):
     276        self._entity_exists = kwargs.pop('__entity_exists', False)
    276277        signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
    277278
    278279        # Set up the storage for instance state
    class Model(object): 
    362363                    pass
    363364            if kwargs:
    364365                raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0])
     366        self._original_pk = self.pk if self._meta.pk is not None else None
    365367        super(Model, self).__init__()
    366368        signals.post_init.send(sender=self.__class__, instance=self)
    367369
    class Model(object): 
    470472        ('raw', 'cls', and 'origin').
    471473        """
    472474        using = using or router.db_for_write(self.__class__, instance=self)
     475        entity_exists = bool(self._entity_exists and self._original_pk == self.pk)
    473476        connection = connections[using]
    474477        assert not (force_insert and force_update)
    475478        if cls is None:
    class Model(object): 
    516519            pk_set = pk_val is not None
    517520            record_exists = True
    518521            manager = cls._base_manager
    519             if pk_set:
     522            # TODO/NONREL: Some backends could emulate force_insert/_update
     523            # with an optimistic transaction, but since it's costly we should
     524            # only do it when the user explicitly wants it.
     525            # By adding support for an optimistic locking transaction
     526            # in Django (SQL: SELECT ... FOR UPDATE) we could even make that
     527            # part fully reusable on all backends (the current .exists()
     528            # check below isn't really safe if you have lots of concurrent
     529            # requests. BTW, and neither is QuerySet.get_or_create).
     530            try_update = connection.features.distinguishes_insert_from_update
     531            if not try_update:
     532                record_exists = False
     533
     534            if try_update and pk_set:
    520535                # Determine whether a record with the primary key already exists.
    521536                if (force_update or (not force_insert and
    522537                        manager.using(using).filter(pk=pk_val).exists())):
    class Model(object): 
    536551                    order_value = manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count()
    537552                    self._order = order_value
    538553
     554                if connection.features.distinguishes_insert_from_update:
     555                    add = True
     556                else:
     557                    add = not entity_exists
     558
    539559                if not pk_set:
    540560                    if force_update:
    541561                        raise ValueError("Cannot force an update in save() with no primary key.")
    542                     values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection))
     562                    values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, add), connection=connection))
    543563                        for f in meta.local_fields if not isinstance(f, AutoField)]
    544564                else:
    545                     values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection))
     565                    values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, add), connection=connection))
    546566                        for f in meta.local_fields]
    547567
    548568                record_exists = False
    class Model(object): 
    566586
    567587        # Signal that the save is complete
    568588        if origin and not meta.auto_created:
     589            if connection.features.distinguishes_insert_from_update:
     590                created = not record_exists
     591            else:
     592                created = not entity_exists
    569593            signals.post_save.send(sender=origin, instance=self,
    570                 created=(not record_exists), raw=raw, using=using)
     594                created=created, raw=raw, using=using)
     595
     596        self._entity_exists = True
     597        self._original_pk = self.pk
    571598
    572599
    573600    save_base.alters_data = True
    class Model(object): 
    580607        collector.collect([self])
    581608        collector.delete()
    582609
     610        self._entity_exists = False
     611        self._original_pk = None
     612
    583613    delete.alters_data = True
    584614
    585615    def _get_FIELD_display(self, field):
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index 26402ba..7a9bb45 100644
    a b class QuerySet(object): 
    282282                if skip:
    283283                    row_data = row[index_start:aggregate_start]
    284284                    pk_val = row_data[pk_idx]
    285                     obj = model_cls(**dict(zip(init_list, row_data)))
     285                    obj = model_cls(**dict(zip(init_list, row_data), __entity_exists=True))
    286286                else:
    287287                    # Omit aggregates in object creation.
    288                     obj = model(*row[index_start:aggregate_start])
     288                    obj = model(*row[index_start:aggregate_start], **{'__entity_exists': True})
    289289
    290290                # Store the source database of the object
    291291                obj._state.db = db
    def get_cached_row(klass, row, index_start, using, max_depth=0, cur_depth=0, 
    11971197            obj = None
    11981198        elif skip:
    11991199            klass = deferred_class_factory(klass, skip)
    1200             obj = klass(**dict(zip(init_list, fields)))
     1200            obj = klass(__entity_exists=True, **dict(zip(init_list, fields)))
    12011201        else:
    1202             obj = klass(*fields)
     1202            obj = klass(*fields, **{'__entity_exists': True})
    12031203
    12041204    else:
    12051205        # Load all fields on klass
    def get_cached_row(klass, row, index_start, using, max_depth=0, cur_depth=0, 
    12151215        if fields == (None,) * field_count:
    12161216            obj = None
    12171217        else:
    1218             obj = klass(**dict(zip(field_names, fields)))
     1218            obj = klass(__entity_exists=True, **dict(zip(field_names, fields)))
    12191219
    12201220    # If an object was retrieved, set the database state.
    12211221    if obj:
Back to Top