Django: получить первый объект из запроса фильтра или создать
В Django queryset предоставляет метод get_or_create, который возвращает объекты или создает объект.
Однако, как и метод get, get_or_create может генерировать исключение, если запрос возвращает несколько объектов.
Есть ли способ сделать это элегантно:
objects = Model.manager.filter(params)
if len(objects) == 0:
obj = Model.objects.create(params)
else:
obj = objects[0]
Ответы
Ответ 1
get_or_create() - это просто удобная функция, поэтому нет ничего плохого в написании собственного, как показано в павиде, или
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
ИЗМЕНИТЬ
Как было предложено, сменил срез [: 1] (который возвращает список с одним входом) после фильтра на [0] (который возвращает фактический объект). Проблема с этим - это вызовет исключение, если оно не соответствует запросу.
Это также приведет к исключению simliar:
Model.objects.filter(field_lookup=value).latest()
Глядя на вопрос снова, я не уверен, хочет ли исходный плакат возвращать несколько объектов/строк или просто способ обхода исключения при извлечении одного объекта/строки.
Вот еще один вариант?
results = Model.objects.filter(...)
if results.exists():
return results
else:
return Model.objects.create(...)
а другой:
result = None
try:
result = Model.objects.get(...)
except Model.DoesNotExist:
result = Model.objects.create(...)
Нет ничего плохого в том, чтобы поднимать и ловить исключения!
Ответ 2
Из django 1.6 существует метод удобства first(), который возвращает первый результат в запросе фильтра или None.
obj = Model.manager.filter(params).first()
if obj is None:
obj = Model.objects.create(params)
Ответ 3
Я только наткнулся на этот вопрос и хотел внести свой вклад, потому что никто не предложил на самом деле поймать IndexError.
try:
obj = Model.objects.filter(params)[0]
except IndexError:
obj = Model.objects.create(params)
Ответ 4
Здесь используется метод диспетчера, который позволит вам элегантно расширить эту функцию
class FilterOrCreateManager(models.Manager):
"""Adds filter_or_create method to objects
"""
def filter_or_create(self, **kwargs):
try:
created = False
obj = self.filter(**kwargs).first()
if obj is None:
obj = self.create(**kwargs)
created = True
return (obj,created)
except Exception as e:
print(e)
Затем убедитесь, что вы добавили менеджера в зависимости от того, какую модель вы хотите использовать:
class MyObj(models.Model):
objects = FilterOrCreateManager()
После этого вы сможете использовать его, как вы бы get_or_create
:
obj_instance, created = MyObj.filter_or_create(somearg='some value')
Ответ 5
Вы можете попробовать следующее:
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
Ответ 6
Это работает для меня:
На ваш взгляд, вызовите что-то вроде этого:
obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))
В диспетчере моделей создайте такую функцию:
class CategoryManager(models.Manager):
def get_or_create_category(self, query):
try:
result = Category.objects.filter(name = query)[0]
except:
result = Category.objects.create(name = query)
return result
Логика проста. Сначала попробуйте получить первый объект категории, имя которого соответствует строке запроса (которая предоставляется формой). Если сбой поиска (поскольку он не существует), создайте новую категорию со строкой в качестве ее имени. Верните результат для использования в вашем представлении.
Ответ 7
Если я правильно понял, вы хотите получить предмет, или если он не существует, создать.
Итак, вы можете попробовать с ошибкой исключения, но я не уверен, какой из них наиболее эффективен:
try:
Model.objects.filter(params)[0]
except Model.IndexError:
Model.objects.create(params)