I have a model that looks like this:
class CharacterSheetNode(models.Model):
parent = models.ForeignKey('self', related_name='items', null=True, on_delete=models.CASCADE)
# other fields
class Meta:
order_with_respect_to = 'parent'
I have 30M+ instances of this model in my database. My problem is that when I create a new instance of it, the following Django code is ran:
site-packages\django\db\models\base.py, line 876:
if meta.order_with_respect_to:
# If this is a model with an order_with_respect_to
# 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']
This piece of code will go through all the CharacterSheetNode
with no parent (there are probably 1M of them), and look for the Max('_order')
value. As if they were all children of the same parent āNoneā.
The query itself takes 7 seconds to run.
How can I avoid running this code if the parent
is null? Or at least tell Django to only set the order_value if there is a parent?
The whole method from which these lines were extracted:
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.
"""
meta = cls._meta
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]
pk_val = self._get_pk_val(meta)
if pk_val is None:
pk_val = meta.pk.get_pk_value_on_save(self)
setattr(self, meta.pk.attname, pk_val)
pk_set = pk_val is not None
if not pk_set and (force_update or update_fields):
raise ValueError("Cannot force an update in save() with no primary key.")
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
):
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]
forced_update = update_fields or force_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:
raise DatabaseError("Save with update_fields did not affect any rows.")
if not updated:
if meta.order_with_respect_to:
# If this is a model with an order_with_respect_to
# 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']
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)
if results:
for value, field in zip(results[0], returning_fields):
setattr(self, field.attname, value)
return updated
I am using Django 3.1