Làm cách nào để 'cập nhật số lượng lớn' với Django?


163

Tôi muốn cập nhật bảng với Django - đại loại như thế này trong SQL thô:

update tbl_name set name = 'foo' where name = 'bar'

Kết quả đầu tiên của tôi là một cái gì đó như thế này - nhưng thật khó chịu phải không?

list = ModelClass.objects.filter(name = 'bar')
for obj in list:
    obj.name = 'foo'
    obj.save()

Có cách nào thanh lịch hơn?


1
Bạn có thể đang tìm kiếm chèn hàng loạt. Hãy xem stackoverflow.com/questions/4294088/
Mạnh

Tôi không muốn chèn dữ liệu mới - chỉ cần cập nhật hiện có.
Thomas Schwärzl

3
Có lẽ với sự giúp đỡ của select_for_update? docs.djangoproject.com/en/dev/ref/models/querysets/ Khăn
Jure C.

Điều gì không khó chịu về ModelClasscách tiếp cận? Sau đó, chuyển đến Django dưới dạng: stackoverflow.com/questions/16853649/
Kẻ

Câu trả lời:


256

Cập nhật:

Phiên bản Django 2.2 hiện có số lượng lớn_update .

Câu trả lời cũ:

Tham khảo phần tài liệu django sau đây

Cập nhật nhiều đối tượng cùng một lúc

Nói tóm lại, bạn sẽ có thể sử dụng:

ModelClass.objects.filter(name='bar').update(name="foo")

Bạn cũng có thể sử dụng Fcác đối tượng để làm những việc như tăng hàng:

from django.db.models import F
Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

Xem tài liệu .

Tuy nhiên, lưu ý rằng:

  • ModelClass.savePhương pháp này sẽ không sử dụng (vì vậy nếu bạn có một số logic bên trong thì nó sẽ không được kích hoạt).
  • Không có tín hiệu django sẽ được phát ra.
  • Bạn không thể thực hiện một .update()Truy vấn được cắt lát, nó phải nằm trên Truy vấn gốc để bạn cần dựa vào .filter().exclude()các phương thức.

27
Cũng lưu ý rằng do hậu quả của việc không sử dụng save(), DateTimeFieldcác trường có auto_now=Truecột ("đã sửa đổi") sẽ không được cập nhật.
Arthur

3
Nhưng ModelClass.objects.filter(name = 'bar').update(name="foo")không hoàn thành mục đích cập nhật hàng loạt, nếu tôi có dữ liệu khác nhau cho các id khác nhau, làm thế nào tôi có thể làm điều đó mà không sử dụng vòng lặp?
Shashank

@shihon Tôi không chắc là tôi có hiểu đúng không nhưng tôi đã thêm ví dụ vào câu trả lời.
jb.

@Shashank bạn đã tìm thấy giải pháp nào cho trường hợp của mình chưa? Tôi cũng đang có kịch bản tương tự.
Sourav Prem

Các đối tượng F không thể được sử dụng để tham chiếu các mô hình khác nhau trong phương thức .update ... ví dụ bạn không thể sử dụng Entry.objects.all().update(title=F('blog__title')). Tài liệu có một đề cập nhỏ về điều này. Nếu bạn muốn lấy dữ liệu từ một mô hình khác để cập nhật các mục nhập của mình, bạn sẽ phải chạy một vòng lặp for
sean.hudson

31

Xem xét sử dụng django-bulk-updateđược tìm thấy ở đây trên GitHub .

Tải về: pip install django-bulk-update

Triển khai: (mã được lấy trực tiếp từ tệp ReadMe của dự án)

from bulk_update.helper import bulk_update

random_names = ['Walter', 'The Dude', 'Donny', 'Jesus']
people = Person.objects.all()

for person in people:
    r = random.randrange(4)
    person.name = random_names[r]

bulk_update(people)  # updates all columns using the default db

Cập nhật: Như Marc chỉ ra trong các bình luận, điều này không phù hợp để cập nhật hàng ngàn hàng cùng một lúc. Mặc dù nó phù hợp với các lô nhỏ hơn từ 10 đến 100 giây. Kích thước của lô phù hợp với bạn phụ thuộc vào độ phức tạp của CPU và truy vấn của bạn. Công cụ này giống như một chiếc xe cút kít hơn là một chiếc xe tải tự đổ.


16
Tôi đã thử cập nhật số lượng lớn django và cá nhân tôi không khuyến khích sử dụng nó. Những gì nó làm trong nội bộ là tạo ra một câu lệnh SQL giống như thế này: CẬP NHẬT "trường" bảng "SET" = CASE "id" KHI% s THEN% s KHI% s THEN% s [...] WHERE id in ( % s,% s, [...]);. Điều này hoàn toàn phù hợp với một vài hàng (khi không cần cập nhật số lượng lớn), nhưng với 10.000, truy vấn rất phức tạp, các postgres dành nhiều thời gian hơn với CPU để hiểu 100% truy vấn, hơn là thời gian nó tiết kiệm ghi vào đĩa .
Marc Garcia

1
@MarcGarcia điểm tốt. Tôi thấy nhiều nhà phát triển sử dụng các thư viện bên ngoài mà không biết tác động của họ
Dejell

3
@MarcGarcia Tôi không đồng ý rằng cập nhật hàng loạt không có giá trị và chỉ thực sự cần thiết khi hàng ngàn cập nhật là cần thiết. Sử dụng nó để thực hiện 10.000 hàng cùng một lúc là không nên vì những lý do bạn đã đề cập, nhưng sử dụng nó để cập nhật 50 hàng cùng một lúc sẽ hiệu quả hơn nhiều so với việc nhấn db với 50 yêu cầu cập nhật riêng biệt.
nu everest

3
Các giải pháp tốt nhất tôi tìm thấy là: a) sử dụng trang trí @ giao dịch.atomic, giúp cải thiện hiệu suất bằng cách sử dụng một giao dịch duy nhất hoặc b) thực hiện chèn số lượng lớn trong một bảng tạm thời, sau đó CẬP NHẬT từ bảng tạm thời sang bảng gốc.
Marc Garcia

1
Tôi biết đây là một chủ đề cũ, nhưng thực sự CASE / WHERE không phải là cách duy nhất. Đối với PostgreQuery, có các cách tiếp cận khác, nhưng chúng là DB cụ thể, ví dụ như stackoverflow.com/a/18799497 Tuy nhiên tôi không chắc liệu điều này có khả thi trong ANSI SQL không
Ilian Iliev

21

Phiên bản Django 2.2 hiện có một bulk_updatephương thức ( ghi chú phát hành ).

https://docs.djangoproject.com/en/urdy/ref/models/querysets/#bulk-update

Thí dụ:

# get a pk: record dictionary of existing records
updates = YourModel.objects.filter(...).in_bulk()
....
# do something with the updates dict
....
if hasattr(YourModel.objects, 'bulk_update') and updates:
    # Use the new method
    YourModel.objects.bulk_update(updates.values(), [list the fields to update], batch_size=100)
else:
    # The old & slow way
    with transaction.atomic():
        for obj in updates.values():
            obj.save(update_fields=[list the fields to update])


8

Nếu bạn muốn đặt cùng một giá trị trên một tập hợp các hàng , bạn có thể sử dụng phương thức update () kết hợp với bất kỳ cụm từ truy vấn nào để cập nhật tất cả các hàng trong một truy vấn:

some_list = ModelClass.objects.filter(some condition).values('id')
ModelClass.objects.filter(pk__in=some_list).update(foo=bar)

Nếu bạn muốn cập nhật một tập hợp các hàng với các giá trị khác nhau tùy thuộc vào một số điều kiện, trong trường hợp tốt nhất bạn có thể bó các bản cập nhật theo các giá trị. Giả sử bạn có 1000 hàng nơi bạn muốn đặt một cột thành một trong các giá trị X, sau đó bạn có thể chuẩn bị các lô trước và sau đó chỉ chạy các truy vấn cập nhật X (mỗi cơ bản có dạng ví dụ đầu tiên ở trên) + CHỌN ban đầu -truy vấn.

Nếu mỗi hàng yêu cầu một giá trị duy nhất , không có cách nào để tránh một truy vấn trên mỗi bản cập nhật. Có lẽ nhìn vào các kiến ​​trúc khác như tìm nguồn cung ứng CQRS / Sự kiện nếu bạn cần hiệu suất trong trường hợp sau này.


1

CNTT trả về số lượng đối tượng được cập nhật trong bảng.

update_counts = ModelClass.objects.filter(name='bar').update(name="foo")

Bạn có thể tham khảo liên kết này để có thêm thông tin về cập nhật hàng loạt và tạo. Cập nhật hàng loạt và tạo

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.