Làm cách nào để sử dụng từ điển để cập nhật các trường trong mô hình Django?


78

Giả sử tôi có một mô hình như thế này:

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

Tôi có thể tạo từ điển rồi chèn hoặc cập nhật mô hình bằng cách sử dụng nó không?

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

2
Đúng. Thử nó. Tra cứu **toán tử trong sổ tay tham chiếu ngôn ngữ Python. docs.python.org/reference/expressions.html#calls
S.Lott

có thể trùng lặp của Cập nhật mô hình django qua kwargs
S. Lott

Câu trả lời:


102

Đây là một ví dụ về tạo bằng từ điển của bạn d:

Book.objects.create(**d)

Để cập nhật một mô hình hiện có, bạn sẽ cần sử dụng filterphương thức QuerySet . Giả sử bạn biết pkSách bạn muốn cập nhật:

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

19
Tôi nghĩ rằng cập nhật chỉ hoạt động trên QuerySet chứ không phải trên một đối tượng duy nhất.
Thierry Lam,

13
Hãy cẩn thận: update()không tôn trọng tín hiệu. Xem câu trả lời của @leech bên dưới.
Wtower

Điều này là tuyệt vời.
joshlsullivan

62

Nếu bạn biết bạn muốn tạo nó:

Book.objects.create(**d)

Giả sử bạn cần kiểm tra một phiên bản hiện có, bạn có thể tìm thấy nó bằng get hoặc 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()

Như đã đề cập trong một câu trả lời khác, bạn cũng có thể sử dụng updatechức năng trên trình quản lý bộ truy vấn, nhưng tôi tin rằng điều đó sẽ không gửi bất kỳ tín hiệu nào ra ngoài (điều này có thể không quan trọng với bạn nếu bạn không sử dụng chúng). Tuy nhiên, bạn có thể không nên sử dụng nó để thay đổi một đối tượng:

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

Không phải mã tạo của bạn dẫn đến 2 lần truy cập cơ sở dữ liệu? Một cái để tạo, cái kia cho .save()?
Jorge Leitao

2
Có, nhưng không phải để tiết kiệm. Nếu nó đã được tạo, sẽ có một cái để tra cứu Sách (CHỌN), sau đó một cái khác để cập nhật nó (câu lệnh UPDATE not INSERT sẽ được tạo). Nếu sách không tồn tại, có một cái để tra cứu nó (CHỌN) và tạo nó (CHÈN).
leech

7
that will not send any signals out: không thể nhấn mạnh điều này đủ.
Wtower

58

Sử dụng **để tạo một mô hình mới. Lặp lại từ điển và sử dụng setattr()để cập nhật mô hình hiện có.

Từ Khung phần còn lại Django của Tom Christie

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
Huh? Bạn có thể đưa ra một ví dụ không? Vì vậy, tôi sẽ phải viết một hàm tùy chỉnh lặp qua từ điển?
TIMEX

1
@TIMEX: Vui lòng đọc. docs.python.org/reference/expressions.html#calls rất rõ ràng về cách hoạt động của điều này.
S.Lott

1
Bạn có thể giải thích tại sao việc sử dụng **để cập nhật có thể không phải là một ý kiến ​​hay?
Robin Nemeth

Sử dụng @Pocin queryset.update(**fields)là một ý kiến ​​hay theo tài liệu django. Trích dẫn: "Nếu bạn chỉ cập nhật một bản ghi và không cần làm gì với đối tượng mô hình, thì cách tiếp cận hiệu quả nhất là gọi update (), thay vì tải đối tượng mô hình vào bộ nhớ."
CrowbarKZ,

2
Câu trả lời tốt, cuộc gọi rất rõ ràng. Không phải là người yêu thích bộ lọc (...). Update (...) vì việc bung bộ lọc có thể dẫn đến ghi đè hàng loạt.
Bryant Jackson

1

nếu bạn đã có đối tượng Django và bạn muốn cập nhật trường của nó, bạn có thể làm điều đó mà không cần bộ lọc. bởi vì bạn đã có nó rồi, trong trường hợp này, bạn có thể:

your_obj.__dict__update(your_dict)
your_obj.save()

0

Thêm vào đầu các câu trả lời khác, đây là phiên bản an toàn hơn một chút để tránh làm rối các trường liên quan:

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()

Nó kiểm tra, trường bạn đang cố cập nhật có thể chỉnh sửa được, không phải là khóa chính và không phải là một trong các trường liên quan.

Ví dụ sử dụng:

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

Các bộ tuần tự .create.updatephương pháp DRF sang trọng có là có một bộ trường giới hạn và được xác thực, đây không phải là trường hợp cập nhật thủ cô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.