پس از ساخت مدل‌ها در جنگو، اغلب نیاز می‌شود که داده‌ها در اختیار برنامه‌های موبایل یا فرانت‌اندهایی مانند React قرار بگیرد. جنگو به صورت پیش‌فرض سازوکاری اختصاصی برای این نوع ارتباطات فراهم نمی‌کند.

در چنین شرایطی، نیاز به یک واسط داریم. واسطی که مدل‌ها را به خروجی JSON تبدیل کند. درخواست‌های GET و POST را مدیریت نماید. و امکان ارتباط اپلیکیشن‌های دیگر با سایت را فراهم سازد.

به این واسط، API گفته می‌شود.

Django REST Framework (یا به اختصار DRF) مجموعه‌ای از ابزارهای آماده برای ساخت همین نوع واسط‌هاست. بدون اینکه توسعه‌دهنده مجبور باشد از صفر منطق کوئری‌پارامترها یا مدیریت وضعیت‌های خطا را پیاده‌سازی کند.

در این درس، با مفاهیم پایه‌ای API آشنا می‌شوید. دلایل استفاده از DRF به جای نوشتن دستی API بررسی می‌گردد. همچنین توضیح داده می‌شود که چرا جنگوی خالص به تنهایی گزینه مناسبی برای پیاده‌سازی API نیست.

محتوای این درس جنبه تئوری دارد. اما تلاش شده هر مفهوم همراه با مثال عینی ارائه شود. هدف این است که پس از مطالعه این درس، بدانید دقیقاً DRF در چه مرحله‌ای از توسعه یک پروژه به کار می‌آید.

API چیست؟ (Web API vs REST)

فرض کنید در یک رستوران نشسته‌اید. شما غذا می‌خواهید، اما نمی‌توانید مستقیم به آشپزخانه بروید و غذا را بردارید. یک نفر بین شما و آشپزخانه وجود دارد: پیشخدمت. شما سفارش را به پیشخدمت می‌دهید. پیشخدمت سفارش را به آشپز می‌رساند. سپس غذای آماده را برای شما می‌آورد. در دنیای نرم‌افزار، API همان نقش پیشخدمت را بازی می‌کند.

API یک واسط است. دو نرم‌افزار به کمک API با هم حرف می‌زنند. بدون اینکه مستقیماً به کدهای یکدیگر دسترسی داشته باشند. فرقی نمی‌کند آن دو نرم‌افزار روی یک کامپیوتر باشند یا در دو نقطه متفاوت از جهان.

تفاوت API معمولی با Web API

API می‌تواند درون یک سیستم عامل باشد. مثلاً وقتی برنامه Paint از ویندوز می‌خواهد ماوس شما را تشخیص دهد، از یک API داخلی استفاده می‌کند. اما Web API نوع خاصی از API است. ارتباط از طریق اینترنت و با پروتکل HTTP انجام می‌شود. وقتی اپلیکیشن موبایل شما اطلاعات آب و هوا را از سرور دریافت می‌کند، دارد با یک Web API کار می‌کند.

وقتی سایت خبری آخرین اخبار را از دیتابیس اصلی می‌خواند، همان مسیر را می‌رود. تفاوت اصلی در نوع ارتباط است. API معمولی محدود به همان دستگاه یا شبکه داخلی است. WebAPI برای ارتباط از راه دور طراحی شده.

آشنایی با REST و اصول آن

REST یک سبک طراحی برای Web APIهاست. مخفف Representational State Transfer است. اما حفظ کردن این عبارت خیلی ضروری نیست. مهم اصولی است که تعریف می‌کند. یک API زمانی RESTful نامیده می‌شود که چند قانون ساده را رعایت کند:

منابع (Resources)

هر چیزی که قرار است از طریق API در دسترس باشد، یک منبع نام دارد. کاربر، مقاله، محصول، سفارش. هر منبع یک آدرس منحصربه‌فرد دارد. مثلاً /users/123/ آدرس منبع کاربر با شناسه ۱۲۳ است.

متدهای HTTP

برای کار با منابع از متدهای استاندارد HTTP استفاده می‌شود:

  • GET برای گرفتن داده
  • POST برای ساختن داده جدید
  • PUT یا PATCH برای بروزرسانی
  • DELETE برای حذف

هر متد معنی مشخصی دارد. وقتی یک URL را با متد GET صدا می‌زنید، انتظار تغییر در داده‌ها وجود ندارد.

Stateless بودن

شاید مهم‌ترین اصل REST. یعنی سرور هیچ اطلاعاتی از کاربر بین درخواست‌ها حفظ نمی‌کند. در هر درخواست، تمام اطلاعات مورد نیاز باید همراه درخواست فرستاده شود. سرور فراموش می‌کند قبلاً چه اتفاقی افتاده. این قانون باعث می‌شود مقیاس‌پذیری بالا برود. سرورها می‌توانند بدون نگرانی از وضعیت یک کاربر خاص، درخواست‌ها را پاسخ دهند.

در درس‌های بعدی هنگام پیاده‌سازی عملی، هر سه اصل بالا را خواهید دید. مخصوصاً Stateless بودن وقتی به بحث احراز هویت می‌رسیم، خیلی ملموس می‌شود.

 

چرا DRF؟ معرفی و مزایا

اجازه بدهید ابتدا این سوال را مطرح کنیم که چرا از خود جنگو استفاده نکنیم؟ در جواب باید گفت که جنگو برای ساخت وب‌سایت‌های سنتی ساخته شده. همان سایت‌هایی که صفحات HTML را در سرور تولید می‌کنند و به مرورگر می‌فرستند. اما وقتی صحبت از API می‌شود، جنگو خالی چند مشکل اساسی دارد.

اول اینکه جنگو خروجی پیش‌فرضی برای JSON ندارد. می‌توانید از JsonResponse استفاده کنید. اما برای هر بار تبدیل داده به JSON باید کد بنویسید. اعتبارسنجی ورودی هم باید دستی انجام شود.

دوم اینکه جنگو متدهای HTTP را به شکل ساده مدیریت نمی‌کند. تشخیص اینکه کاربر درخواست GET فرستاده یا POST، نیاز به چک کردن شرطی دارد.

سوم اینکه احراز هویت برای API در جنگوی خالی سخت است. توکن‌ها، هدرهای سفارشی، و مدیریت دسترسی‌ها همه باید از صفر نوشته شوند.

و آخر اینکه ابزارهایی مثل صفحه‌بندی، فیلتر کردن، و مستندسازی خودکار در جنگو وجود ندارد. هرکدام را خودتان باید بسازید.

نتیجه نهایی یک کد تکراری، مستعد خطا، و غیراستاندارد است.

مزایای DRF نسبت به نوشتن دستی API

سریالایزرها

کار تبدیل مدل‌های جنگو به JSON را خودکار می‌کنند. همچنین اعتبارسنجی داده ورودی. با چند خط کد، یک مدل کامل تبدیل به API می‌شود.

احراز هویت آماده

DRF چندین روش احراز هویت را از قبل پیاده‌سازی کرده. Token، Session، Basic، و حتی قابلیت اضافه کردن JWT با یک کتابخانه جانبی. بدون اینکه نیاز باشد توکن‌ها را دستی تولید و بررسی کنید.

صفحه‌بندی

وقتی تعداد رکوردها زیاد می‌شود، فرستادن همه آن‌ها در یک پاسخ منطقی نیست. DRF صفحه‌بندی را با چند خط تنظیم فعال می‌کند. کاربر می‌تواند انتخاب کند هر صفحه چند رکورد ببیند.

مستندسازی خودکار

همان لحظه که API را می‌سازید، DRF یک صفحه مرورگرپسند برای تست آماده می‌کند. همه اندپوینت‌ها، متدهای مجاز، و فرمت داده‌ها در آن صفحه نشان داده می‌شود. بدون اینکه خطی مستندات جداگانه بنویسید.

علاوه بر اینها، مدیریت خطا، انواع ویوهای آماده، و سازگاری با استانداردهای REST از دیگر مزایای DRF هستند.

مقایسه DRF با فریمورک‌های مشابه در پایتون

FastAPI

FastAPI جدیدتر است و سرعت بالایی دارد. مستندسازی خودکار با OpenAPI جزو قوت‌های اصلی آن است. اما اکوسیستم کوچک‌تری نسبت به DRF دارد. همچنین بر پایه‌ی جنگو ساخته نشده، پس از ORM جنگو نمی‌توانید مستقیم استفاده کنید.

Flask با کتابخانه‌های جانبی

Flask یک میکروفیمورک است. برای API می‌توانید از Flask-RESTful استفاده کنید. اما خیلی چیزها را خودتان باید بسازید. سریالایزر، احراز هویت، صفحه‌بندی. Flask به شما آزادی کامل می‌دهد، اما هزینه آن نوشتن کد بیشتر است.

کجا DRF برنده است؟

اگر از قبل جنگو بلد هستید و پروژه شما مدل‌های پیچیده و روابط بین جداول دارد، DRF بهترین گزینه است. یکپارچگی کامل با ORM جنگو، کمترین کدنویسی برای تبدیل مدل‌ها به API، و جامعه کاربری بزرگ از نقاط قوت اصلی محسوب می‌شود.

اگر سرعت اجرا برایتان اولویت اول است و مدل‌های ساده‌ای دارید، FastAPI می‌تواند جذاب باشد. اما برای یادگیری و استخدام در شرکت‌های ایرانی که بیشتر پروژه‌هایشان با جنگو است، DRF همچنان انتخاب اصلی محسوب می‌شود.

معماری کلاینت-سرور در DRF

در جنگوی سنتی، سرور همه کار را انجام می‌دهد. مدل را از دیتابیس می‌خواند. داده را در قالب HTML قالب‌بندی می‌کند. و صفحه کامل را به مرورگر می‌فرستد. به این روش Server-Side Rendering یا SSR می‌گویند. کاربر روی لینکی کلیک می‌کند. صفحه دوباره بارگذاری می‌شود. تمام HTML از نو ساخته و ارسال می‌گردد.

در API-based، سرور فقط داده خام می‌فرستد. معمولاً به شکل JSON. کار نمایش و چیدمان بر عهده برنامه دیگری است. ممکن است همان مرورگر باشد. یا یک اپلیکیشن موبایل. یا حتی یک دستگاه اینترنت اشیا. ساخت HTML در سمت کلاینت انجام می‌شود. سرور فقط وظیفه تامین داده را دارد.

نقش DRF به عنوان واسط بین Frontend و دیتابیس

فرض کنید یک تیم فرانت‌اند با React کار می‌کند. تیم بک‌اند هم با جنگو. بدون DRF، این دو تیم راه مستقیمی برای ارتباط ندارند. فرانت‌اند نمی‌تواند مستقیماً به دیتابیس دسترسی داشته باشد. امنیت اجازه نمی‌دهد. DRF یک لایه میانی می‌سازد. فرانت‌اند یک درخواست GET به DRF می‌فرستد. DRF مدل مربوطه را از دیتابیس می‌خواند. به JSON تبدیل می‌کند. و برای فرانت‌اند برمی‌گرداند.

همین سناریو برای POST هم صادق است. فرانت‌اند JSON می‌فرستد. DRF اعتبارسنجی می‌کند و در دیتابیس ذخیره می‌نماید. این جداسازی مزیت بزرگی دارد. تیم بک‌اند فقط روی منطق داده تمرکز می‌کند. تیم فرانت‌اند فقط روی تجربه کاربری. و DRF پل ارتباطی بین آن دو است. همین نقش برای Vue، Next، یا حتی اپلیکیشن موبایل اندروید و iOS هم کار می‌کند.

معرفی Stateful vs Stateless

Stateful (با حالت)

سرور اطلاعات کاربر را بین درخواست‌ها حفظ می‌کند. مثلاً بعد از ورود به سایت، یک متغیر در سرور ذخیره می‌شود که "کاربر علی وارد شده". درخواست بعدی کاربر، سرور هنوز یادش است که این همان علی است. در جنگوی سنتی با session ها، وضعیت کاربر در سرور نگهداری می‌شود. این یعنی stateful.

Stateless (بدون حالت)

هر درخواست کاملاً مستقل از بقیه است. سرور هیچ اطلاعاتی از کاربر در حافظه خود نگه نمی‌دارد. بعد از پاسخ دادن به درخواست، همه چیز را فراموش می‌کند. اگر کاربر بخواهد کاری انجام دهد، باید هر بار تمام اطلاعات لازم را همراه درخواست بفرستد. مثلاً توکن احراز هویت را در هدر قرار دهد.

چرا REST Stateless است؟

سه دلیل اصلی دارد.

  • اول: مقیاس‌پذیری. اگر سروری وضعیت کاربر را نگه دارد، آن کاربر همیشه باید به همان سرور متصل شود. با stateless بودن، هر سروری می‌تواند به هر درخواستی پاسخ دهد. کافی است توکن معتبر باشد.
  • دوم: سادگی. سرور مجبور نیست حافظه خود را مدیریت کند. نیازی به پاک کردن session‌های منقضی شده نیست. منطق ساده‌تر می‌شود.
  • سوم: بازیابی بعد از خطا. اگر یک سرور از کار بیفتد، سرور دیگر می‌تواند بدون مشکل درخواست‌های بعدی را پاسخ دهد. چون اطلاعات کاربر در جای دیگری ذخیره نشده.

در DRF، احراز هویت با توکن همین قاعده را پیاده می‌کند. کاربر توکن را همراه هر درخواست می‌فرستد. سرور توکن را بررسی می‌کند، اما چیزی در حافظه نگه نمی‌دارد. این یعنی stateless.

پیش‌نیازهای استفاده از DRF

قبل از شروع یادگیری DRF، باید چند چیز را بلد باشید. این پیش‌نیازها قابل چشم‌پوشی نیستند. بدون آن‌ها، کدهای DRF برای شما مبهم و گیج‌کننده خواهد بود.

آشنایی با پایتون (توابع، کلاس‌ها، دیکشنری)

DRF با پایتون نوشته شده. شما هم برای استفاده از آن باید پایتون بلد باشید.

توابع در پایتون را یاد بگیرید. چطور تعریف می‌شوند. چطور پارامتر می‌گیرند. چطور خروجی برمی‌گردانند.

کلاس‌ها هم مهم هستند. در DRF مدام با کلاس‌ها سر و کار دارید. ویوها کلاس هستند. سریالایزرها کلاس هستند. اگر با مفهوم self و __init__ و ارث‌بری آشنا نباشید، خیلی زود گم می‌شوید.

دیکشنری هم از ارکان اصلی کار با DRF است. داده‌هایی که از درخواست می‌آید به شکل دیکشنری است. داده‌ای که برای پاسخ آماده می‌شود هم دیکشنری است. باید راحت با کلیدها و مقادیر کار کنید.

آشنایی با جنگو (مدل‌ها، ORM، URLconf)

فرض کنید DRF یک لایه اضافه روی جنگو است. اگر جنگو را بلد نباشید، DRF را هم نمی‌توانید یاد بگیرید.

مدل‌های جنگو اولین و مهمترین بخش هستند. DRF مدل‌های شما را به API تبدیل می‌کند. باید بلد باشید یک مدل چطور تعریف می‌شود. فیلدهای مختلف چه کاربردی دارند. رابطهForeignKey و ManyToManyField چطور کار می‌کنند.

ORM جنگو هم لازم است. وقتی یک درخواست GET می‌آید، DRF پشت صحنه از ORM برای گرفتن داده استفاده می‌کند. باید بلد باشید filter و get و all چه می‌کنند.

URLconf هم بی نیاز نیستید. در DRF آدرس‌ها را به ویوها متصل می‌کنید. همان کاری که در جنگوی معمولی انجام می‌دادید. فقط گاهی با Routerها ساده‌تر می‌شود.

آشنایی مقدماتی با HTTP و مفاهیم وب

این بخش خیلی سنگین نیست. اما دانستن چند چیز الزامی است.

متدهای GET، POST، PUT، DELETE را بشناسید. کدهای وضعیت ۲۰۰، ۴۰۴، ۵۰۰ را دیده باشید. بدانید هدر چیست و بدنه درخواست چه نقشی دارد.

همین سطح از آشنایی کافی است. اگر قبلاً در جنگوی سنتی با فرمها کار کرده باشید، خیلی از این مفاهیم را به طور غیرمستقیم تجربه کرده‌اید.

یک توصیه مهم

اگر در هر کدام از این سه حوزه ضعف دارید، قبل از شروع DRF آن را جبران کنید. دو درس آموزش پایتون پایه و جنگوی پایه در همین آکادمی دقیقاً همین پیش‌نیازها را پوشش می‌دهند. مطالعه مجدد آن درس‌ها ارزشش را دارد.

بررسی اجمالی قابلیت‌های کلیدی DRF

این بخش را به عنوان نقشه راه درس‌های بعدی در نظر بگیرید. هر کدام از این قابلیت‌ها در جای خود به صورت عملی پیاده‌سازی خواهد شد.

سریالایزرها (تبدیل مدل ↔ JSON)

سریالایزر قلب DRF است. دو کار اصلی انجام می‌دهد:

  • اول: داده مدل جنگو را به JSON تبدیل می‌کند. برای وقتی که می‌خواهید پاسخ بفرستید.
  • دوم: JSON دریافتی از کاربر را تبدیل به دیکشنری می‌کند. اعتبارسنجی را هم انجام می‌دهد. مثلاً بررسی می‌کند فیلد ایمیل حتماً @ داشته باشد.

با یک ModelSerializer ساده، یک مدل کامل به API تبدیل می‌شود. بدون نوشتن کد تکراری.

ویوهای مبتنی بر کلاس (APIView, ViewSets)

در جنگوی معمولی بیشتر از توابع برای ویوها استفاده می‌کنید. در DRF اما ویوها معمولاً به شکل کلاس نوشته می‌شوند. APIView پایه‌ترین ویو کلاسی است. متدهای get، post، put، delete را در آن پیاده می‌کنید.

ViewSet مرحله بالاتر است. متدهای list، create، retrieve، update، destroy را پشتیبانی می‌کند. با اضافه شدن Router، دیگر نیازی به نوشتن URL برای هر کدام ندارید.

احراز هویت آماده (Token, JWT, Session)

ساختن احراز هویت از صفر کار سختی است. DRF چند روش آماده در اختیار شما می‌گذارد. TokenAuthentication یک توکن یکتا برای هر کاربر می‌سازد. کاربر توکن را در هدر درخواست می‌فرستد. سرور بررسی می‌کند.

SessionAuthentication از همان سیستم session جنگو استفاده می‌کند. برای موقعی که فرانت‌اند و بک‌اند روی یک دامنه هستند. JWT یک استاندارد مدرن‌تر است. با کتابخانه djangorestframework-simplejwt اضافه می‌شود. توکن امضا شده دارد و تاریخ انقضا پذیر است.

صفحه‌بندی، فیلتر، جستجو

وقتی تعداد رکوردها زیاد می‌شود، فرستادن همه آنها در یک پاسخ منطقی نیست. صفحه‌بندی مشخص می‌کند هر بار چند رکورد فرستاده شود. کاربر می‌تواند با پارامتر ?page=2 صفحه بعد را درخواست کند. فیلتر به کاربر اجازه می‌دهد فقط رکوردهایی را بگیرد که شرط خاصی دارند. مثلاً ?price__lt=100000.

جستجو هم با پارامتر ?search=python کار می‌کند. فیلدهای مشخص شده را بررسی می‌کند و نتایج مرتبط را برمی‌گرداند.

مستندسازی خودکار (Browsable API)

یکی از قابلیت‌های منحصربه‌فرد DRF. وقتی API را روی مرورگر باز می‌کنید، صفحه زیبایی می‌بینید. تمام اندپوینت‌ها و متدهای مجاز نشان داده می‌شود. حتی می‌توانید فرم را پر کنید و درخواست تستی بفرستید. این صفحه برای توسعه و تست فوق‌العاده مفید است. نیاز به ابزار جداگانه مثل Postman را کاهش می‌دهد. در محیط تولید (production) می‌توانید آن را غیرفعال کنید.

این پنج قابلیت، هسته اصلی DRF را تشکیل می‌دهند. در درس‌های بعدی، هر کدام را به طور جداگانه و با مثال عملی پیاده‌سازی می‌کنیم.

متدهای HTTP: GET, POST, PUT, PATCH, DELETE

هر درخواستی که به یک API فرستاده می‌شود، یک متد همراه خود دارد. این متد مشخص می‌کند که کاربر چه قصدی دارد.

GET

برای گرفتن داده از سرور استفاده می‌شود. بدون تغییر در وضعیت سرور. فرض کنید می‌خواهید لیست مقالات را ببینید. یا اطلاعات یک مقاله خاص را بخوانید. متد مناسب GET است.

POST

برای ساختن داده جدید به کار می‌رود. مثلاً ثبت یک مقاله جدید یا ایجاد یک کاربر تازه. داده‌های جدید در بدنه درخواست قرار می‌گیرند.

PUT

برای جایگزینی کامل یک منبع استفاده می‌شود. تمام فیلدهای آن منبع باید در درخواست فرستاده شود. فیلدهایی که نیایند، خالی یا مقدار پیش‌فرض می‌گیرند.

PATCH

برای بروزرسانی جزیی یک منبع به کار می‌رود. فقط فیلدهایی که می‌خواهید تغییر دهید را می‌فرستید. بقیه فیلدها به همان شکل قبلی باقی می‌مانند.

DELETE

برای حذف یک منبع. اگر کاربر با شناسه ۱۲۳ را حذف کنید، درخواست بعدی برای دریافت همان کاربر با خطای ۴۰۴ مواجه می‌شود.

 

کدهای وضعیت (Status Codes)

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

۲۰۰ OK

درخواست موفق بوده. پاسخ در بدنه برگشته است. معمولاً برای GET و PUT و PATCH استفاده می‌شود.

۲۰۱ Created

منبع جدید ساخته شده. معمولاً در پاسخ به درخواست POST برگردانده می‌شود.

۴۰۰ Bad Request

درخواست از نظر فرمت مشکل دارد. مثلاً فیلد اجباری را نفرستاده‌اید. یا نوع داده اشتباه است.

۴۰۱ Unauthorized

کاربر هنوز وارد نشده. یا توکن معتبری همراه درخواست نفرستاده. یعنی سرور نمی‌داند چه کسی هستید.

۴۰۳ Forbidden

کاربر وارد شده، اما اجازه انجام این کار را ندارد. مثلاً یک کاربر معمولی می‌خواهد مقاله دیگران را حذف کند.

۴۰۴ Not Found

منبعی با آدرس مورد نظر وجود ندارد. یا آدرس اشتباه تایپ شده، یا رکورد با آن شناسه قبلاً حذف شده است.

۵۰۰ Internal Server Error

خطایی در سمت سرور رخ داده. کدی که خود برنامه‌نویس انتظارش را نداشته. مثلاً فراخوانی یک متد روی None.

ساختار یک درخواست و پاسخ RESTful

درخواست (Request) شامل چهار بخش اصلی است:

  1. آدرس (URL): مشخص می‌کند کدام منبع را می‌خواهید. مثل /articles/5/
  2. متد (Method): مشخص می‌کند چه کاری می‌خواهید انجام دهید. مثل GET یا POST
  3. هدرها (Headers): اطلاعات جانبی مثل نوع محتوا یا توکن احراز هویت
  4. بدنه (Body): داده‌هایی که می‌فرستید. معمولاً به شکل JSON. برای متدهای POST، PUT، PATCH استفاده می‌شود.

پاسخ (Response) شامل سه بخش اصلی است:

  1. کد وضعیت (Status Code): مثل ۲۰۰ یا ۴۰۴
  2. هدرها (Headers): اطلاعاتی مثل نوع محتوای پاسخ
  3. بدنه (Body): داده‌ای که درخواست کرده‌اید. معمولاً JSON. برای درخواست DELETE معمولاً بدنه خالی است.

یک مثال از یک درخواست GET موفق:

درخواست فرستاده می‌شود به GET /users/5/

پاسخ برمی‌گردد با کد ۲۰۰ و بدنه‌ای به این شکل:

{
  "id": 5,
  "name": "محمد کریمی",
  "email": "mohammad@example.com"
}

تمام APIهایی که در ادامه این درس می‌سازید، از همین ساختار پیروی می‌کنند.

آشنایی با JSON و مقایسه با XML

JSON چیست؟

JSON مخفف JavaScript Object Notation است. قالبی متنی برای ذخیره و انتقال داده. انسان می‌تواند آن را بخواند. کامپیوتر هم به راحتی پردازش می‌کند.

یک نمونه ساده JSON:

{
  "نام": "علی رضایی",
  "سن": ۳۲,
  "آموزش": true
}

مقایسه JSON با XML

XML هم قالبی قدیمی برای انتقال داده است. سال‌ها قبل خیلی استفاده می‌شد. نمونه XML برای همان داده بالا:

<کاربر>
  <نام>علی رضایی</نام>
  <سن>۳۲</سن>
  <آموزش>true</آموزش>
</کاربر>

تفاوت اصلی در حجم و سادگی است. JSON خیلی کوتاه‌تر نوشته می‌شود. به تگ باز و بسته نیاز ندارد. حجم نهایی کمتر و سرعت پردازش بالاتر است. XML هنوز در بعضی سیستم‌های قدیمی یا پروتکل‌های خاص مثل SOAP دیده می‌شود. اما برای APIهای امروزی، JSON استاندارد تقریباً قطعی است.

چرا JSON در APIها محبوب است؟

پنج دلیل اصلی دارد.

  • اول: ساختار ساده و خوانا. یک برنامه‌نویس تازه‌کار هم بعد از چند دقیقه متوجه ساختار JSON می‌شود.
  • دوم: حجم کم. نسبت به XML گاهی تا ۳۰ درصد کوچک‌تر است. در ترافیک اینترنت این تفاوت اهمیت دارد.
  • سوم: پشتیبانی همه زبان‌ها. پایتون، جاوااسکریپت، جاوا، PHP، همه برای کار با JSON ابزار آماده دارند.
  • چهارم: هماهنگی طبیعی با جاوااسکریپت. چون مرورگرها JSON را خیلی سریع تبدیل به آبجکت می‌کنند. برای برنامه‌نویسی وب این مزیت بزرگی است.
  • پنجم: امکان تو در تو شدن. می‌توانید داخل یک JSON، لیست و آبجکت دیگری تعریف کنید. این قدرت بیان بالایی می‌دهد.

ساختار JSON: object, array, key-value

JSON فقط دو ساختار اصلی دارد.

Object

یک جفت آکولاد {} در ابتدا و انتها. داخل آن چند کلید-مقدار قرار می‌گیرد. هر کلید حتماً با دابل کوتیشن نوشته می‌شود. کلیدها باید منحصربه‌فرد باشند.

{
  "نام": "سارا",
  "شهر": "تهران"
}

Array

یک جفت براکت [] در ابتدا و انتها. داخل آن یک لیست از مقادیر قرار می‌گیرد. ترتیب مهم است. نوع مقادیر می‌تواند هر چیزی باشد.

["سارا", "مریم", "ندا"]

Key-Value

به هر جفت کلید و مقدار می‌گویند key-value. کلید حتماً رشته (string) است. مقدار می‌تواند انواع مختلفی باشد:

  • رشته: "متن"
  • عدد: ۳۲ یا ۳.۱۴
  • بولین: true یا false
  • null: null
  • object دیگر: {...}
  • array دیگر: [...]

یک مثال ترکیبی:

{
  "نام": "محمد",
  "سن": ۲۸,
  "مهارت‌ها": ["پایتون", "جنگو", "DRF"],
  "آدرس": {
    "شهر": "اصفهان",
    "خیابان": "چهارباغ"
  },
  "فعال": true
}

تبدیل JSON به دیکشنری پایتون و بالعکس

پایتون کتابخانه استانداردی به اسم json دارد. بدون نیاز به نصب اضافه کار می‌کند.

تبدیل دیکشنری پایتون به JSON (سریالایز کردن)

import json

دیکشنری_مثال = {
    "نام": "مریم",
    "سن": ۳۰,
    "فعال": True
}

رشته_json = json.dumps(دیکشنری_مثال)
print(رشته_json)

خروجی:

{"نام": "مریم", "سن": 30, "فعال": true}

توجه کنید که True پایتون تبدیل شده به true در JSON.

تبدیل JSON به دیکشنری پایتون (دی‌سریالایز کردن)

import json

رشته_json = '{"نام": "مریم", "سن": 30, "فعال": true}'

دیکشنری_نتیجه = json.loads(رشته_json)
print(دیکشنری_نتیجه["نام"])

خروجی:

مریم

وقتی درخواستی به API می‌رسد، DRF خودکار بدنه JSON را به دیکشنری پایتون تبدیل می‌کند. نیازی به فراخوانی دستی json.loads ندارید.

همچنین وقتی می‌خواهید پاسخ بفرستید، DRF دیکشنری یا لیست پایتون را خودکار به JSON تبدیل می‌کند. تابع json.dumps را مستقیم استفاده نمی‌کنید. این کار توسط سریالایزرها انجام می‌شود. در درس‌های بعدی این فرآیند را مرحله به مرحله خواهید دید.

 

مقایسه DRF با نوشتن API خام با JsonResponse جنگو

خیلی از برنامه‌نویس‌های تازه‌کار فکر می‌کنند می‌توانند با خود JsonResponse هم API بسازند. از نظر فنی این حرف درست است. اما هزینه نگهداری و توسعه را بالا می‌برد.

در این بخش دو کد را کنار هم می‌بینید. یکی با جنگوی خالص. دیگری با DRF. هدف یکسان است: ساختن یک API برای لیست کتاب‌ها و اضافه کردن کتاب جدید.

کد با JsonResponse جنگوی خالص

from django.http import JsonResponse
from django.views import View
from .models import Book
import json

class BookAPI(View):
    def get(self, request):
        books = Book.objects.all()
        data = []
        for book in books:
            data.append({
                'id': book.id,
                'title': book.title,
                'author': book.author
            })
        return JsonResponse(data, safe=False)
    
    def post(self, request):
        body = json.loads(request.body)
        title = body.get('title')
        author = body.get('author')
        
        if not title or not author:
            return JsonResponse({'error': 'title and author are required'}, status=400)
        
        book = Book.objects.create(title=title, author=author)
        return JsonResponse({
            'id': book.id,
            'title': book.title,
            'author': book.author
        }, status=201)

کد با DRF (APIView)

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer

class BookAPI(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

و سریالایزر مربوطه:

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['id', 'title', 'author']

مزایای DRF در کاهش کد تکراری و اعتبارسنجی خودکار

مقایسه این دو کد چند تفاوت اساسی را نشان می‌دهد.

کمتر نوشتن، کار بیشتر

در نسخه جنگوی خالص، شما مجبورید حلقه for بزنید و دستی دیکشنری بسازید. در DRF، سریالایزر با many=True این کار را در یک خط انجام می‌دهد.

اعتبارسنجی خودکار

در نسخه خالص، باید چک کنید عنوان و نویسنده پر شده یا نه. اگر فیلدهای بیشتری داشتید، این کد خیلی بلند می‌شد.

در DRF، متد is_valid() تمام اعتبارسنجی‌های تعریف شده در سریالایزر را یکجا اجرا می‌کند. اگر مدل شما فیلد price با اعتبارسنجی min_value=1000 داشته باشد، DRF خودکار آن را بررسی می‌کند.

پاسخ استاندارد

JsonResponse با safe=False گاهی گیج‌کننده می‌شود. اما Response در DRF با ساختار یکسان و پشتیبانی از کدهای وضعیت استاندارد، کد را خوانا‌تر می‌کند.

مدیریت خطا

در نسخه خالص، خطاهای اعتبارسنجی را خودتان باید به JSON تبدیل کنید. در DRF، خطاهای سریالایزر به صورت خودکار به خروجی استاندارد تبدیل می‌شوند. فرمت خطاها هم یکسان است. کلاینت می‌داند چطور پردازش کند.

نتیجه نهایی

برای یک API با دو مدل ساده، شاید تفاوت زیاد به چشم نیاید. اما وقتی پروژه بزرگ می‌شود. مدل‌ها ۲۰ فیلد دارند. روابط خارجی و تو در تو وجود دارد. در آن نقطه است که ارزش DRF واقعاً دیده می‌شود. کدهای تکراری ناپدید می‌شوند. و باگ‌های انسانی کاهش پیدا می‌کنند.

آشنایی با Browsable API در DRF

این قابلیت چیست و چه کمکی به توسعه‌دهنده می‌کند؟ بیشتر فریمورک‌های API فقط داده خام برمی‌گردانند. اگر آدرس را در مرورگر باز کنید، یک صفحه سفید یا یک رشته JSON می‌بینید. نه چیز دیگر. اما DRF یک صفحه HTML کامل برای هر اندپوینت تولید می‌کند. این صفحه شامل چند بخش مفید است:

  • عنوان اندپوینت و توضیحات (اگر نوشته باشید)
  • متدهای HTTP مجاز روی این آدرس (GET، POST، و غیره)
  • فرمی برای ارسال درخواست و دیدن پاسخ
  • محتوای پاسخ به صورت JSON فرمت شده

به این قابلیت می‌گویند Browsable API.

برای یک توسعه‌دهنده، این صفحه مثل یک داشبورد کوچک است. بدون نصب نرم‌افزار اضافه، بدون نوشتن کد تست، می‌تواند رفتار API را بررسی کند. مهمترین کمکی که این قابلیت می‌کند، صرفه‌جویی در زمان است. در حین توسعه، مدام بین ویرایشگر کد و مرورگر جابه‌جا می‌شوید. Browsable API اجازه می‌دهد سریعترین مسیر را برای تست انتخاب کنید.

علاوه بر این، مستندات زنده است. هر تغییری در کد بدهید، بلافاصله در این صفحه اعمال می‌شود. نیازی به build کردن یا رفرش خاصی نیست.

چطور می‌توان API را در مرورگر تست کرد بدون Postman

فرض کنید یک API برای مدیریت کتاب‌ها ساخته‌اید. آدرس آن است: http://localhost:8000/api/books/ کافی است این آدرس را در مرورگر باز کنید.

تست درخواست GET

به محض باز شدن صفحه، همه کتاب‌ها را به صورت JSON می‌بینید. در بالای صفحه یک بخش آبی رنگ با عنوان "GET" وجود دارد. دکمه "OPTIONS" را بزنید. پاسخ سرور با تمام جزئیات نشان داده می‌شود.

تست درخواست POST

در پایین همان صفحه، یک فرم می‌بینید. عنوان "Content" را دارد. فیلدهای مدل شما در آن فرم قرار گرفته‌اند. مقادیر مورد نظر را پر کنید. دکمه "POST" را بزنید. صفحه رفرش می‌شود. کتاب جدید به لیست اضافه می‌گردد.

تست درخواست روی یک کتاب خاص

آدرس http://localhost:8000/api/books/1/ را باز کنید.

در این صفحه، فرمی برای PUT و PATCH و DELETE هم وجود دارد. می‌توانید یک کتاب را ویرایش یا حذف کنید. همه چیز از طریق همان مرورگر.

نکته مهم برای احراز هویت

اگر API شما نیاز به توکن دارد، Browsable API یک دکمه "Log in" در گوشه بالای صفحه قرار می‌دهد. با ورود به سایت جنگو (همان panel ادمین)، توکن به صورت خودکار در درخواست‌ها اضافه می‌شود.

محدودیت Browsable API

این ابزار برای توسعه و تست فوق‌العاده است. اما در محیط تولید معمولاً آن را غیرفعال می‌کنید. دو دلیل دارد:

اول: حجم پاسخ را افزایش می‌دهد. صفحه HTML اضافی کنار JSON فرستاده می‌شود.

دوم: امنیت. بهتر است کاربر نهایی این صفحه را نبینید.

برای غیرفعال کردن در محیط تولید، کافی است در settings.py مقدار DEFAULT_RENDERER_CLASSES را تنظیم کنید. در درس‌های پیشرفته این کار را خواهید دید.

 

جمع‌بندی 

در این درس یاد گرفتید که API یک واسط برای ارتباط نرم‌افزارهاست و REST مجموعه قوانینی است که این ارتباط را استاندارد می‌کند. دیدید که جنگوی خالص برای ساختن API محدودیت‌هایی دارد و DRF این محدودیت‌ها را با ابزارهایی مثل سریالایزر، ویوهای کلاسی، احراز هویت آماده و صفحه‌بندی پر می‌کند. با JSON و تفاوت آن با XML آشنا شدید. معماری کلاینت-سرور و تفاوت حالت stateful و stateless را مرور کردید. در انتها هم دیدید که چطور بدون نصب Postman و فقط با مرورگر، می‌توان API را تست کرد. از درس بعد، وارد بخش عملی می‌شویم. DRF را نصب می‌کنیم، اولین ویو را می‌نویسیم، و نتیجه را در Browsable API می‌بینیم.