Як використовувати словник для оновлення полів у моделях Django?


78

Припустимо, у мене є така модель:

class Book(models.Model):
    num_pages = ...
    author = ...
    date = ...

Чи можу я створити словник, а потім вставити або оновити модель, використовуючи його?

d = {"num_pages":40, author:"Jack", date:"3324"}

2
Так. Спробуй це. Знайдіть **оператор у довідковому посібнику з мови Python. docs.python.org/reference/expressions.html#calls
С.Лотт

Відповіді:


102

Ось приклад створення за допомогою вашого словника d:

Book.objects.create(**d)

Щоб оновити існуючу модель, вам потрібно буде використовувати filterметод QuerySet . Припускаючи, що ви знаєте pkкнигу, яку хочете оновити:

Book.objects.filter(pk=pk).update(**d)

19
Я думаю, оновлення працює лише на QuerySet, а не на одному об’єкті.
Тьєррі Лам

13
Будьте обережні: update()не поважає сигнали. Дивіться відповідь @leech нижче.
Wtower 24.03.17

Це чудово.
joshlsullivan

62

Якщо ви знаєте, що хотіли б створити його:

Book.objects.create(**d)

Припускаючи, що вам потрібно перевірити наявний екземпляр, ви можете знайти його за допомогою get або create:

instance, created = Book.objects.get_or_create(slug=slug, defaults=d)
if not created:
    for attr, value in d.items(): 
        setattr(instance, attr, value)
    instance.save()

Як згадувалося в іншій відповіді, ви також можете використовувати updateфункцію в менеджері наборів запитів, але я вважаю, що це не буде надсилати жодних сигналів (що може не мати для вас значення, якщо ви їх не використовуєте). Однак ви, мабуть, не повинні використовувати його для зміни окремого об'єкта:

Book.objects.filter(id=id).update()

Ваш код створення не призводить до 2 звернень до бази даних? Один для створення, інший для .save()?
Хорхе Лейтао,

2
Так, але не для економії. Якщо його було створено, для пошуку книги (SELECT) буде один, а потім - для оновлення (буде сформовано оператор UPDATE, а не INSERT). Якщо книги не існує, є хто шукати її (ВИБРАТИ) та створити (ВСТАВИТИ).
п'явка

7
that will not send any signals out: не можу наголосити на цьому досить.
Wtower

59

Використовуйте **для створення нової моделі. Перегляньте словник та використовуйте setattr()для оновлення існуючої моделі.

З рамки відпочинку Джанго Тома Крісті

https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/serializers.py

for attr, value in validated_data.items():
    setattr(instance, attr, value)
instance.save()

1
А? Можете навести приклад? Отже, мені довелося б написати власну функцію, яка циклічно переглядає словник?
TIMEX

1
@TIMEX: Будь ласка, прочитайте. docs.python.org/reference/expressions.html#calls дуже зрозуміло, як це працює.
S.Lott

1
Не могли б ви пояснити, чому використання **для оновлення може бути не найкращою ідеєю?
Робін Немет

Використання @Pocin queryset.update(**fields)- це гарна ідея відповідно до django docs. Цитата: "Якщо ви просто оновлюєте запис і вам не потрібно нічого робити з об’єктом моделі, найефективнішим підходом є виклик update (), а не завантаження об’єкта моделі в пам’ять."
CrowbarKZ

2
Гарна відповідь, дзвінок дуже чіткий. Не є шанувальником фільтра (...). Оновлення (...), оскільки заплутування фільтра може призвести до масових перезаписів.
Брайант Джексон,

2

якщо ви вже маєте об'єкт Django і хочете оновити його поле, ви можете зробити це без фільтра. тому що у вас це вже є, у цьому випадку рік тому може:

your_obj.__dict__update(your_dict)
your_obj.save()

Будь ласка, додайте ".": Your_obj .__ dict __. Оновлення (your_dict)
Скотт

0

Додаючи поверх інших відповідей, ось трохи безпечніша версія, щоб запобігти псуванню пов’язаних полів:

def is_simple_editable_field(field):
    return (
            field.editable
            and not field.primary_key
            and not isinstance(field, (ForeignObjectRel, RelatedField))
    )

def update_from_dict(instance, attrs, commit):
    allowed_field_names = {
        f.name for f in instance._meta.get_fields()
        if is_simple_editable_field(f)
    }

    for attr, val in attrs.items():
        if attr in allowed_field_names:
            setattr(instance, attr, val)

    if commit:
        instance.save()

Він перевіряє, чи поле, яке ви намагаєтесь оновити, можна редагувати, не є первинним ключем і не є одним із пов’язаних полів.

Приклад використання:

book = Book.objects.first()
update_from_dict(book, {"num_pages":40, author:"Jack", date:"3324"})

Розкішними серіалізаторами DRF .createі .updateметодами є те, що існує обмежений і перевірений набір полів, що не стосується ручного оновлення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.