پروسه Validation و Deserialization در DRF

responseامروز داشتم یه endpoint مینوشتم و قرار بود مثلا یه رکورد x ایجاد بشه که فیلد productش فارنکی بود، وقتی برای سریالایزر از ModelSerializer استفاده کرده باشیم و تو مدل یه فیلد فارنکی هم داشته باشیم DRF براش یه فیلد دیفالت رو در نظر میگیره و اگر زمان POST کردن دیتا ایدی رو اشتباه بدیم، این ارور بالا رو تو response برمیگردونه. اینجا یه مشکلی برام پیش اومد چون نمیتونستم با فیلد ولیدیتور یا get-object تو ویو و... بیام فرمت error رو بهتر بکنم و اون چیزی که میخوام رو تو response برگردونم برای همین یکم سرچ کردم و کد هاش رو دیدم و... در نهایت خواستم تجربم رو اینجا به اشتراک بزارم.# ModelSerializerداخل DRF میتونیم دیتا رو Serialize یا Deserialize بکنیم که این پروسه اتوماتیک هستش و وقتی دیکشنری میدیم به serializer میفهمه و deserialize میکنه و وقتی ابجکت پایتونی میدیم بهش میاد سریالایزش میکنه، مثلا وقتی ریکوئست POST رو ارسال میکنیم میاد deserialize میکنه:serializer(ممکنه از جنریک ویو ،ویو ست و... استفاده کنیم که و این قسمت رو نبینیم.)اینجا اگر MySerializer از ModelSerizlier و یا HyperlinkedModelSerializer ارث بری کرده باشه یه سری ویژگی رو دریافت میکنه، که اینجا ما با ویژگی فیلد های ریلیشنال کار داریم که باعث میشد اون ارور تو Response برای من نمایش داده باشه و با مواردی که گفتم نتونم هندلش بکنم.وقتی از ModelSerializer استفاده میکنیم اگر داخل مدل مون یه فیلد فارنکی داشته باشیم، اینجا داخل مدل سریلایزر به صورت دیفالت میاد از فیلد PrimaryKeyRelatedField استفاده میکنه که برای OnToOne یا ForeignKey ریلیشن استفاده میشه و این خودش یه class هستش که شامل یه سری متود میشه، متودی که ما باهاش کار داریم to_internal_value هستش اما قبل از اینکه بریم راجب این حرف بزنیم باید یه مورد دیگه رو هم بگیم.# Method: is_valid - Processزمانی که کاربر ریکوئست POST رو ارسال میکنه، دیتاش به شکل دیکشنری(json) هستش و باید این دیکشنری تبدیل بشه به ابجکت پایتونی و اینجا ما درواقع با صدا زدن متود is_valid اون اینستنسی که بالاتر ساختیم این پروسه رو انجام میدیم، وقتی این متود صدا زده میشه درواقع flow زیر شکل میگیره:validation and deserialization processاینجا اولین بار اون مواردی که داخل متود to_internal_value هست چک میشه (البته اولین متود نیست در ادامه میگیم).بعد از این میاد validators ها رو چک میکنه: این ولیدیتور ها رو زمان ایجاد فیلد تو کانستراکتور میدیم بهش، همون پارامتر validators که میتونه مثلا MaxLengthValidator ،UniqueValidator ProhibitNullCharactersValidator و... باشه)فیلدولیدیتور ها هم شامل متود هایی میشن که داخل سریالایزر تعریف میکنیم مثل validate_age, validate_email و...ابجکت ولیدیشن یا cross-validation هم شامل مواردی میشه که ما نیاز داریم چند مورد رو با هم ولیدیت بکنیم مثلا بگیم اگر کاربر پرمیوم بود و پروداکت x از کتگوری y رو میخواست بیا براش یه تخفیف بده...(این مورد 3-4 شبیه clean وclean_field داخل form ها هستن).# Method: is_valid - Process Detailsبالاتر یه توضیح کلی دادیم و اما بریم داخل جزئیات :‌در مرحله اول داخل is_valid با متود validate_empty_values چک میشه و اگر فیلد های که داریم مقدارشون خالی باشه(استرینگ "" یا Null یا...) و همچنین required نباشن میگه برای این فیلد نیازی به ولیدیشن نیست.اما اگر validate_empty_values ما False باشه یعنی فیلد empty نداشته باشیم میاد متود to_internal_value ابجکت فعلی رو ران میکنه، اینجا ابجکت فعلی میشه همون فیلد هامون که تو پروسه Deserialization ایجاد شده، یعنی مثلا PrimaryKeyRelatedField یا CharField و هر چیزی که داخلی مدل سریالیزر تعریف کردیم.نکته: این فیلد هایی که داخل سریلایزر تعریف میکنیم همشون متود to_internal_value رو دیفالت دارن چون از کلس Field ارث بری میکنن.اگر داخل سریلایزر مون یه فیلد فارنکی داشته باشیم، همونطور که بالاتر گفتم میشه PrimaryKeyRelatedField و اینجا زمانی که متود to_internal_value صدا زده میشه این فیلد یه عمکلرد متفاوتی رو داره و میاد این کوئری رو میزنه:queryاین data میشه همون ایدی فیلد فارنکی که تو ریکوئست POST میفرستیم و خود فیلد هم در زمان Deserialization یه کوئری ست تایین میشه براش(دیفالت yyy.objects.all()i) و الان اینجا وقتی get میکنه اگر ایدی وجود نداشت باشه میاد تو excpetion و اون ارور رو با فرمت عکس اول برمیگردونه!.# Overwrite to_internal_valueمیتونیم بیایم داخل متود post یا initial یا handle_exception این رو هندل بکنیم ولی من متود to_internal_value رو overwrite کردم چون نمی یه سری سریالایزر داشتم که از سریالایز فعلی ارث بری کرده بودن و به نظرم اینجا بهتر بود براش(اگه نظر شما چیز دیگه ای کامنت بزارید ). البته متود post میتونه بهتر باشه چون در این مثال overhead کمتری داره و نیازی به deserialize کردن نیست و همون اول میتونیم کوئری بزنیم و چک کنیم این مورد رو.to_internal_value method اگر به نظرتون به شکل دیگه ای میشد بهتر نوشت یا هر چیز دیگه ای میتونید کامنت بزارید، مرسی که وقت گذاشتید❤️!