Muat ulang objek Django dari database

161

Apakah mungkin untuk menyegarkan keadaan objek Django dari database? Maksud saya perilaku kira-kira setara dengan:

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

UPDATE: Menemukan perang membuka kembali / memperbaiki wont di pelacak: http://code.djangoproject.com/ticket/901 . Masih tidak mengerti mengapa pengelola tidak menyukai ini.

grep
sumber
Dalam konteks SQL biasa, ini tidak masuk akal. Objek basis data hanya dapat diubah setelah transaksi Anda selesai dan melakukan a commmit. Setelah Anda selesai melakukannya, Anda harus menunggu untuk melakukan transaksi SQL berikutnya. Kenapa melakukan itu? Berapa lama Anda akan menunggu transaksi selanjutnya?
S.Lott
Ini sepertinya fungsi yang tidak perlu; sudah dimungkinkan untuk hanya melihat kembali objek dari database.
Stephan
Saya ingin ini juga, tetapi telah ditutup berulang kali di sini
eruciform
2
Itu tidak tepat karena objek model Django adalah proxy. Jika Anda mendapatkan baris tabel yang sama menjadi dua objek - x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), mereka akan menguji sama tetapi mereka adalah objek yang berbeda dan negara tidak dibagikan. Anda dapat mengubah keduanya secara mandiri dan menyimpannya - yang terakhir disimpan menentukan status baris dalam database. Oleh karena itu benar untuk memuat ulang dengan tugas sederhana - x1 = X.objects.get (id = 1). Memiliki metode memuat ulang akan menyebabkan banyak orang salah menyimpulkan bahwa x1.f = 'nilai baru'; (x1.f == x2.f) Benar.
Paul Whipp

Jawaban:

260

Pada Django 1.8 objek yang menyegarkan dibangun. Tautan ke dokumen .

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)
Tim Fletcher
sumber
@ fcracker79 Ya, itu hanya diterapkan pada 1.8. Untuk versi Django sebelumnya, Anda sebaiknya menggunakan salah satu jawaban lain.
Tim Fletcher
1
Tidak yakin apa artinya "Semua bidang yang tidak ditangguhkan diperbarui" yang disebutkan dalam dokumen?
Yunti
1
@Yunti Anda dapat menunda bidang, atau secara eksplisit meminta hanya sebagian bidang dan objek yang dihasilkan hanya akan terisi sebagian. refresh_from_dbhanya akan memperbarui bidang yang sudah diisi tersebut.
301_Moved_Permanently
Tidak dapat menemukan detail di dokumen, tetapi DoesNotExistpengecualian dengan benar melempar jika objek yang mendasarinya dihapus saat memanggil refresh_from_db. FYI.
Tim Tisdall
28

Saya merasa relatif mudah untuk memuat ulang objek dari database seperti:

x = X.objects.get(id=x.id)
Rory
sumber
19
Ya, tapi ... setelah itu Anda harus memperbarui semua referensi ke objek ini. Tidak terlalu berguna dan rawan kesalahan.
grep
2
Menemukan ini perlu ketika Celery memperbarui objek saya di db di luar Django, Django tampaknya menyimpan cache objek karena tidak tahu itu telah berubah.
Bob Spryn
3
dari django.db.models.loading get_model import; instance = get_model (instance) .objects.get (pk = instance.pk)
Erik
1
@ grep baru saja kehilangan 2 jam menulis tes untuk kasus penggunaan ini: 1: Inisialisasi model; 2: Perbarui Model melalui Formulir; 3: Uji bahwa nilai baru diperbarui .... Jadi ya, rawan kesalahan.
vlad-ardelean
3
Saya pikir refresh_from_dbmenyelesaikan semua masalah ini.
Flimm
16

Mengacu pada komentar @ grep, seharusnya tidak mungkin dilakukan:

# 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
sumber
Terima kasih atas solusinya. Kalau saja SO diperbolehkan beberapa suara-up!
user590028
11
Django sekarang menyediakan refresh_from_dbmetode.
Flimm
9

Sebagaimana @Flimm tunjukkan, ini adalah solusi yang sangat hebat:

foo.refresh_from_db()

Ini memuat ulang semua data dari database ke objek.

Ron
sumber