Перезагрузить объект django из базы данных

161

Можно ли обновить состояние объекта django из базы данных? Я имею в виду поведение примерно эквивалентно:

new_self = self.__class__.objects.get(pk=self.pk)
for each field of the record:
    setattr(self, field, getattr(new_self, field))

ОБНОВЛЕНИЕ: обнаружен переоткрытый / wontfix войны в трекере: http://code.djangoproject.com/ticket/901 . До сих пор не понимаю, почему сопровождающим это не нравится.

Grep
источник
В обычном контексте SQL это не имеет смысла. Объект базы данных может быть изменен только после того, как ваша транзакция завершится и сделает a commmit. Как только вы это сделаете, вам придется подождать, пока следующая транзакция SQL не будет зафиксирована. Зачем это делать? Как долго вы собираетесь ждать следующей транзакции?
S.Lott
Это кажется ненужной функцией; уже возможно просто повторно найти объект из базы данных.
Стефан
я хотел бы , как хорошо, но она была закрыта несколько раз здесь
eruciform
2
Это не подходит, потому что объекты модели Django являются прокси. Если вы получаете одну и ту же строку таблицы на два объекта - x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), они будут проверяться как равные, но это разные объекты и состояние не является общим. Вы можете изменить оба независимо и сохранить их - последний сохраненный определяет состояние строки в базе данных. Поэтому корректно перезагрузить с простым присваиванием - x1 = X.objects.get (id = 1). Наличие метода перезагрузки привело бы к тому, что многие люди ошибочно поняли, что x1.f = 'new value'; (x1.f == x2.f) верно.
Пол Уипп

Ответы:

260

Начиная с Django 1.8, встроены объекты обновления. Ссылка на документацию .

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F('val') + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)
Тим Флетчер
источник
@ fcracker79 Да, это было реализовано только в 1.8. Для более ранних версий Django лучше всего использовать один из других ответов.
Тим Флетчер
1
Не уверены, что означает "Все неотложенные поля обновлены", упомянутые в документе?
Юнти
1
@Yunti Вы можете отложить поля или явно запросить только подмножество полей, и полученный объект будет заполнен только частично. refresh_from_dbбудет обновлять только такие уже заполненные поля.
301_Moved_Permanently
Не удалось найти детали в документах, но он правильно выдает DoesNotExistисключение, если базовый объект был удален при вызове refresh_from_db. FYI.
Тим Тисдалл
28

Я обнаружил, что сравнительно легко перезагрузить объект из базы данных примерно так:

x = X.objects.get(id=x.id)
Рори
источник
19
Да, но ... после этого вы должны обновить все ссылки на этот объект. Не очень удобно и подвержено ошибкам.
grep
2
Считая, что это необходимо, когда Celery обновил мой объект в БД вне django, django, очевидно, хранил кеш объекта, так как не знал, что он изменился.
Боб Сприн
3
из django.db.models.loading import get_model; instance = get_model (instance) .objects.get (pk = instance.pk)
Эрик,
1
@grep просто потерял 2 часа на написание теста для этого варианта использования: 1: инициализировать модель; 2: обновить модель через форму; 3: Проверьте, что новое значение обновлено .... Так что да, подвержен ошибкам.
vlad-ardelean
3
Я думаю, что refresh_from_dbрешает все эти проблемы.
Flimm
16

Что касается комментария @ grep, то нельзя ли это сделать:

# Put this on your base model (or monkey patch it onto django's Model if that's your thing)
def reload(self):
    new_self = self.__class__.objects.get(pk=self.pk)
    # You may want to clear out the old dict first or perform a selective merge
    self.__dict__.update(new_self.__dict__)

# Use it like this
bar.foo = foo
assert bar.foo.pk is None
foo.save()
foo.reload()
assert bar.foo is foo and bar.foo.pk is not None
Eloff
источник
Спасибо за решение. Если бы только ТАК допускалось несколько голосов "за"!
user590028
11
Джанго теперь предоставляет refresh_from_dbметод.
Flimm
9

Как отметил @Flimm, это действительно потрясающее решение:

foo.refresh_from_db()

Это перезагрузит все данные из базы данных в объект.

Рон
источник