2009-03-25 23 views
7

Tôi có một ForeignKey có thể rỗng trong mô hình của tôi để mô hình hóa một khớp nối lỏng giữa các mô hình. Có vẻ như vậy:Nullable ForeignKeys và xóa một thể hiện mô hình được tham chiếu

class Message(models.Model): 
    sender = models.ForeignKey(User, null=True, blank=True) 
    sender_name = models.CharField(max_length=255) 

Khi lưu tên người gửi được ghi vào thuộc tính sender_name. Bây giờ, tôi muốn có thể xóa cá thể Người dùng được tham chiếu bởi người gửi và để lại thông báo tại chỗ.

Ngoài hộp, mã này luôn dẫn đến thư đã xóa ngay khi tôi xóa phiên bản Người dùng. Vì vậy, tôi nghĩ rằng một xử lý tín hiệu sẽ là một ý tưởng tốt.

def my_signal_handler(sender, instance, **kwargs): 
    instance.message_set.clear() 
pre_delete.connect(my_signal_handler, sender=User) 

Đáng buồn thay, đó không phải là giải pháp. Bằng cách nào đó Django đầu tiên thu thập những gì nó muốn xóa và sau đó bắn xử lý pre_delete.

Bất kỳ ý tưởng nào? Cái nút ở đâu trong não tôi?

Trả lời

13

Django thực sự mô phỏng hành vi của ON DELETE CASCADE của SQL và không có cách nào được ghi lại trong tài liệu của hộp để thay đổi điều này. Các tài liệu mà họ đề cập đến ở đây là gần cuối phần này: Deleting objects.

Bạn nói đúng là Django thu thập tất cả các phiên bản mô hình có liên quan, sau đó gọi trình xử lý trước khi xóa cho từng trường hợp. Người gửi tín hiệu sẽ là lớp mô hình sắp bị xóa, trong trường hợp này là Message, thay vì User, điều này làm cho khó phát hiện sự khác biệt giữa xóa tầng được kích hoạt bởi Người dùng và xóa bình thường ... đặc biệt là từ tín hiệu để xóa lớp Người dùng cuối cùng, vì đó là lần xóa cuối cùng :-)

Tuy nhiên, bạn có thể nhận danh sách các đối tượng mà Django đang đề xuất xóa trước khi gọi hàm User.delete(). Mỗi cá thể mô hình có một phương thức nửa riêng gọi là _collect_sub_objects() để biên dịch danh sách các cá thể có khóa ngoài trỏ đến nó (nó biên dịch danh sách này mà không xóa các cá thể). Bạn có thể xem phương thức này được gọi bằng cách xem số delete() trong số django.db.base.

Nếu đây là một trong các đối tượng của riêng bạn, tôi khuyên bạn nên ghi đè phương pháp delete() trên cá thể của bạn để chạy _collect_sub_objects() rồi ngắt khóa ngoại trừ trước khi gọi xóa siêu hạng. Vì bạn đang sử dụng đối tượng Django tích hợp mà bạn có thể thấy quá khó để phân lớp (mặc dù có thể thay thế đối tượng Người dùng của riêng bạn cho django), bạn có thể phải dựa vào logic xem để chạy _collect_sub_objects và ngắt FK trước xóa.

Dưới đây là một ví dụ nhanh-và-bẩn:

from django.db.models.query import CollectedObjects 
u = User.objects.get(id=1) 


instances_to_be_deleted = CollectedObjects() 
u._collect_sub_objects(instances_to_be_deleted) 

for k in instances_to_be_deleted.ordered_keys(): 
    inst_dict = instances_to_be_deleted.data[k] 
    for i in inst_dict.values(): 
     i.sender = None # You will need a more generic way for this 
     i.save() 

u.delete() 
+0

Cảm ơn lời giải thích và gợi ý để tìm hiểu. –

+0

Câu trả lời kỹ lưỡng. –

0

Vừa phát hiện ra ON DELETE CASCADE hành vi bản thân mình, tôi thấy rằng trong Django 1,3 họ đã thực hiện các khóa ngoại vi configurable.

Các vấn đề liên quan