Перезагрузить объект 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 . До сих пор не понимаю, почему сопровождающим это не нравится.


В обычном контексте 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

Спасибо за решение. Если бы только ТАК допускалось несколько голосов "за"!
— user590028

11
Джанго теперь предоставляет refresh_from_dbметод.
— Flimm

9

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

foo.refresh_from_db()

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.