تا اینجا DRF را نصب کردهاید. تنظیمات اولیه را انجام دادهاید. حتی یک ویو تستی با APIView نوشتید و دیدید که Browsable API چطور کار میکند.
اما آن ویو فقط یک پیام ساده برمیگرداند. بدون مدل، بدون دیتابیس، بدون هیچ منطق واقعی.
وقتش رسیده که دستتان به کد واقعی API عادت کند.
در این درس، با سادهترین روش ممکن یک API مینویسیم: Function-Based View. نیازی به کلاس پیچیده نیست. نیازی به سریالایزر هم نداریم. فقط یک تابع پایتونی با یک دکوریتور خاص.
بعد از نوشتن API، یاد میگیرید چطور با Postman آن را تست کنید. Postman ابزاری است که هر توسعهدهنده بکاند باید بلد باشد. بدون آن، Testing APIها خستهکننده و وقتگیر میشود.
در انتهای این درس، شما میتوانید:
یک API با متد GET بنویسید
پارامتر از مسیر (Dynamic URL) بگیرید
Query Parameter دریافت کنید
همه را با Postman تست کنید
این درس پلی است بین مباحث تئوری و کار عملی واقعی. بعد از این درس، وارد بحث سریالایزرها میشویم. اما فعلاً تمرکز روی سادگی و درک جریان درخواست و پاسخ است.
اگر محیط جنگو و DRF شما آماده است، بیایید شروع کنیم.
تفاوت Function-Based View (FBV) با Class-Based View (CBV) در DRF
در DRF دو روش برای نوشتن ویو دارید. اولی با تابع، دومی با کلاس. در روش تابعی، یک تابع معمولی پایتون مینویسید. بالای آن یک دکوریتور به اسم @api_view اضافه میکنید. داخل تابع، بر اساس نوع درخواست (GET یا POST) تصمیم میگیرید چه پاسخی بدهید.
در روش کلاسی، یک کلاس میسازید که از APIView ارثبری میکند. داخل کلاس، متدهای get، post، put و delete را جداگانه تعریف میکنید. از نظر یادگیری، روش تابعی سادهتر است. کد کمتری دارد. تمرکز کمتری روی ساختار کلاس و ارثبری نیاز دارد. برای یک برنامهنویس تازهکار، فهمیدن تابع راحتتر از فهمیدن کلاس است. اما روش کلاسی در پروژههای بزرگ تمیزتر و قابل توسعهتر است.
چه موقع از FBV استفاده کنیم؟
سه موقعیت وجود دارد که روش تابعی انتخاب بهتری است.
اول: پروژه کوچک و ساده
اگر فقط چند اندپوینت API دارید و هر کدام منطق سادهای دارد، نوشتن کلاس اضافی به نظر زیادی میآید. یک تابع با ده خط کد کار را راه میاندازد.
دوم: مرحله یادگیری و آشنایی با DRF
وقتی تازه با DRF آشنا میشوید، بهتر است ساده شروع کنید. روش تابعی شما را زودتر به نتیجه میرساند. بعد از اطمینان از کارکرد درست، میتوانید همان منطق را به روش کلاسی بازنویسی کنید.
سوم: زمانی که نیاز به منطق شرطی ساده دارید
فرض کنید میخواهید بر اساس مقدار یک پارامتر در آدرس، پاسخ متفاوتی برگردانید. با تابع، یک if ساده کافی است. در روش کلاسی هم میشود، اما ساختار بیشتری نیاز دارد.
یک نکته مهم
روش تابعی محدودیت هم دارد. اگر میخواهید از قابلیتهای پیشرفته DRF مثل احراز هویت پیچیده، ViewSet، یا میکسینها استفاده کنید، مجبورید به سراغ روش کلاسی بروید.
اما برای اولین API شما، روش تابعی کاملاً مناسب است. چون هدف اصلی درک جریان درخواست و پاسخ است، نه پیچیدگی ساختار کلاس.
در این درس، تمام مثالها را با روش تابعی مینویسیم. بعداً در درسهای مربوط به ویوهای پیشرفته، روش کلاسی را کامل پوشش میدهیم.
آمادهسازی: ساخت یک اپلیکیشن جدید در پروژه
برای نوشتن API، بهتر است کدها را در یک اپلیکیشن جداگانه قرار دهید. این کار باعث میشود فایلها نامرتب نشوند. همچنین در آینده اگر بخواهید همین API را در پروژه دیگری استفاده کنید، به راحتی میتوانید کل اپ را منتقل کنید.
دستور python manage.py startapp myapi
ترمینال را باز کنید. مطمئن شوید محیط مجاز فعال است. در پوشه اصلی پروژه (جایی که فایل manage.py قرار دارد) این دستور را وارد کنید:
python manage.py startapp myapi
بعد از اجرا، یک پوشه جدید به اسم myapi ساخته میشود. داخل آن فایلهایی مثل views.py، models.py و urls.py (که باید خودتان بسازید) قرار دارند.
اسم myapi را میتوانید هر چیزی بگذارید. مثلاً blog_api یا shop_api. فقط سعی کنید اسم معنادار باشد. بعداً وقتی چندین اپ دارید، یادتان بماند هر کدام برای چیست.
اضافه کردن اپ به INSTALLED_APPS
ساختن پوشه اپ کافی نیست. جنگو تا وقتی نام آن را در INSTALLED_APPS نبینید، آن را بارگذاری نمیکند.
فایل settings.py پروژه را باز کنید. لیست INSTALLED_APPS را پیدا کنید. اسم اپ جدید را به آن اضافه کنید:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third party apps
'rest_framework',
# Local apps
'myapi',
]
فراموش نکنید که:
اول: بین اپهای قبلی و جدید کاما بگذارید. کاما جا بیفتد، خطای SyntaxError میگیرید.
دوم: بعد از اضافه کردن، فایل را ذخیره کنید. اگر سرور توسعه روشن است، آن را ریستارت کنید. جنگو فقط هنگام راهاندازی، لیست اپها را میخواند.
یک نکته برای ادامه
از این لحظه به بعد، هر ویو، سریالایزر یا مدلی که مینویسیم، داخل همین اپ myapi قرار میگیرد. نیازی نیست هر بار اپ جدید بسازید. این یکی برای تمام درسهای بعدی کافی است.
حالا که اپ آماده است، در زیرعنوان بعدی اولین ویو را با دکوریتور @api_view مینویسیم.
نوشتن اولین ویو با @api_view دکوریتور
در DRF برای تبدیل یک تابع معمولی به ویو API، از دکوریتور @api_view استفاده میکنید. این دکوریتور بالای تابع نوشته میشود.
کار اصلی این دکوریتور ساده است. تابع شما را میگیرد و یک ویو API استاندارد از آن میسازد. بدون این دکوریتور، تابع شما فقط یک تابع معمولی باقی میماند. جنگو نمیفهمد که قرار است به درخواستهای HTTP پاسخ دهد.
روش استفاده:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def my_view(request):
return Response({'message': 'سلام'})
دکوریتور یک آرگومان اجباری دارد: لیست متدهای مجاز. در مثال بالا فقط GET مجاز است. اگر کاربر با متد POST درخواست بفرستد، DRF خودکار خطای 405 Method Not Allowed برمیگرداند.
تفاوت آن با APIView کلاسبیس
APIView یک کلاس است. شما از آن ارثبری میکنید و متدهای get، post و غیره را در کلاس تعریف میکنید.
@api_view اما برای توابع ساده طراحی شده است.
تفاوت اصلی در چهار چیز خلاصه میشود:
حجم کد: در روش تابعی، کل ویو در یک تابع نوشته میشود. در روش کلاسی، نیاز به تعریف کلاس و چند متد دارید.
مدیریت متدها: در روش تابعی، داخل تابع با if request.method == 'GET' متدها را از هم جدا میکنید. در روش کلاسی، هر متد در یک تابع جداگانه است.
قابلیت توسعه: روش کلاسی برای پروژههای بزرگ مناسبتر است. ارثبری، میکسینها، و قابلیتهای پیشرفته فقط در کلاسها در دسترس است.
سادگی یادگیری: روش تابعی برای شروع بسیار سادهتر است. کد کمتری برای فهمیدن نیاز دارید.
برای اولین API شما، روش تابعی انتخاب درستی است.
نوشتن یک ویو GET ساده که متن خوشامدگویی برمیگرداند
حالا یک ویو ساده مینویسیم. کاری جز برگرداندن یک پیغام متنی ندارد.
فایل views.py را در اپ myapi باز کنید. کد زیر را بنویسید:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def welcome(request):
return Response({
'message': 'به آکادمی پایتون خوش آمدید',
'info': 'این اولین API شما با DRF است'
})
بیایید خط به خط ببینیم چه اتفاقی میافتد.
خط اول و دوم: کتابخانههای لازم را وارد میکنیم. api_view برای دکوریتور، Response برای ساختن پاسخ JSON.
خط سوم: دکوریتور @api_view(['GET']). یعنی این ویو فقط درخواست GET را میپذیرد.
خط چهارم: تعریف تابع به اسم welcome با پارامتر request. این شیء حاوی تمام اطلاعات درخواست است (هدرها، متد، پارامترها، و غیره).
خط پنجم تا هفتم: با Response یک دیکشنری پایتون را به JSON تبدیل میکنیم و برمیگردانیم.
نکته مهم: تابع Response خودکار کد وضعیت ۲۰۰ (OK) را تنظیم میکند. مگر اینکه شما طور دیگری مشخص کنید.
این ویو آماده است. اما هنوز به هیچ آدرسی متصل نشده. در زیرعنوان بعدی، مسیر را تعریف میکنیم و نتیجه را در مرورگر میبینیم.
تعریف مسیر (URL) برای ویو جدید
ویویی که نوشتیم تا الان فقط یک تابع پایتونی است. کاربران نمیتوانند به آن دسترسی داشته باشند. برای اینکه درخواستها به این ویو برسند، باید یک مسیر (URL) تعریف کنیم.
ساخت فایل urls.py در اپ
در اپ myapi، فایل urls.py وجود ندارد. باید خودتان آن را بسازید.
به پوشه myapi بروید. یک فایل جدید با اسم urls.py ایجاد کنید. محتوای اولیه آن را خالی بگذارید. الان کدها را اضافه میکنیم.
استفاده از path و اتصال به ویو
فایل urls.py داخل اپ myapi را باز کنید. کد زیر را بنویسید:
from django.urls import path
from . import views
urlpatterns = [
path('welcome/', views.welcome, name='welcome'),
]
بیایید ببینیم هر خط چه میکند.
خط اول: تابع path را از جنگو وارد میکند. این تابع وظیفه اتصال آدرس به ویو را دارد.
خط دوم: همه ویوهای داخل اپ را وارد میکند. views.welcome همان تابعی است که در زیرعنوان قبل نوشتیم.
خط چهارم: یک مسیر تعریف میکند. اولین آرگومان 'welcome/' آدرس نسبی است. دومین آرگومان views.welcome نام ویو. سومین آرگومان name='welcome' یک اسم اختیاری برای ارجاع بعدی در کد.
نکته مهم: آدرس حتماً باید به / ختم شود. 'welcome/' درست است. 'welcome' (بدون اسلش آخر) ممکن است خطا بدهد.
تنظیم مسیر در urls.py اصلی پروژه
حالا باید به جنگو بگوییم که هر درخواستی به /api/ رسید، آن را به اپ myapi ارجاع بدهد.
فایل urls.py اصلی پروژه (کنار settings.py) را باز کنید. کد زیر را اضافه کنید:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('myapi.urls')),
]
خط دوم: تابع include را هم وارد کردیم.
خط پنجم: هر آدرسی که با api/ شروع شود، به فایل urls.py داخل اپ myapi ارجاع داده میشود.
حالا ویو welcome از طریق آدرس زیر در دسترس است:
http://127.0.0.1:8000/api/welcome/
چرا این آدرس؟
api/ از مسیر اصلی پروژه
welcome/ از مسیر داخل اپ
جنگو این دو را به هم میچسباند و آدرس نهایی ساخته میشود.
اجرای تست
سرور را روشن کنید:
python manage.py runserver
مرورگر را باز کنید و به آدرس زیر بروید:
http://127.0.0.1:8000/api/welcome/
اگر همه چیز درست باشد، صفحه Browsable API را میبینید. در پایین صفحه، پاسخ JSON نمایش داده میشود:
{
"message": "به آکادمی پایتون خوش آمدید",
"info": "این اولین API شما با DRF است"
}
یک نکته مهم
اگر خطای 404 دیدید، یعنی مسیر اشتباه است. دو جا را چک کنید:
- اول: آدرس را درست تایپ کردهاید؟ حتماً /api/welcome/ را وارد کنید، نه چیز دیگر.
- دوم: سرور را بعد از تغییرات ریستارت کردهاید؟ جنگو فقط هنگام راهاندازی، مسیرها را میخواند.
در بخش بعدی، همین API را با Postman تست میکنیم.
معرفی و نصب Postman
وقتی یک API مینویسید، نیاز دارید قبل از اینکه فرانتاند به آن متصل شود، آن را تست کنید. مطمئن شوید خروجی درست است. ورودی را درست میگیرد. و در شرایط مختلف رفتار مناسبی دارد.
اینجاست که Postman وارد میشود.
Postman یک پلتفرم حرفهای برای تست و مدیریت APIهاست. با آن میتوانید درخواستهای GET، POST، PUT و DELETE بفرستید. هدرهای سفارشی اضافه کنید. توکن احراز هویت را در درخواست قرار دهید. و پاسخ سرور را با فرمت مرتب ببینید.
مهمترین کمکی که Postman میکند، صرفهجویی در زمان است. بدون آن، برای تست هر API باید کد تست جداگانه مینوشتید یا از خط فرمان با curl کار میکردید. با Postman، همه چیز در یک صفحه گرافیکی در دسترس است.
علاوه بر این، میتوانید درخواستها را در مجموعه (Collection) ذخیره کنید. هر بار که تغییری در کد میدهید، یک کلیک کافی است تا دوباره تست کنید. برای پروژههای بزرگ، این قابلیت فوقالعاده کاربردی است.
دانلود و نصب سریع (یا معرفی نسخه وب)
گزینه اول: نصب برنامه دسکتاپ
به وبسایت رسمی Postman بروید: postman.com
بر روی دکمه "Download the App" کلیک کنید. نسخه مناسب سیستم خود را انتخاب کنید. فایل نصبی دانلود میشود. بعد از نصب، برنامه را اجرا کنید. یک حساب کاربری رایگان بسازید یا وارد شوید.
برنامه دسکتاپ همه امکانات را دارد. محدودیت مرورگر را ندارد. میتوانید مستقیماً به localhost و APIهای روی سیستم خودتان دسترسی پیدا کنید.
گزینه دوم: نسخه وب (بدون نصب)
اگر نمیخواهید نرمافزاری نصب کنید، نسخه وب Postman گزینه خوبی است. همان آدرس postman.com را باز کنید. روی "Web Version" کلیک کنید. بدون نصب، داخل مرورگر کار میکند.
یک نکته مهم: نسخه وب به تنهایی نمیتواند به localhost یا 127.0.0.1 متصل شود. محدودیت امنیتی مرورگر اجازه نمیدهد. برای رفع این مشکل، باید یک افزونه کوچک به اسم Postman Desktop Agent را نصب کنید. این افزونه پلی بین مرورگر و کامپیوتر شما میشود.
برای این دوره بهتر است، برنامه دسکتاپ را نصب کنید. نصب آن ساده است. یک بار انجام میشود و بعداً دیگر سردرد ندارد. همچنین همه قابلیتها را در اختیار شما میگذارد.
در زیرعنوان بعدی، با همین Postman اولین درخواست GET را به APIای که نوشتیم میفرستیم.
تست API با Postman
حالا که Postman را نصب کردهاید، وقت آن رسیده که API خود را با آن تست کنید.
ساخت یک درخواست GET جدید در Postman
برنامه Postman را باز کنید.
اگر برای اولین بار است وارد میشوید، صفحه اصلی را میبینید. در بخش سمت چپ، برگه "Workspace" را انتخاب کنید. روی دکمه آبی رنگ "New" کلیک کنید. از منوی باز شده، گزینه "HTTP Request" را انتخاب کنید.
یک تب جدید باز میشود.
در این تب، چند بخش مهم میبینید:
یک کادر کشویی در سمت چپ. مقدار پیشفرض آن "GET" است. همین را نگه میداریم.
یک کادر متنی بزرگ در کنار آن. اینجا باید آدرس API را وارد کنید.
یک دکمه آبی رنگ با عنوان "Send" در سمت راست.
برای درخواست GET، نیازی به تغییر چیز دیگری نیست. هدرها و بدنه خالی میمانند.
وارد کردن آدرس و ارسال درخواست
در کادر متنی کنار "GET"، آدرس کامل API خود را وارد کنید:
http://127.0.0.1:8000/api/welcome/
دقت کنید که سرور جنگو باید در حال اجرا باشد. اگر سرور را قبلاً روشن کردهاید، همان را نگه دارید. اگر خاموش است، در ترمینال دستور python manage.py runserver را بزنید.
حالا روی دکمه آبی "Send" کلیک کنید.
Postman درخواست را به سرور شما میفرستد. معمولاً کمتر از یک ثانیه طول میکشد.
مشاهده پاسخ JSON
بعد از کلیک روی Send، بخش پایینی صفحه پر میشود. اینجا پاسخ سرور را میبینید.
در سمت راست، چند برگه وجود دارد. برگه "Body" را انتخاب کنید. داخل آن، سه گزینه برای نمایش پاسخ میبینید: "Pretty"، "Raw"، "Preview".
گزینه "Pretty" را انتخاب کنید. Postman پاسخ را با فرمت مرتب و رنگی نشان میدهد. باید چیزی شبیه این ببینید:
{
"message": "به آکادمی پایتون خوش آمدید",
"info": "این اولین API شما با DRF است"
}
**
در بالای همین بخش، کد وضعیت را هم میبینید. جایی نوشته شده "Status: 200 OK". یعنی درخواست موفق بوده است.
در سمت چپ کد وضعیت، زمان پاسخ و حجم تقریبی پاسخ هم نمایش داده میشود.
مقایسه با Browsable API
در مرورگر، شما صفحه HTML همراه با دکمه و توضیحات میدیدید. در Postman، فقط داده خالص JSON را میبینید. هیچ فرمت اضافهای ندارد. این همان چیزی است که فرانتاند واقعاً دریافت میکند.
Postman به شما نشان میدهد که API شما در دنیای واقعی چطور رفتار میکند. بدون هیچ واسطهای.
نکته مهم
اگر خطای "Could not get any response" دیدید، یعنی Postman نمیتواند به سرور شما متصل شود. دو علت اصلی دارد:
اول: سرور جنگو روشن نیست. در ترمینال آن را اجرا کنید.
دوم: اگر از نسخه وب Postman استفاده میکنید، باید Postman Desktop Agent را نصب کرده باشید. در غیر این صورت به localhost دسترسی ندارد.
در زیرعنوان بعدی، یک API پیشرفتهتر مینویسیم که پارامتر از مسیر دریافت میکند.
نوشتن یک API با قابلیت دریافت پارامتر از مسیر (Dynamic URL)
API قبلی ما یک پیام ثابت برگشت میداد. در دنیای واقعی، APIها باید بر اساس ورودی کاربر پاسخ متفاوت بدهند. یکی از روشهای ارسال ورودی، استفاده از پارامتر در خود آدرس است.
مثال: api/greet/<name>/
فرض کنید میخواهیم یک API بسازیم که اسم کاربر را از آدرس بگیرد و به او خوشامد بگوید. آدرس به این شکل است:
http://127.0.0.1:8000/api/greet/ali/
اگر کاربر به جای ali اسم دیگری بنویسد، API همان اسم را در پاسخ نشان میدهد.
برای پیادهسازی این قابلیت، باید در تعریف مسیر، یک قسمت متغیر در نظر بگیریم. در جنگو، این کار با نوشتن <name> در آدرس انجام میشود.
خواندن پارامتر از kwargs
وقتی مسیری با پارامتر متغیر تعریف میکنید، جنگو مقدار آن را داخل kwargs (مخفف keyword arguments) به ویو میفرستد.
مراحل کار:
قدم اول: نوشتن ویو
فایل views.py را در اپ myapi باز کنید. تابع جدید زیر را اضافه کنید:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def greet(request, name):
message = f"سلام {name}، به آکادمی پایتون خوش آمدی"
return Response({'message': message})
توجه کنید که تابع greet دو پارامتر دارد: request و name. نام دوم باید دقیقاً با نام متغیری که در آدرس تعریف میکنید، یکی باشد.
قدم دوم: تعریف مسیر
فایل urls.py داخل اپ myapi را باز کنید. مسیر جدید را به urlpatterns اضافه کنید:
from django.urls import path
from . import views
urlpatterns = [
path('welcome/', views.welcome, name='welcome'),
path('greet/<name>/', views.greet, name='greet'),
]
<name> در آدرس یعنی هر مقدار اینجا قرار بگیرد، در متغیر name ذخیره میشود.
قدم سوم: تست در مرورگر
سرور را روشن کنید. آدرس زیر را در مرورگر وارد کنید:
http://127.0.0.1:8000/api/greet/sara/
پاسخ JSON را میبینید:
{
"message": "سلام sara، به آکادمی پایتون خوش آمدی"
}
حالا اسم را تغییر دهید. مثلاً reza را امتحان کنید:
http://127.0.0.1:8000/api/greet/reza/
پاسخ به همین شکل تغییر میکند.
برگرداندن پیام اختصاصی مانند Hello {name}
ویو ما از f-string برای ساخت پیام استفاده میکند. هر مقدار که در آدرس وارد شود، داخل متن قرار میگیرد.
این روش مزیت بزرگی دارد. کاربر میتواند بدون استفاده از فرم یا بدنه درخواست، مستقیماً از آدرس، داده را ارسال کند.
نکات فنی
نوع داده پارامتر همیشه رشته (string) است. اگر نیاز به عدد دارید، میتوانید در مسیر از <int:price> استفاده کنید. مثلاً api/products/<int:product_id>/. جنگو خودکار مقدار را به عدد تبدیل میکند.
همچنین میتوانید چند پارامتر داشته باشید. مثلاً api/greet/<first_name>/<last_name>/.
http://127.0.0.1:8000/api/greet/maryam/
روی Send کلیک کنید. پاسخ را مشاهده کنید. سپس اسم را تغییر دهید و دوباره بفرستید.
جمعبندی
با این تکنیک، API شما میتواند داده را از طریق آدرس دریافت کند. این روش برای مناطقی مثل پروفایل کاربران (مانند /users/123/)، جزئیات محصولات (مانند /products/42/)، و صفحات داینامیک مناسب است.
در بخش بعدی، روش دیگری برای دریافت داده را یاد میگیریم: Query Parameters.
نوشتن یک API با قابلیت دریافت داده از Query Parameters
در روش قبلی، داده را از داخل خود آدرس میخواندیم. اما گاهی نمیخواهید پارامتر بخشی از مسیر باشد. مثلاً در صفحه جستجو، کاربر عبارتی را وارد میکند و شما آن را به سرور میفرستید. برای این کار از Query Parameters استفاده میشود.
مثال: api/search/?q=python
فرض کنید میخواهید یک API جستجو بسازید. آدرس پایه آن api/search/ است. کاربر عبارت جستجوی خود را با علامت سوال و کلید q به آدرس اضافه میکند:
http://127.0.0.1:8000/api/search/?q=python
اگر کاربر به دنبال عبارت دیگری باشد، فقط مقدار q را تغییر میدهد:
http://127.0.0.1:8000/api/search/?q=django
این روش انعطاف بیشتری نسبت به پارامترهای مسیر دارد. ترتیب پارامترها مهم نیست. همچنین میتوانید چندین پارامتر را با & از هم جدا کنید.
خواندن پارامتر با request.query_params
در DRF، برای دسترسی به Query Parameters از request.query_params استفاده میکنیم. این شیء شبیه یک دیکشنری پایتون است.
مراحل کار:
قدم اول: نوشتن ویو
فایل views.py را در اپ myapi باز کنید. تابع جدید زیر را اضافه کنید:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def search(request):
query = request.query_params.get('q', '')
if not query:
return Response({
'error': 'لطفاً عبارت جستجو را با پارامتر q وارد کنید'
}, status=400)
results = [
{'id': 1, 'title': f'دوره آموزش {query}'},
{'id': 2, 'title': f'کتاب مرجع {query}'},
{'id': 3, 'title': f'ویدیوهای پیشرفته {query}'},
]
return Response({
'query': query,
'count': len(results),
'results': results
})
بیایید خط به خط ببینیم چه میشود.
خط اول و دوم: کتابخانههای لازم.
خط سوم: دکوریتور با متد GET.
خط چهارم: تابع search با پارامتر request.
خط پنجم: request.query_params.get('q', ''). این خط مقدار پارامتر q را میخواند. اگر پارامتر q وجود نداشته باشد، یک رشته خالی برمیگرداند.
خط هفتم تا دهم: اگر query خالی بود، یعنی کاربر پارامتری نفرستاده. در این حالت با کد وضعیت ۴۰۰ خطا برمیگردانیم.
خط دوازدهم تا پانزدهم: یک لیست فرضی از نتایج میسازیم. در دنیای واقعی اینجا باید از دیتابیس جستجو کنید.
خط هفدهم تا بیست و یکم: پاسخ نهایی را برمیگردانیم. شامل عبارت جستجو، تعداد نتایج، و خود نتایج.
قدم دوم: تعریف مسیر
فایل urls.py داخل اپ myapi را باز کنید. مسیر جدید را اضافه کنید:
from django.urls import path
from . import views
urlpatterns = [
path('welcome/', views.welcome, name='welcome'),
path('greet/<name>/', views.greet, name='greet'),
path('search/', views.search, name='search'),
]
قدم سوم: تست در مرورگر
سرور را روشن کنید. آدرس زیر را در مرورگر وارد کنید:
http://127.0.0.1:8000/api/search/?q=python
پاسخ JSON شبیه این میبینید:
{
"query": "python",
"count": 3,
"results": [
{"id": 1, "title": "دوره آموزش python"},
{"id": 2, "title": "کتاب مرجع python"},
{"id": 3, "title": "ویدیوهای پیشرفته python"}
]
}
حالا آدرس را تغییر دهید و q=django را امتحان کنید:
http://127.0.0.1:8000/api/search/?q=django
همان ساختار با مقدار جدید برمیگردد.
تست حالت خطا
آدرس زیر را وارد کنید (بدون پارامتر q):
http://127.0.0.1:8000/api/search/
پاسخ خطا را میبینید:
{
"error": "لطفاً عبارت جستجو را با پارامتر q وارد کنید"
}
در Postman هم کد وضعیت ۴۰۰ Bad Request را نشان میدهد.
برگرداندن نتایج فرضی
در این مثال، نتایج به صورت فرضی و از پیش تعیین شده برگردانده شد. در پروژه واقعی، به جای لیست دستی، از دیتابیس و ORM جنگو استفاده میکنید. مثلاً:
results = Product.objects.filter(name__icontains=query)
بعداً در درس فیلتر و جستجو، این کار را کامل یاد میگیرید.
تفاوت path parameters با query parameters
| ویژگی | پارامتر مسیر (Path) | پارامتر جستجو (Query) |
| مثال | /users/123/ | /search/?q=ali |
| جایگاه | بخشی از آدرس | بعد از علامت سوال |
| اجباری بودن | معمولاً اجباری است | اختیاری است |
| کاربرد | شناسایی یک منبع خاص | فیلتر، جستجو، صفحهبندی |
تمرین برای شما
یک API بنویسید که دو پارامتر start و end از Query Parameters بگیرد و بازه اعداد بین آنها را برگرداند. مثلاً /api/range/?start=5&end=10 خروجی [5,6,7,8,9,10] را نشان دهد.
در بخش بعدی، کدهای وضعیت را در Postman بررسی میکنیم.
بررسی کدهای وضعیت (Status Codes) در پاسخ Postman
هر درخواست HTTP که به سرور میفرستید، یک کد سه رقمی همراه پاسخ برمیگردد. این کد نشان میدهد درخواست شما موفق بوده یا نه. اگر خطایی رخ داده، مشکل از کجاست.
۲۰۰ OK برای موفقیت
کد ۲۰۰ یعنی همه چیز درست کار کرده است. سرور درخواست شما را فهمیده، پردازش کرده، و نتیجه را برگردانده است.
در APIهایی که تا الان نوشتیم، هر بار که آدرس درست را میفرستادید، پاسخ با کد ۲۰۰ برمیگشت.
این کد آنقدر رایج است که معمولاً به آن توجه نمیکنید. اما نبودن آن یعنی حتماً مشکلی وجود دارد.
۴۰۴ برای آدرس اشتباه
کد ۴۰۴ یعنی سرور آدرسی که شما فرستادهاید را پیدا نمیکند.
چند دلیل رایج:
- آدرس را اشتباه تایپ کردهاید. مثلاً به جای api/welcome/ نوشتهاید api/welcom/
- مسیر در فایل urls.py تعریف نشده است
- سرور در حال اجرا نیست (در این حالت اصلاً پاسخی نمیرسد، نه ۴۰۴)
برای تست، در Postman آدرس زیر را وارد کنید:
http://127.0.0.1:8000/api/wrong-address/
روی Send کلیک کنید. پاسخ ۴۰۴ را میبینید.
چطور کد وضعیت را در Postman ببینیم
روش اول: در کنار دکمه Send
بعد از ارسال درخواست، درست در کنار دکمه Send، کد وضعیت را میبینید. مثلاً نوشته شده "Status: 200 OK". رنگ آن هم سبز است.
روش دوم: در برگه Headers
در بخش پایینی Postman، برگه "Headers" را انتخاب کنید. در اینجا میتوانید هدرهای پاسخ را ببینید. یکی از این هدرها معمولاً وضعیت را نشان میدهد.
روش سوم: در نوار پایین پنجره
Postman در نوار پایین خود (کنار کلمه "Body") کد وضعیت را هم نمایش میدهد.
تمرین عملی در Postman
سه درخواست زیر را یکی یکی امتحان کنید و به کد وضعیت هر کدام توجه کنید:
۱. آدرس درست: http://127.0.0.1:8000/api/welcome/
نتیجه: ۲۰۰ OK
۲. آدرس با غلط املایی: http://127.0.0.1:8000/api/welcom/
نتیجه: ۴۰۴ Not Found
۳. آدرس با متد اشتباه (اگر در Postman به جای GET، POST را انتخاب کنید):
آدرس درست http://127.0.0.1:8000/api/welcome/ اما با متد POST
نتیجه: ۴۰۵ Method Not Allowed
چرا کدهای وضعیت مهم هستند؟
برنامهنویس فرانتاند بر اساس کد وضعیت تصمیم میگیرد چه رفتاری نشان دهد. مثلاً:
- اگر کد ۲۰۰ دید، داده را به کاربر نشان میدهد.
- اگر کد ۴۰۴ دید، پیغام "صفحه مورد نظر یافت نشد" نشان میدهد.
- اگر کد ۴۰۰ دید، به کاربر میگوید ورودی شما اشتباه است.
- اگر کد ۵۰۰ دید، یعنی خطا از سمت سرور است و باید به تیم بکاند گزارش دهد.
در درسهای بعدی، با کدهای بیشتری مثل ۲۰۱ (Created)، ۴۰۰ (Bad Request)، و ۴۰۳ (Forbidden) آشنا میشوید.
عیبیابی خطاهای رایج در اولین API
وقتی اولین API را مینویسید، دیدن خطا طبیعی است. نگران نباشید. در این بخش سه خطای بسیار رایج را با هم بررسی میکنیم.
فراموشی دکوریتور @api_view
نشانه خطا:
شما ویو را نوشتهاید، مسیر را تنظیم کردهاید، اما وقتی آدرس را در مرورگر باز میکنید، یک صفحه خطای سفید میبینید یا ارور عجیبی ظاهر میشود.
دلیل:
دکوریتور @api_view را بالای تابع ننوشتهاید. جنگو نمیداند که این تابع باید یک پاسخ API برگرداند. در نتیجه رفتار پیشفرض جنگو اجرا میشود.
کد اشتباه:
from rest_framework.response import Response
def welcome(request): # @api_view فراموش شده
return Response({'message': 'سلام'})
کد درست:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def welcome(request):
return Response({'message': 'سلام'})
راه حل:
بالای تابع خود حتماً @api_view(['GET']) یا هر متد دیگری که نیاز دارید را بنویسید.
خطای ۴۰۴ (Not Found) به دلیل مسیر اشتباه
نشانه خطا:
در مرورگر یا Postman پیام "Not Found" یا "404" میبینید.
دلایل احتمالی:
اول: آدرس را اشتباه تایپ کردهاید.
بررسی کنید که api/welcome/ را وارد کردهاید یا api/welcom/ (با حذف e).
دوم: مسیر را در urls.py اپ تعریف نکردهاید.
فایل urls.py داخل اپ myapi را باز کنید. ببینید path('welcome/', views.welcome, name='welcome') آنجا هست یا نه.
سوم: مسیر را در urls.py اصلی پروژه اضافه نکردهاید.
فایل urls.py اصلی را چک کنید. آیا path('api/', include('myapi.urls')) وجود دارد؟
چهارم: سرور را بعد از اضافه کردن مسیر ریستارت نکردهاید.
جنگو مسیرها را فقط هنگام راهاندازی میخواند. سرور را متوقف کنید (Ctrl+C) و دوباره python manage.py runserver را بزنید.
راه حل سریع:
به ترتیب سه فایل زیر را چک کنید:
myapi/views.py – ویو وجود دارد؟
myapi/urls.py – مسیر به ویو متصل شده؟
project/urls.py – مسیر api/ به اپ متصل شده؟
اگر همه درست بود، سرور را ریستارت کنید.
خطای ۵۰۰ به دلیل ارور در کد پایتون
نشانه خطا:
در مرورگر یک صفحه آبی با عنوان "Server Error (500)" میبینید. در ترمینال (محل اجرای سرور) ردپای خطا با رنگ قرمز نشان داده میشود.
دلیل:
یک جای کد شما خطای پایتونی وجود دارد. مثلاً نام متغیر را اشتباه نوشتهاید. یا از یک شیء که وجود ندارد استفاده کردهاید.
مثال خطا:
فرض کنید در ویو welcome به جای برگرداندن دیکشنری، یک اشتباه تایپی کردید:
@api_view(['GET'])
def welcome(request):
return Response({'messag': 'سلام'}) # message اشتباه نوشته شده messag
اگر messag کلید باشد، مشکلی نیست. اما اگر جای دیگری از آن استفاده کنید، خطا میدهد.
یک مثال واقعیتر:
@api_view(['GET'])
def search(request):
query = request.query_params.get('q')
results = Product.objects.filter(title__icontains=query) # Product مدل وجود ندارد
return Response({'results': results})
اگر مدل Product را تعریف نکرده باشید یا فراموش کرده باشید آن را import کنید، خطای ۵۰۰ میگیرید.
راه حل:
- گام اول: به ترمینال نگاه کنید. ردپای خطا دقیقاً نشان میدهد کدام فایل و کدام خط مشکل دارد. دنبال کلمه "Error" بگردید.
- گام دوم: خط مشخص شده را بررسی کنید. ببینید آیا متغیر یا مدلی را استفاده کردهاید که تعریف نشده؟
- گام سوم: بعد از رفع خطا، سرور خودکار کد جدید را میگیرد (مگر اینکه خطا در فایل settings.py یا هنگام راهاندازی باشد که در آن صورت باید ریستارت کنید).
یک توصیه مهم
در محیط توسعه، خطای ۵۰۰ دوست شماست. چون به شما میگوید دقیقاً کجا اشتباه کردهاید. از دیدن آن نترسید. پیغام خطا را با دقت بخوانید. اغلب خودش راه حل را نشان میدهد.
جمعبندی و آماده شدن برای سریالایزرها (درس بعدی)
در این درس، اولین API خود را با DRF نوشتید.
یاد گرفتید که تفاوت Function-Based View و Class-Based View چیست. یک اپلیکیشن جدید به اسم myapi ساختید و آن را به INSTALLED_APPS اضافه کردید.
اولین ویو را با دکوریتور @api_view نوشتید. مسیر آن را در urls.py اپ تنظیم کردید. و مسیر اصلی پروژه را به اپ متصل نمودید.
نتیجه را در Browsable API دیدید و با Postman تست کردید.
سپس یک API با پارامتر از مسیر (Dynamic URL) نوشتید که به هر اسمی پیام اختصاصی میداد. بعد از آن یک API جستجو با Query Parameters ساختید که عبارت جستجو را میگرفت و نتایج فرضی برمیگرداند.
کدهای وضعیت ۲۰۰ و ۴۰۴ را در Postman بررسی کردید. و در انتها، سه خطای رایج و راه حل هر کدام را مرور نمودید.
چه چیزهایی در FBV محدودیت دارد و چرا به سراغ سریالایزرها میرویم؟
APIهایی که در این درس نوشتید، همه کار میکنند. اما برای پروژههای واقعی کافی نیستند.
محدودیت اول: تبدیل دستی داده
در ویوهای ما، دادهها را دستی به دیکشنری تبدیل میکردیم. مثلاً برای جستجو، یک لیست از دیکشنریها ساختیم. اگر مدل شما ۲۰ فیلد داشته باشد، نوشتن این کد برای هر بار خستهکننده و خطاخیز است.
سریالایزرها این کار را خودکار میکنند. کافی است بگویید از کدام مدل استفاده کنم. خودش همه فیلدها را میخواند و به JSON تبدیل میکند.
محدودیت دوم: اعتبارسنجی دستی
در API جستجو، بررسی کردیم که اگر q وجود نداشت، خطا برگردانیم. اما برای دادههای پیچیدهتر مثل ایمیل، شماره تلفن، یا تاریخ، اعتبارسنجی دستی سخت و طولانی میشود.
سریالایزرها اعتبارسنجی خودکار دارند. میتوانید برای هر فیلد قانون مشخص کنید. مثلاً ایمیل حتماً @ داشته باشد. یا قیمت از صفر بزرگتر باشد.
محدودیت سوم: ارتباط با مدل و دیتابیس
در این درس، ما از دیتابیس استفاده نکردیم. اما در پروژه واقعی، APIها با مدلهای جنگو کار میکنند. سریالایزرها ارتباط بین درخواست JSON و مدل جنگو را خودکار میکنند. با یک خط serializer.save() داده در دیتابیس ذخیره میشود.
محدودیت چهارم: کد تکراری
برای هر مدل جدید، باید ویوهای مشابه بنویسید. یک ویو برای لیست، یک ویو برای جزئیات، یک ویو برای ساخت، و غیره.
در DRF با ViewSet و ModelViewSet میتوانید همه اینها را با چند خط کد بسازید. سریالایزرها بخش مهمی از این فرآیند هستند.
مسیر ادامه
از درس بعد، وارد بحث سریالایزرها میشویم. اولین مدل را میسازیم. سریالایزر مربوط به آن را مینویسیم. و بدون نوشتن کد تبدیل دستی، داده را به JSON تبدیل میکنیم.
شما برای درس بعد کاملاً آماده هستید. چون الان بلدید:
یک اپ بسازید
ویو بنویسید
مسیر تنظیم کنید
و API را با Postman تست کنید
تنها چیزی که کم دارید، سریالایزرهاست.