2010-10-14 27 views
14

Sử dụng các mô hình liên quan sau đây (một blog entry có thể có nhiều sửa đổi):Django - xóa Cascade trong ManyToManyRelation

class BlogEntryRevision(models.Model): 
    revisionNumber = models.IntegerField() 
    title = models.CharField(max_length = 120) 
    text = models.TextField() 
    [...] 

class BlogEntry(models.Model): 
    revisions = models.ManyToManyField(BlogEntryRevision) 
    [...] 

Làm thế nào tôi có thể nói Django để xóa tất cả liên quan BlogEntryRevision s khi BlogEntry tương ứng sẽ bị xóa? Mặc định có vẻ là giữ các đối tượng trong một quan hệ nhiều-nhiều nếu một đối tượng của "bên kia" bị xóa. Bất kỳ cách nào để làm điều này - tốt nhất là không ghi đè BlogEntry.delete?

Trả lời

10

Tôi nghĩ rằng bạn đang hiểu nhầm bản chất của mối quan hệ ManyToMany. Bạn nói về "BlogEntry tương ứng" đang bị xóa. Nhưng toàn bộ điểm của ManyToMany là mỗi BlogEntryRevision có nhiều BlogCác bài viết liên quan đến nó. (Và, tất nhiên, mỗi BlogEntry có nhiều BlogEntryRevisions, nhưng bạn đã biết điều đó rồi.)

Từ tên bạn đã sử dụng và thực tế là bạn muốn chức năng xóa tầng này, tôi nghĩ bạn sẽ tốt hơn với tiêu chuẩn ForeignKey từ BlogEntryRevision để BlogEntry. Miễn là bạn không đặt null=True trên Khóa ngoại tuyến đó, việc xóa sẽ bị treo - khi BlogEntry bị xóa, tất cả Bản xem lại cũng sẽ bị xóa.

+2

Có, tôi thừa nhận rằng sai lầm trong phần bình luận cho câu trả lời khác. Vì lợi ích của sự hoàn chỉnh: Giả sử một trường hợp sử dụng có ý nghĩa hơn với 'ManyToManyRelation', điều gì sẽ là một cách tốt để xếp tầng xóa? Cách tiếp cận từ câu trả lời đã xóa của Gabi Purcaru có hiệu quả không? – AndiDog

+2

Việc xóa sẽ nhảy vào '' null = True'' –

+0

Hành vi xóa tầng trên 'ForeignKey' có thể được điều khiển bằng ['on_delete'] (https://docs.djangoproject.com/en/dev/ref/models/ fields/# django.db.models.ForeignKey.on_delete) (nhưng không mặc định là 'CASCADE') – meshy

2

Bạn có thể sử dụng một custom model manager, nhưng tài liệu dường như chỉ ra rằng nó does do something like this already và tôi có thể không nhớ chính xác những gì này có nghĩa là:

Phương pháp xóa, thuận tiện, là tên delete(). Phương pháp này ngay lập tức xóa đối tượng và có không có giá trị trả về. Ví dụ:

e.delete() 

Bạn cũng có thể xóa đối tượng với số lượng lớn. Mỗi QuerySet có phương thức delete() , xóa tất cả các thành viên của QuerySet đó.

Ví dụ, điều này sẽ xoá tất cả nhập đối tượng với một năm PUB_DATE năm 2005:

Entry.objects.filter(pub_date__year=2005).delete() 

Hãy nhớ rằng ý chí này, bất cứ khi nào có thể, được thực hiện hoàn toàn trong SQL, và do đó delete () các phương thức các cá thể đối tượng riêng lẻ sẽ không được gọi là nhất thiết phải được gọi trong quá trình . Nếu bạn đã cung cấp phương thức delete() tùy chỉnh trên lớp mô hình và muốn đảm bảo rằng nó được gọi, bạn sẽ cần phải "thủ công" xóa trường hợp của mô hình đó (ví dụ: lặp qua QuerySet và gọi xóa() trên từng đối tượng riêng lẻ) thay vì sử dụng phương thức xóa hàng loạt() của một QuerySet.

Khi Django xóa một đối tượng, nó mô phỏng hành vi của SQL chế ON DELETE CASCADE - trong Nói cách khác, bất kỳ đối tượng trong đó có phím nước ngoài chỉ vào đối tượng để bị xóa sẽ bị xóa cùng với số . Ví dụ:

b = Blog.objects.get(pk=1) 
# This will delete the Blog and all of its Entry objects. 
b.delete() 
+0

Phải, xóa tầng là mặc định cho 1: N ('ForeignKey') quan hệ như' Blog 1: N Entry' trong tài liệu trích dẫn. Nhưng tôi có một 'ManyToManyRelation', do đó, thác chỉ xóa các hồ sơ mà nói" mục X và sửa đổi Y thuộc về nhau "nhưng không phải là bản ghi' BlogEntryRevision' chính nó. Có lẽ tôi nên làm điều đó với 'ForeignKey' trong trường hợp sử dụng của tôi ... Dù sao, hãy xem xét câu hỏi như một câu hỏi chung chung về xóa nhiều tầng. – AndiDog

8

tôi đã sử dụng hợp cụ thể này chính xác ngày hôm nay:

  • mẫu Tác giả: có thể có nhiều mục
  • Mẫu nhập cảnh: có thể có nhiều tác giả

Đối với điều này, tôi đang sử dụng một ManyToManyRelationship.

Trường hợp sử dụng của tôi là: nếu tôi xóa mục nhập cuối cùng của một tác giả cụ thể, thì tác giả này cũng sẽ bị xóa.

Các giải pháp có thể đạt được bằng cách sử dụng pre_delete signal:

@receiver(pre_delete, sender=Entry) 
def pre_delete_story(sender, instance, **kwargs): 
    for author in instance.authors.all(): 
     if author.entries.count() == 1 and instance in author.entries.all(): 
      # instance is the only Entry authored by this Author, so delete it 
      author.delete() 
+0

Tại sao chọn' và instance trong authors.entries.all() '? Bạn đã lặp qua các tác giả trong quan hệ cá thể. –

+0

Đó là một câu hỏi hay, tôi không chắc tại sao tôi lại thêm nó. Nó có lẽ cũng có thể mà không cần kiểm tra. – jessepeng

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