Tải lại đối tượng django từ cơ sở dữ liệu


159

Có thể làm mới trạng thái của một đối tượng django từ cơ sở dữ liệu? Ý tôi là hành vi gần tương đương với:

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

CẬP NHẬT: Đã tìm thấy một cuộc chiến mở lại / wontfix trong trình theo dõi: http://code.djangoproject.com/ticket/901 . Vẫn không hiểu tại sao những người bảo trì không thích điều này.


Trong ngữ cảnh SQL thông thường, điều này không có ý nghĩa. Đối tượng cơ sở dữ liệu chỉ có thể được thay đổi sau khi giao dịch của bạn kết thúc và thực hiện a commmit. Khi bạn đã thực hiện điều đó, bạn sẽ phải chờ giao dịch SQL tiếp theo để thực hiện. Tại sao làm điều đó? Bao lâu bạn sẽ chờ đợi cho giao dịch tiếp theo?
S.Lott

Đây dường như là một chức năng không cần thiết; có thể chỉ cần tra cứu lại đối tượng từ cơ sở dữ liệu.
Stephan

tôi cũng muốn điều này, nhưng nó đã bị đóng cửa liên tục ở đây
eruciform

2
Nó không thích hợp vì các đối tượng mô hình Django là proxy. Nếu bạn nhận được cùng một hàng của bảng thành hai đối tượng - x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), họ sẽ kiểm tra như nhau nhưng chúng là các đối tượng khác nhau và trạng thái không được chia sẻ. Bạn có thể thay đổi cả hai độc lập và lưu chúng - lần lưu cuối cùng xác định trạng thái của hàng trong cơ sở dữ liệu. Do đó, chính xác là tải lại với phép gán đơn giản - x1 = X.objects.get (id = 1). Có một phương thức tải lại sẽ dẫn đến nhiều người suy luận sai rằng x1.f = 'giá trị mới'; (x1.f == x2.f) là Đúng.
Paul Whipp

Câu trả lời:


258

Kể từ Django 1.8, các đối tượng làm mới được tích hợp. Liên kết đến tài liệu .

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 Vâng, nó chỉ được thực hiện trong 1.8. Đối với các phiên bản trước của Django, tốt nhất bạn nên sử dụng một trong những câu trả lời khác.
Tim Fletcher

1
Không chắc chắn "Tất cả các trường không hoãn được cập nhật" được đề cập trong tài liệu nghĩa là gì?
Yunti

1
@Yunti Bạn có thể trì hoãn các trường hoặc chỉ yêu cầu một tập hợp con các trường và đối tượng kết quả sẽ chỉ được điền một phần. refresh_from_dbsẽ chỉ cập nhật các trường đã được điền.
301_Mond_P vĩnh viễn

Không thể tìm thấy chi tiết trong tài liệu, nhưng nó sẽ ném DoesNotExistngoại lệ một cách chính xác nếu đối tượng cơ bản bị xóa khi gọi refresh_from_db. FYI.
Tim Tonomall

28

Tôi đã thấy tương đối dễ dàng để tải lại đối tượng từ cơ sở dữ liệu như vậy:

x = X.objects.get(id=x.id)

19
Có, nhưng ... sau đó bạn phải cập nhật tất cả các tham chiếu đến đối tượng này. Không tiện dụng và dễ bị lỗi.
grep

2
Nhận thấy điều này là cần thiết khi Celery cập nhật đối tượng của tôi trong db bên ngoài django, django rõ ràng đã giữ một bộ đệm của đối tượng vì nó không biết nó đã thay đổi.
Bob Spryn

3
từ django.db.models.loading nhập get_model; dụ = get_model (dụ) .objects.get (pk = instance.pk)
Erik

1
@grep chỉ mất 2 giờ để viết bài kiểm tra cho trường hợp sử dụng này: 1: Khởi tạo mô hình; 2: Cập nhật Mô hình thông qua Biểu mẫu; 3: Kiểm tra xem giá trị mới có được cập nhật không .... Vì vậy, dễ bị lỗi.
vlad-ardelean

3
Tôi nghĩ rằng refresh_from_dbgiải quyết tất cả những vấn đề này.
Flimm

16

Để tham khảo bình luận của @ grep, không nên làm như sau:

# 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

Cảm ơn giải pháp. Nếu chỉ SO cho phép nhiều phiếu bầu!
dùng590028

11
Django hiện cung cấp refresh_from_dbphương pháp.
Flimm

9

Như @Flimm đã chỉ ra, đây là một giải pháp thực sự tuyệt vời:

foo.refresh_from_db()

Điều này tải lại tất cả dữ liệu từ cơ sở dữ liệu vào đối tượng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.