تک ویت امید

گزینه‌های مختلف ForeignKey on_delete در جنگو

· امید ·
on_delete در foreignkey

foreignkey مدل های جنگو لازمه که مقدار on_delete رو براشون تعریف کنی. و این برای ForeignKey و OneToOne و ManyToMany ها هم صادقه. توی این نوشته مقادیر مختلفی که میتونیم برای on_delete ست کنیم رو با مثال توضیح میدم.

دو تا مدل Post و Comment رو به شکل زیر تعریف کردم:

class Post(models.Model):
	title = models.CharField(max_length=255)

class Comment(models.Model):
	content = models.TextField()  #👇🏻
	post = models.ForeignKey(Post, on_delete=[?])

توی مثال بالا مدل Comment وقتی که Post رو حذف می‌کنیم بر اساس مقداری که برای on_delete ست کردیم عمل میکنه، و باید توجه داشته باشیم که این کار بصورت برعکس عمل نمیکنه یعنی اگر کامنتی حذف بشه تاثیری روی Post نداره.

حالا که متوجه شدیم جریان از چه قراره بریم که متد ها رو برسی کنیم.

models.CASCADE

وقتی که این مقدار رو ست میکنیم مدلمون به شکل زیر در میاد:

class Comment(models.Model):
	... 
	post = models.ForeignKey(Post, on_delete=models.CASCADE)

وقتی که ما post.delete() رو صدا بزنیم، میره و کامنت ها اون پست رو پاک میکنه.

models.SET(function or value)

اولین مورد از سه گانه خانواده models.SET[…]. برای این مورد، رابطه Post و Comment با عقل جور در نمیاد که وقتی پستی پاک میشه کامنت هارو به پست دیگه ارجاع بدیم.

مثالی که بیشتر با عقل جور درمیاد یه ForeignKey از Comment به کاربر هستش. وقتی که یه کاربر رو حذف میکنیم، ممکنه که بخوایم کامنت هاشون رو حفظ کنیم که مشابهش رو ممکنه توی Reddit دیده باشید.

foreignkey on_delete in reddit deleted user

میتونیم به همچین چیزی با استفاده از models.SET به دست بیاریم:

def get_deleted_user_instance():
    return User.objects.get(username='deleted')

class Comment(models.Model):
    ...
    author = models.ForeignKey(User,
            on_delete=models.SET(get_deleted_user_instance))
    #                                 👆🏻 

حالا اگر کاربر رو حذف کنیم، مقدار “deleted” جایگزین اون کاربر میشه.

models.SET_DEFAULT

اینم تقریبا مثل SET قبلی کار میکنه با این تفاوت که SET_DEFAULT مقدارش رو با default که توی مدل تعریف مکنیم جایگزین میکنه.

ANONYMOUS_USER_ID = 1

class Comment(models.Model):
    ...
    author = models.ForeignKey(User, 
            default=ANONYMOUS_USER_ID, 
            on_delete=models.SET_DEFAULT)

models.SET_NULL

حالا که با خانواده SET ها آشنا شدیم دیگه مشخصه که این یکی چیکار میکنه.

البته باید این نکته رم در نظر داشته باشیم که برای اینکه بتونیم از SET_NULL استفاده کنیم باید مقدار null=True رو برای مدلمون تعریف کرده باشیم.

models.PROTECT

وقتی که بخوایم Comment رو حذف کنیم PROTECT برامون ارور بر میگردونه:

class Comment(models.Model):
	... 
	post = models.ForeignKey(Post, on_delete=models.PROTECT)

اگه ما Post درست کنیم میتونیم بدون مشکل اون رو حذف کنیم. ولی اگه یه نفر یه Comment بزاره، وقتی که بخوایم کامنت رو حذف کنیم جنگو میاد و ارور ProtectedError رو برامون برمیگردونه.

models.DO_NOTHING

از اسمش مشخصه که چیکار میکنه، هیچکاری نمیکنه. و به شکل زیر پیاده سازی شده که درواقع یه pass هستش:

# in django.db.models.deletion
def DO_NOTHING(collector, field, sub_objs, using):
    pass

این میتونه خیلی خطرناک و دردسر ساز باشه چون مدل ممکنه به چیزی اشاره نداشته باش. از این مورد پیشنهاد نمیشه که استفاده بشه مگر اینکه در سطح دیتابیس رابطه هارو تعریف کرده باشید.

جمع بندی

امیدوارم که یکی دو چیز یادگرفته باشید. بیشتر چیزی که الان درمورد on_delete در foreignkey جنگو خوندید توی داکیونت های جنگو نوشته شده ولی مقداری کمتر توضیح داده(همین عکسی که اول پست گذاشتم). و اگه میخواید بیشتر توی این موضوع عمیق بشید سورس کد on_delete handler جنگو رو یه نگاهی بندازید.