2010-02-14 40 views
15

Nếu chúng ta thiết lập một hồ sơ như thế nào Django khuyến cáo:Django xóa đối tượng nước ngoài?

class Profile(models.Model): 
    user = models.ForeignKey(User, unique=True) 

Sau đó, khi bạn xóa đối tượng User từ Django admin, nó xóa too.This hồ sơ của mình là bởi vì hồ sơ có một chính nước ngoài để sử dụng và nó muốn để bảo vệ tính toàn vẹn tham chiếu. Tuy nhiên, tôi muốn chức năng này ngay cả khi con trỏ đang đi theo cách khác. Ví dụ, trên lớp Profile của tôi, tôi có:

shipper = models.ForeignKey(Shipper, unique=True, blank=True, null=True) 
carrier = models.ForeignKey(Carrier, unique=True, blank=True, null=True) 
affiliat = models.ForeignKey(Affiliate, unique=True, blank=True, null=True, verbose_name='Affiliate') 

Và tôi muốn nó để nếu bạn xóa Profile nó sẽ xóa người gửi hàng/hãng/đối tượng liên kết liên quan (đừng hỏi tôi tại sao Django thực hiện " liên kết "một số từ khóa lạ). Bởi vì chủ hàng, nhà cung cấp dịch vụ và đơn vị liên kết là loại người dùng và không có ý nghĩa gì đối với họ khi không có phần còn lại của dữ liệu (không ai có thể đăng nhập dưới dạng dữ liệu).

Lý do tôi không đặt các phím trên các đối tượng khác, là bởi vì khi đó Django sẽ phải nội bộ tham gia tất cả những bảng mỗi khi tôi muốn kiểm tra loại người dùng đã ...

+0

"đơn vị liên kết" chắc chắn không phải là "một loại từ khóa kỳ lạ nào" ở Django. Tôi có thể tạo ra một mô hình với một lĩnh vực có tên là "liên kết" và làm việc với nó chỉ tốt trong mã của tôi. –

+0

Điều đó rất lạ. Tất cả mọi thứ hoạt động tốt nhưng nó từ chối xuất hiện trong phần quản trị. Bạn đã kiểm tra điều đó chưa? SVN checkout như một tuần trước. – mpen

Trả lời

10

Trong khi sử dụng tín hiệu post_delete như được mô tả bernardo ở trên là phương pháp ok, điều đó sẽ hoạt động tốt, tôi cố gắng tránh sử dụng tín hiệu ít nhất có thể như tôi cảm thấy nó vi phạm mã của bạn không cần thiết bằng cách thêm hành vi vào chức năng chuẩn ở những nơi mà người ta có thể mong đợi.

Tôi thích phương pháp ghi đè ở trên, tuy nhiên, ví dụ do Felix đưa ra không có một lỗ hổng gây tử vong; Hàm delete() được ghi đè như sau:

def delete(self, using=None): 
    using = using or router.db_for_write(self.__class__, instance=self) 
    assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname) 

    collector = Collector(using=using) 
    collector.collect([self]) 
    collector.delete() 

Chú ý tham số 'sử dụng', trong hầu hết trường hợp chúng ta gọi là xóa() với các đối số trống để chúng ta có thể đã biết. Trong ví dụ trên, tham số này được chúng tôi ghi đè và không xem xét chức năng của siêu lớp, nếu ai đó chuyển thông số 'using' khi xóa Tiểu sử, nó sẽ gây ra hành vi không mong muốn. Để tránh điều đó, chúng tôi sẽ chắc chắn để bảo vệ lập luận cùng với lika mặc định của nó vậy:

class Profile(models.Model): 
# ... 

def delete(self, using=None): 
    if self.shipper: 
     self.shipper.delete() 
    if self.carrier: 
     self.carrier.delete() 
    if self.affiliat: 
     self.affiliat.delete() 
    super(Profile, self).delete(using) 

Một khó khăn cho việc tiếp cận trọng, tuy nhiên, đó là xóa() không được rõ ràng gọi cho mỗi bản ghi db trên số lượng lớn xóa, điều này có nghĩa là nếu bạn muốn xóa nhiều Cấu hình cùng một lúc và giữ hành vi ghi đè (gọi .delete() trên truy vấn django chẳng hạn), bạn sẽ cần tận dụng tín hiệu xóa (như được mô tả bởi bernardo) hoặc bạn sẽ cần phải lặp qua từng hồ sơ xóa chúng riêng lẻ (đắt tiền và xấu xí).

+0

không cần phải xác định lý do tại sao bạn trả lời khi câu trả lời là tốt, và bạn nhận được tín dụng cho nó quá – dashesy

+0

Lưu ý rằng xóa () phương thức cho một đối tượng không nhất thiết phải được gọi khi xóa các đối tượng hàng loạt bằng cách sử dụng một QuerySet. Để đảm bảo logic xóa tùy chỉnh được thực hiện, bạn có thể sử dụng các tín hiệu pre_delete và/hoặc post_delete. https://docs.djangoproject.com/en/1.9/topics/db/models/#overriding-model-methods – dnaranjo

+0

@dnaranjo Có, đây là tất cả được đặt ra trong những cạm bẫy được liệt kê ở phần cuối của câu trả lời của tôi. Cảm ơn bạn đã bình luận mặc dù, hy vọng một người không đọc qua toàn bộ câu trả lời sẽ nhận thấy những cạm bẫy tiềm năng bằng cách đọc ý kiến ​​của bạn, và sẽ hỗ trợ trong việc đưa ra một quyết định giáo dục cho nhu cầu cá nhân của họ. – krayzk

5

Bạn có thể override the delete() phương thức của lớp Cấu hình và xóa các đối tượng khác trong phương thức này trước khi bạn xóa cấu hình thực.

Cái gì như:

class Profile(models.Model): 
    # ... 

    def delete(self): 
     if self.shipper: 
      self.shipper.delete() 
     if self.carrier: 
      self.carrier.delete() 
     if self.affiliat: 
      self.affiliat.delete() 
     super(Profile, self).delete() 
+0

Có vẻ tốt, nhưng phương thức 'delete()' dường như không bao giờ được gọi khi tôi xóa nội dung thông qua quản trị Django ...? – mpen

+2

@Mark: Nó ít nhất sẽ hoạt động khi bạn xóa một đối tượng duy nhất. Với việc xóa nhiều đối tượng có vẻ như là một vấn đề: http://code.djangoproject.com/ticket/10751 –

5

Một cách tốt hơn để làm điều này và làm việc với các phương pháp xóa đối tượng và QuerySet của xóa phương pháp được sử dụng tín hiệu post_delete, như bạn có thể nhìn thấy trong documentation.

Trong trường hợp của bạn, mã của bạn sẽ là khá tương tự như sau:

from django.db import models 
from django.dispatch import receiver 

@receiver(models.signals.post_delete, sender=Profile) 
def handle_deleted_profile(sender, instance, **kwargs): 
    if instance.shipper: 
     instance.shipper.delete() 
    if instance.carrier: 
     instance.carrier.delete() 
    if instance.affiliat: 
     instance.affiliat.delete() 

này chỉ hoạt động cho Django 1.3 hoặc cao hơn bởi vì các tín hiệu post_delete đã được bổ sung trong phiên bản Django này.

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