;...z@nko!#
;...z@nko!#
خواندن ۴ دقیقه·۲ سال پیش

پروسه Validation و Deserialization در DRF

response
response

امروز داشتم یه endpoint مینوشتم و قرار بود مثلا یه رکورد x ایجاد بشه که فیلد productش فارنکی بود، وقتی برای سریالایزر از ModelSerializer استفاده کرده باشیم و تو مدل یه فیلد فارنکی هم داشته باشیم DRF براش یه فیلد دیفالت رو در نظر میگیره و اگر زمان POST کردن دیتا ایدی رو اشتباه بدیم، این ارور بالا رو تو response برمیگردونه.

اینجا یه مشکلی برام پیش اومد چون نمیتونستم با فیلد ولیدیتور یا get-object تو ویو و... بیام فرمت error رو بهتر بکنم و اون چیزی که میخوام رو تو response برگردونم برای همین یکم سرچ کردم و کد هاش رو دیدم و... در نهایت خواستم تجربم رو اینجا به اشتراک بزارم.



# ModelSerializer

داخل DRF میتونیم دیتا رو Serialize یا Deserialize بکنیم که این پروسه اتوماتیک هستش و وقتی دیکشنری میدیم به serializer میفهمه و deserialize میکنه و وقتی ابجکت پایتونی میدیم بهش میاد سریالایزش میکنه، مثلا وقتی ریکوئست POST رو ارسال میکنیم میاد deserialize میکنه:

serializer
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
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
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
to_internal_value method


اگر به نظرتون به شکل دیگه ای میشد بهتر نوشت یا هر چیز دیگه ای میتونید کامنت بزارید، مرسی که وقت گذاشتید❤️!

drfdjangovalidationpython
یه بک اند دولوپر پایتون، علاقمه مند به DevOps و دیپ شدن در مباحث مرتبط "-) https://mastodon.social/@zankoAN
شاید از این پست‌ها خوشتان بیاید