Django-DB-Migrations: không thể ALTER TABLE vì nó có các sự kiện kích hoạt đang chờ xử lý


121

Tôi muốn xóa null = True khỏi TextField:

-    footer=models.TextField(null=True, blank=True)
+    footer=models.TextField(blank=True, default='')

Tôi đã tạo một chuyển đổi giản đồ:

manage.py schemamigration fooapp --auto

Vì một số cột chân trang chứa NULLnên tôi nhận được điều này errornếu tôi chạy quá trình di chuyển:

django.db.utils.IntegrityError: cột "footer" chứa giá trị rỗng

Tôi đã thêm điều này vào di chuyển giản đồ:

    for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
        sender.footer=''
        sender.save()

Bây giờ tôi nhận được:

django.db.utils.DatabaseError: cannot ALTER TABLE "fooapp_emailsender" because it has pending trigger events

Chuyện gì thế?


1
Câu hỏi này tương tự: stackoverflow.com/questions/28429933/… và có câu trả lời hữu ích hơn cho tôi.
SpoonMeiser

Câu trả lời:


138

Một lý do khác cho điều này có thể là do bạn cố gắng đặt một cột thành NOT NULLkhi nó thực sự đã có NULLcác giá trị.


7
Đến địa chỉ này bạn có thể sử dụng một di chuyển dữ liệu hoặc bằng tay (vỏ manage.py) đi vào và cập nhật các giá trị không tuân thủ
mgojohn

@mgojohn Làm thế nào để bạn làm điều đó?
kim tự tháp

1
@pyramidface Nếu bạn không quá cầu kỳ, bạn chỉ có thể cập nhật các giá trị null tại django shell. Nếu bạn đang tìm kiếm thứ gì đó chính thức hơn và có thể kiểm tra được, điều đó phụ thuộc vào phiên bản bạn đang sử dụng. Nếu bạn sử dụng south, hãy xem: south.readthedocs.org/en/latest/tutorial/part3.html và nếu bạn sử dụng di chuyển django, hãy xem phần "di chuyển dữ liệu" tại đây: docs.djangoproject.com/en/1.8/topics/ di cư
mgojohn

131

Mọi di chuyển đều nằm trong một giao dịch. Trong PostgreSQL, bạn không được cập nhật bảng và sau đó thay đổi lược đồ bảng trong một giao dịch.

Bạn cần phải tách di chuyển dữ liệu và di chuyển giản đồ. Trước tiên, hãy tạo di chuyển dữ liệu với mã này:

 for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
    sender.footer=''
    sender.save()

Sau đó, tạo sự di chuyển giản đồ:

manage.py schemamigration fooapp --auto

Bây giờ bạn có hai giao dịch và việc di chuyển trong hai bước sẽ hoạt động.


8
PostgreSQL có thể đã thay đổi hành vi của nó liên quan đến các giao dịch như vậy, vì tôi đã quản lý để chạy quá trình di chuyển với cả thay đổi dữ liệu và giản đồ trên máy phát triển của tôi (PostgreSQL 9.4) trong khi nó không thành công trên máy chủ (PostgreSQL 9.1).
Bertrand Bordage,

1
Đối với tôi cũng gần như vậy. Nó hoạt động hoàn hảo cho hơn 100 lần di chuyển (bao gồm ~ 20 lần di chuyển dữ liệu) cho đến ngày nay, đồng thời bổ sung ràng buộc duy nhất cùng nhau cùng với việc di chuyển dữ liệu loại bỏ các bản sao trước đó. PostgreSQL 10,0
LinPy quạt

Nếu sử dụng hoạt động RunPython trong quá trình di chuyển để di chuyển dữ liệu, bạn chỉ cần đảm bảo rằng đó là hoạt động cuối cùng. Django biết rằng nếu hoạt động RunPython là cuối cùng, để mở giao dịch của chính nó.
Dougyfresh 10/09/19

1
@Dougyfresh đây có phải là một tính năng được ghi lại của django không?
guettli

Tôi thực sự không thấy điều này ở đâu cả, chỉ là thứ tôi quan sát được. docs.djangoproject.com/en/2.2/ref/migration-operations/…
Dougyfresh

9

Vừa gặp vấn đề này. Bạn cũng có thể sử dụng db.start_transaction () và db.commit_transaction () trong việc di chuyển giản đồ để tách các thay đổi dữ liệu khỏi các thay đổi lược đồ. Có lẽ không quá rõ ràng để có một quá trình di chuyển dữ liệu riêng biệt nhưng trong trường hợp của tôi, tôi sẽ cần lược đồ, dữ liệu và sau đó là một lần di chuyển giản đồ khác nên tôi quyết định thực hiện tất cả cùng một lúc.


7
Vấn đề với giải pháp này là: Điều gì sẽ xảy ra nếu quá trình di chuyển của bạn không thành công sau db.commit_transaction ()? Tôi thích sử dụng ba chuyển đổi, nếu bạn cần: schema-mig, data-mig, schema-mig.
guettli

5
Xem: django.readthedocs.io/en/latest/ref/migration-operations.html Trên cơ sở dữ liệu hỗ trợ các giao dịch DDL (SQLite và PostgreSQL), hoạt động RunPython không có bất kỳ giao dịch nào được thêm tự động ngoài các giao dịch được tạo cho mỗi lần di chuyển. Vì vậy, trên PostgreSQL, chẳng hạn, bạn nên tránh kết hợp các thay đổi lược đồ và hoạt động RunPython trong cùng một lần di chuyển hoặc bạn có thể gặp lỗi như OperationalError: không thể ALTER TABLE "mytable" vì nó có các sự kiện kích hoạt đang chờ xử lý.
Iasmini Gomes,

5

Tại các hoạt động, tôi đặt SET CONSTRAINTS:

operations = [
    migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE;'),
    migrations.RunPython(migration_func),
    migrations.RunSQL('SET CONSTRAINTS ALL DEFERRED;'),
]

Tốt hơn nên sử dụng SeparateDatabaseAndState
bdoubleu

0

Bạn đang thay đổi lược đồ cột. Cột chân trang đó không thể chứa giá trị trống nữa. Rất có thể có các giá trị trống đã được lưu trữ trong DB cho cột đó. Django sẽ cập nhật các hàng trống đó trong DB của bạn từ giá trị trống thành giá trị mặc định hiện tại bằng lệnh di chuyển. Django cố gắng cập nhật các hàng trong đó cột chân trang có giá trị trống và thay đổi lược đồ cùng lúc (tôi không chắc).

Vấn đề là bạn không thể thay đổi cùng một lược đồ cột mà bạn đang cố gắng cập nhật các giá trị cho cùng một lúc.

Một giải pháp sẽ là xóa tệp di chuyển cập nhật giản đồ. Sau đó, chạy một tập lệnh để cập nhật tất cả các giá trị đó thành giá trị mặc định của bạn. Sau đó, chạy lại quá trình di chuyển để cập nhật giản đồ. Bằng cách này, cập nhật đã được thực hiện. Di chuyển Django chỉ thay đổi lược đồ.


1
Chạy một số tập lệnh không thực sự là một lựa chọn đối với tôi. Tôi có một số trường hợp của cơ sở dữ liệu và quá trình triển khai liên tục chỉ gọi "management.py di chuyển". Câu hỏi này đã là câu trả lời hợp lệ và hoạt động tốt.
guettli
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.