Есть ли идиоматический способ получить или создать, а затем обновить объект в Django?
У меня есть модель Django под названием StaffSettings, которая содержит различные параметры конфигурации для пользователей в моем приложении Django. Каждый пользователь имеет не более одной записи в таблице StaffSettings.
Предположим, что один параметр-default_year_level, и у меня есть код для моих пользовательских объектов, таких как:
def set_default_year_level(u, year_level):
obj, _created = StaffSettings.objects.get_or_create(user=u)
obj.default_year_level = year_level
obj.save()
Я бы предпочел, чтобы тело функции помещалось в одну строку, потому что это похоже на общий случай использования, но если бы я определил его как
def set_default_year_level(u, year_level):
StaffSettings.objects.filter(user=u).update(default_year_level=year_level)
Который прекрасно работает, если пользователь, о котором идет речь, уже имеет строка в таблице StaffSettings, но она не создаст соответствующую строку, если она не существует.
Каков идиоматический / лучший способ кодирования этого? (например, существует ли какая-то функция filter_or_create
? Или другие люди пишут декораторы / вспомогательные функции для обработки этой идиомы?)
2 ответа:
Я не вижу никаких проблем с вашей первой функцией, я бы написал то же самое для этого usecase.
Однако если вам нужна одна и та же функция на большом количестве полей в вашей модели, и вы не хотите повторяться, вы можете передать поле в качестве параметра:
def set_default_value(u, field, value): obj, _created = StaffSettings.objects.get_or_create(user=u) setattr(obj, field, value) obj.save()
И я буду держаться подальше от функции update(), так как эта функция предназначена для обновления нескольких объектов одновременно и не вызывает метод save() и сигналы на ваших моделях (см. https://docs.djangoproject.com/en/dev/topics/db/queries/#updating-multiple-objects-at-once)
Начиная с Django 1.7 вы можете использовать
update_or_create()
:def set_default_year_level(u, year_level): obj, _created = StaffSettings.objects.update_or_create( user=u, default_year_level=year_level )
Или, для более общего случая, как объяснено в предыдущем ответе:
def set_default_values(u, **kwargs): obj, _created = StaffSettings.objects.update_or_create(user=u, defaults=kwargs)
Который также достигает вашего дополнительного требования
Я бы предпочел, чтобы тело функции помещалось в одну строку