Удаление экземпляра перед сохранением нового (метод сохранения модели Django)
Я хочу ограничить количество записей в моей модели максимум 10 строками для каждого пользователя (модель имеет внешний ключ к модели пользователя). Какова наилучшая практика для этого?
Я думал переопределить метод сохранения, выполнить проверку и удалить перед сохранением:
def save(self, *args, **kwargs):
if MyModel.objects.filter(user=self.user).count() >= 10:
oldest_record = MyModel.objects.filter(user=self.user).order_by('created_at').first()
oldest_record.delete()
super().save(*args, **kwargs)
Но мне интересно, является ли это хорошим решением, лучше ли (и как это сделать) обеспечить это на уровне базы данных, и возможны ли проблемы, когда один и тот же пользователь сохраняет два экземпляра одновременно (хотя это очень маловероятно). Есть ли опыт использования чего-то подобного?
Вы можете повысить эффективность, используя user_id
вместо user
, так как это предотвращает выборку пользователя.
Мы также можем получить все элементы за одну выборку и таким образом предотвратить некоторые условия гонки:
def save(self, *args, **kwargs):
if instance._state.adding:
items = (
MyModel.objects.filter(user_id=self.user_id)
.order_by('created_at')
.only('pk')[9:]
)
if items:
MyModel.objects.filter(pk__in={item.pk for item in items}).delete()
super().save(*args, **kwargs)
Но переопределение .save()
методов, сигналов и т.д. можно обойти с помощью массовых операций (например, bulk create).
Если лучше (и как это сделать) обеспечить это на уровне базы данных.
Вы можете обеспечить это на уровне базы данных с помощью UniqueConstraint
, которая делает комбинацию user_id
и первичного ключа уникальной по модулю десять. В этом случае логика переходит к выбору хорошего первичного ключа.
Это означает, что для пользователя мы можем назначить ему, например, предметы с первичными ключами 51
, 26
, 32
и т.д., но не 31
, поскольку …1
уже "выбран".
Возможно, лучше периодически запускать команду управления для очистки старых записей, а на переднем конце всегда ограничивать количество MyModel
для пользователя десятью. Это, вероятно, достаточно надежно.