on_delete làm gì trên các mô hình Django?


348

Tôi khá quen thuộc với Django, nhưng gần đây nhận thấy có một on_delete=models.CASCADEtùy chọn với các mô hình, tôi đã tìm kiếm tài liệu tương tự nhưng không thể tìm thấy gì hơn:

Thay đổi trong Django 1.9:

on_deletebây giờ có thể được sử dụng làm đối số vị trí thứ hai (trước đây nó thường chỉ được truyền dưới dạng đối số từ khóa). Nó sẽ là một đối số bắt buộc trong Django 2.0.

một trường hợp ví dụ về việc sử dụng là

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

On_delete làm gì? ( Tôi đoán các hành động sẽ được thực hiện nếu mô hình bị xóa )

Không gì models.CASCADElàm gì? ( bất kỳ gợi ý trong tài liệu )

Những lựa chọn khác có sẵn ( nếu tôi đoán là chính xác )?

Tài liệu cho việc này nằm ở đâu?


Ngoài ra còn có một câu trả lời cho một câu hỏi tương tự tại stackoverflow.com/questions/47914325/
Khăn

1
Văn bản từ câu hỏi tương tự này hiện được liệt kê, dưới đây, về câu trả lời này. Nó bắt đầu "FYI, tham số on_delete trong các mô hình ngược với những gì nó nghe giống như." Nó cung cấp nhiều chi tiết hơn các câu trả lời ban đầu.
HelenM

Câu trả lời:


777

Đây là hành vi để áp dụng khi đối tượng được tham chiếu bị xóa. Nó không đặc trưng cho django, đây là một tiêu chuẩn SQL.

Có 6 hành động có thể thực hiện khi sự kiện đó xảy ra:

  • CASCADE: Khi đối tượng được tham chiếu bị xóa, cũng xóa các đối tượng có tham chiếu đến nó (Khi bạn xóa một bài đăng trên blog chẳng hạn, bạn cũng có thể muốn xóa bình luận). Tương đương SQL : CASCADE.
  • PROTECT: Cấm xóa đối tượng được tham chiếu. Để xóa nó, bạn sẽ phải xóa tất cả các đối tượng tham chiếu thủ công. Tương đương SQL : RESTRICT.
  • SET_NULL: Đặt tham chiếu thành NULL (yêu cầu trường là nullable). Chẳng hạn, khi bạn xóa một Người dùng, bạn có thể muốn giữ các bình luận mà anh ấy đã đăng trên các bài đăng trên blog, nhưng nói rằng nó đã được đăng bởi một người dùng ẩn danh (hoặc đã xóa). Tương đương SQL : SET NULL.
  • SET_DEFAULT: Đặt giá trị mặc định. Tương đương SQL : SET DEFAULT.
  • SET(...): Đặt một giá trị nhất định. Đây không phải là một phần của tiêu chuẩn SQL và hoàn toàn được xử lý bởi Django.
  • DO_NOTHING: Có lẽ là một ý tưởng rất tồi vì điều này sẽ tạo ra các vấn đề toàn vẹn trong cơ sở dữ liệu của bạn (tham chiếu một đối tượng thực sự không tồn tại). Tương đương SQL : NO ACTION.

Nguồn: Tài liệu Django

Xem thêm tài liệu của PostGreSQL chẳng hạn.

Trong hầu hết các trường hợp, CASCADElà hành vi được mong đợi, nhưng đối với mỗi ForeignKey, bạn nên luôn tự hỏi hành vi dự kiến ​​trong tình huống này là gì. PROTECTSET_NULLthường hữu ích. Đặt CASCADEnơi không nên, có khả năng có thể xóa tất cả cơ sở dữ liệu của bạn theo tầng, chỉ bằng cách xóa một người dùng.


Ghi chú bổ sung để làm rõ hướng thác

Thật buồn cười khi nhận thấy rằng hướng CASCADEhành động không rõ ràng đối với nhiều người. Trên thực tế, nó buồn cười để thông báo rằng chỉ những CASCADEhành động không rõ ràng. Tôi hiểu hành vi xếp tầng có thể gây nhầm lẫn, tuy nhiên bạn phải nghĩ rằng đó là cùng một hướng với bất kỳ hành động nào khác . Do đó, nếu bạn cảm thấy CASCADEhướng đó không rõ ràng với bạn, điều đó thực sự có nghĩa là on_deletehành vi đó không rõ ràng với bạn.

Trong cơ sở dữ liệu của bạn, một khóa ngoại được cơ bản đại diện bởi một trường số nguyên mà giá trị là khóa chính của đối tượng nước ngoài. Hãy nói rằng bạn có một mục comment_A , trong đó có một khóa ngoại đến một mục article_B . Nếu bạn xóa mục bình luận_A , mọi thứ đều ổn, article_B đã từng sống mà không có bình luận_A và đừng bận tâm nếu nó bị xóa. Tuy nhiên, nếu bạn xóa article_B , sau đó comment_A hoảng loạn! Nó không bao giờ sống mà không có article_B và cần nó, đó là một phần thuộc tính của nó ( article=article_Bnhưng * article_B ** ???) là gì. Đây là nơi on_deletebước vào, để xác định cách khắc phục lỗi toàn vẹn này, bằng cách nói:

  • "Không! Làm ơn! Đừng! Tôi không thể sống thiếu em!" (được nói PROTECTbằng ngôn ngữ SQL)
  • "Được rồi, nếu tôi không phải của bạn, thì tôi không là ai cả" (người ta nói SET_NULL)
  • "Tạm biệt thế giới, tôi không thể sống thiếu bài viết_B" và tự sát (đây là CASCADEhành vi).
  • "Không sao, tôi đã có người yêu rảnh rỗi, tôi sẽ tham khảo bài viết_C từ bây giờ" ( SET_DEFAULThoặc thậm chí SET(...)).
  • "Tôi không thể đối mặt với thực tế, tôi sẽ tiếp tục gọi tên bạn ngay cả khi đó là điều duy nhất còn lại với tôi!" ( DO_NOTHING)

Tôi hy vọng nó làm cho hướng thác rõ ràng hơn. :)


19
Một câu hỏi ngớ ngẩn, nhưng thác phải luôn luôn là một hướng phải không? Tức là nếu Comment có khóa ngoại để BlogPostxóa BlogPost thì nên xóa Nhận xét, nhưng xóa Nhận xét không nên xóa BlogPost, bất kể RDMS?
Anthony Manning-Franklin

20
@AnthonyManningFranklin Chắc chắn. Khi xóa chỉ được kích hoạt khi một tham chiếu bị "hỏng". Đó không phải là trường hợp khi bạn xóa một bình luận, khi bạn xóa tham chiếu cùng một lúc.
Antoine Pinsard 14/03/2017

6
Câu hỏi không ngớ ngẩn; Tôi cũng cần lời giải thích đó. Vì vậy, ở đây chúng tôi giả sử mối quan hệ là một bên, chủ sở hữu của mối quan hệ là Comment, người có trường FK trong bảng của nó, trong khi BlogPost"sở hữu" Commentnếu chúng ta nói về mô hình thực tế. Tốt
WesternGun

3
Một điều quan trọng cần lưu ý là việc thiết lập một on_delete trong Django KHÔNG tạo ra một mệnh đề ON DELETE trong chính cơ sở dữ liệu. Hành vi được chỉ định (như CASCADE) sẽ chỉ ảnh hưởng đến việc xóa được thực hiện thông qua Django và không ảnh hưởng đến việc xóa trực tiếp trong cơ sở dữ liệu.
JoeMjr2

2
Giải thích tuyệt vời. Nhận được một upvote từ tôi!
Homunculus Reticulli

42

Các on_deletephương pháp được sử dụng để nói với Django phải làm gì với trường hợp mô hình mà phụ thuộc vào các trường hợp mô hình mà bạn xóa. (ví dụ như một ForeignKeymối quan hệ). Thông báo on_delete=models.CASCADEcho Django xếp tầng hiệu ứng xóa tức là tiếp tục xóa các mô hình phụ thuộc.

Đây là một ví dụ cụ thể hơn. Giả sử bạn có một Authormô hình ForeignKeytrong một Bookmô hình. Bây giờ, nếu bạn xóa một thể hiện của Authormô hình, Django sẽ không biết phải làm gì với các thể hiện của Bookmô hình phụ thuộc vào thể hiện của Authormô hình đó. Các on_deletephương pháp bảo Django phải làm gì trong trường hợp đó. Cài đặt on_delete=models.CASCADEsẽ hướng dẫn Django xếp tầng hiệu ứng xóa tức là xóa tất cả các phiên bản Bookmô hình phụ thuộc vào thể hiện Authormô hình mà bạn đã xóa.

Lưu ý: on_deletesẽ trở thành một đối số bắt buộc trong Django 2.0. Trong các phiên bản cũ hơn, nó mặc định là CASCADE.

Đây là toàn bộ tài liệu chính thức.


37

FYI, on_deletetham số trong các mô hình là ngược với những gì nó nghe như. Bạn đặt on_deleteKhóa ngoài (FK) trên mô hình để báo cho django biết phải làm gì nếu mục FK mà bạn đang trỏ đến trong hồ sơ của bạn bị xóa. Các tùy chọn cửa hàng của chúng tôi đã sử dụng nhiều nhất là PROTECT, CASCADE, và SET_NULL. Dưới đây là các quy tắc cơ bản tôi đã tìm ra:

  1. Sử dụng PROTECTkhi FK của bạn đang trỏ đến một bảng tra cứu thực sự không nên thay đổi và điều đó chắc chắn không nên khiến bảng của bạn thay đổi. Nếu bất cứ ai cố gắng xóa một mục trên bảng tra cứu đó, PROTECTngăn họ xóa nó nếu nó được gắn với bất kỳ hồ sơ. Nó cũng ngăn django xóa bản ghi của bạn chỉ vì nó đã xóa một mục trên bảng tra cứu. Phần cuối cùng này là rất quan trọng. Nếu ai đó xóa giới tính "Nữ" khỏi bảng Giới tính của tôi, tôi sẽ KHÔNG muốn xóa ngay lập tức bất kỳ ai và tất cả những người tôi có trong bảng Người có giới tính đó.
  2. Sử dụng CASCADEkhi FK của bạn đang trỏ đến một bản ghi "cha mẹ". Vì vậy, nếu một người có thể có nhiều mục PersonEthnicity (anh / cô ấy có thể Mỹ Da Đỏ, Đen, và Trắng), và rằng Người được xóa, tôi thực sự sẽ muốn bất kỳ "con" PersonEthnicity mục bị xóa. Họ không liên quan nếu không có Người.
  3. Sử dụng SET_NULLkhi bạn làm muốn mọi người được phép xóa một mục trên một bảng nhìn lên, nhưng bạn vẫn muốn giữ hồ sơ của bạn. Ví dụ: nếu một Người có thể có Trường trung học, nhưng điều đó thực sự không quan trọng với tôi nếu trường cấp ba đó biến mất trên bàn tra cứu của tôi, tôi sẽ nói on_delete=SET_NULL. Điều này sẽ để lại hồ sơ cá nhân của tôi ra khỏi đó; nó sẽ chỉ đặt FK trung học trên Người của tôi thành null. Rõ ràng, bạn sẽ phải cho phép null=Truetrên FK đó.

Dưới đây là một ví dụ về một mô hình thực hiện cả ba điều:

class PurchPurchaseAccount(models.Model):
    id = models.AutoField(primary_key=True)
    purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
    paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
    _updated = models.DateTimeField()
    _updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.

    def __unicode__(self):
        return str(self.paid_from_acct.display)
    class Meta:
        db_table = u'purch_purchase_account'

Là một mẩu tin cuối cùng, bạn có biết rằng nếu bạn không chỉ định on_delete(hoặc không), thì hành vi mặc định là CASCADE? Điều này có nghĩa là nếu ai đó đã xóa mục nhập giới tính trên bảng Giới tính của bạn, bất kỳ hồ sơ Người nào có giới tính đó cũng sẽ bị xóa!

Tôi sẽ nói, "Nếu nghi ngờ, hãy thiết lập on_delete=models.PROTECT." Sau đó đi kiểm tra ứng dụng của bạn. Bạn sẽ nhanh chóng tìm ra FK nào sẽ được gắn nhãn các giá trị khác mà không gây nguy hiểm cho bất kỳ dữ liệu nào của bạn.

Ngoài ra, điều đáng chú ý on_delete=CASCADElà thực sự không được thêm vào bất kỳ di chuyển nào của bạn, nếu đó là hành vi bạn đang chọn. Tôi đoán điều này là bởi vì nó là mặc định, vì vậy việc đặt on_delete=CASCADEcũng giống như không đặt gì cả.


12

Như đã đề cập trước đó, CASCADE sẽ xóa bản ghi có khóa ngoại và tham chiếu đến một đối tượng khác đã bị xóa. Vì vậy, ví dụ nếu bạn có trang web bất động sản và có Tài sản tham chiếu Thành phố

class City(models.Model):
    # define model fields for a city

class Property(models.Model):
    city = models.ForeignKey(City, on_delete = models.CASCADE)
    # define model fields for a property

và bây giờ khi Thành phố bị xóa khỏi cơ sở dữ liệu, tất cả các Thuộc tính được liên kết (ví dụ: bất động sản nằm trong thành phố đó) cũng sẽ bị xóa khỏi cơ sở dữ liệu

Bây giờ tôi cũng muốn đề cập đến giá trị của các tùy chọn khác, chẳng hạn như SET_NULL hoặc SET_DEFAULT hoặc thậm chí DO_NOTHING. Về cơ bản, từ góc độ quản trị, bạn muốn "xóa" những hồ sơ đó. Nhưng bạn không thực sự muốn chúng biến mất. Vì nhiều lý do. Ai đó có thể đã vô tình xóa nó, hoặc để kiểm tra và giám sát. Và báo cáo đơn giản. Vì vậy, nó có thể là một cách để "ngắt kết nối" tài sản khỏi Thành phố. Một lần nữa, nó sẽ phụ thuộc vào cách ứng dụng của bạn được viết.

Ví dụ: một số ứng dụng có trường "bị xóa" là 0 hoặc 1. Và tất cả các tìm kiếm và danh sách lượt xem của chúng, v.v., mọi thứ có thể xuất hiện trong báo cáo hoặc bất cứ nơi nào người dùng có thể truy cập từ giao diện người dùng, loại trừ mọi thứ deleted == 1. Tuy nhiên, nếu bạn tạo một báo cáo tùy chỉnh hoặc truy vấn tùy chỉnh để kéo xuống danh sách các bản ghi đã bị xóa và thậm chí nhiều hơn để xem khi nào nó được sửa đổi lần cuối (trường khác) và bởi ai (tức là ai đã xóa nó và khi nào) .. đó là rất thuận lợi từ quan điểm điều hành.

Và đừng quên rằng bạn có thể hoàn nguyên việc xóa ngẫu nhiên đơn giản như deleted = 0đối với những hồ sơ đó.

Quan điểm của tôi là, nếu có một chức năng, luôn có một lý do đằng sau nó. Không phải lúc nào cũng là một lý do tốt. Nhưng một lý do. Và thường là một tốt quá.


3
Điều này rất hữu ích vì nó đã làm rõ hướng CASCADE xảy ra theo hướng nào. Câu trả lời được chấp nhận là không rõ ràng nếu bạn không quen thuộc với các tầng SQL.
Codecripblr 15/03/19

Cảm ơn bạn :) nhiều đánh giá cao!
George Mogilevsky

2
Tôi nêu lên câu trả lời này vì nó đáp ứng sự nghi ngờ của tôi về hướng đi trong mô hình mối quan hệ
edepe

6

Đây là câu trả lời cho câu hỏi của bạn cho biết: tại sao chúng tôi sử dụng on_delete?

Khi một đối tượng được tham chiếu bởi ForeignKey bị xóa, Django theo mặc định mô phỏng hành vi của ràng buộc SQL TRÊN XÓA CASCADE và cũng xóa đối tượng có chứa ForeignKey. Hành vi này có thể được ghi đè bằng cách chỉ định đối số on_delete. Ví dụ: nếu bạn có ForeignKey nullable và bạn muốn nó được đặt thành null khi đối tượng được tham chiếu bị xóa:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

Các giá trị có thể cho on_delete được tìm thấy trong django.db.models:

CASCADE: Xóa tầng; mặc định

BẢO VỆ: Ngăn chặn việc xóa đối tượng được tham chiếu bằng cách nâng ProtectedError, một lớp con của django.db.IntegrityError.

SET_NULL: Đặt ForeignKey null; điều này chỉ có thể nếu null là True.

SET_DEFAULT: Đặt ForeignKey thành giá trị mặc định của nó; một mặc định cho ForeignKey phải được đặt.


Những từ đơn giản làm cho tôi rõ ràng vì tôi cũng không trưởng thành với sql và django. Cảm ơn bạn.
wm.p1us

2

Giả sử bạn có hai mô hình, một tên là Người và một mô hình khác có tên Công ty .

Theo định nghĩa, một người có thể tạo ra nhiều hơn một công ty.

Xem xét một công ty có thể có một và chỉ một người, chúng tôi muốn rằng khi một người bị xóa thì tất cả các công ty liên quan đến người đó cũng bị xóa.

Vì vậy, chúng tôi bắt đầu bằng cách tạo một mô hình Person, như thế này

class Person(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.id+self.name

Sau đó, mô hình công ty có thể trông như thế này

class Companies(models.Model):
    title = models.CharField(max_length=20)
    description=models.CharField(max_length=10)
    person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)

Lưu ý việc sử dụng on_delete=models.CASCADEtrong các công ty mô hình. Đó là xóa tất cả các công ty khi người sở hữu nó (ví dụ của lớp Person) bị xóa.


1

Định hướng lại mô hình tinh thần của bạn về chức năng của "CASCADE" bằng cách nghĩ đến việc thêm FK vào một tầng đã có sẵn (tức là một thác nước). Nguồn của thác nước này là một Khóa chính. Xóa dòng chảy xuống.

Vì vậy, nếu bạn xác định on_delete của FK là "CASCADE", thì bạn đang thêm bản ghi của FK này vào một loạt các xóa có nguồn gốc từ PK. Bản ghi của FK có thể tham gia vào tầng này hay không ("SET_NULL"). Trong thực tế, một bản ghi với FK thậm chí có thể ngăn chặn dòng chảy của việc xóa! Xây dựng một con đập với "BẢO VỆ."


0

Sử dụng CASCADE có nghĩa là thực sự bảo Django xóa bản ghi được tham chiếu. Trong ví dụ về ứng dụng thăm dò ý kiến ​​dưới đây: Khi một 'Câu hỏi' bị xóa, nó cũng sẽ xóa các Lựa chọn mà Câu hỏi này có.

vd: Câu hỏi: Bạn đã nghe về chúng tôi như thế nào? (Lựa chọn: 1. Bạn bè 2. Quảng cáo trên TV 3. Công cụ tìm kiếm 4. Quảng cáo qua email)

Khi bạn xóa câu hỏi này, nó cũng sẽ xóa tất cả bốn lựa chọn này khỏi bảng. Lưu ý rằng nó chảy theo hướng nào. Bạn không cần phải đặt on_delete = model.CASCADE trong Câu hỏi mô hình đưa nó vào Lựa chọn.

from django.db import models



class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.dateTimeField('date_published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_legth=200)
    votes = models.IntegerField(default=0)
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.