آموزش پایتون

ساخت API در جنگو با استفاده از Django Rest Framework

فهرست مطالب:

ایجاد پروژه جنگو

در این مثال ما یک پروژه به نام myApi ساخته ایم که داخل آن یک اپ به نام post قرار داده شده. حالا ما میخواهیم مطالب نوشته شده در اپ posts رو بصورت API صادر کنیم!

اگر با نحوه ساخت یک پروژه جنگو آشنایی ندارید، ابتدا این مطلب را بخوانید.

بعد از یادگیری نحوه ایجاد پروژه جنگو، یک پروژه بسازید و در آن یک اپ جدید برای ایجاد API ایجاد کنید.

اگر با نحوه ساخت اپ جدید در پروژه جنگو آشنایی ندارید ابتدا این مطلب را بخوانید و بعد از ساخت اپ جدید، بقیه مراحل را بخوانید.

نصب Django Rest Framework در venv پروژه

در حالی که venv پروژه در ترمینال فعال است، با استفاده از دستور زیر، Django Rest Framework رو نصب کنید:

pip install djangorestframework

داکیومنت های سایت این فریمورک هم میتونه به شما کمک کنه.

شناساندن DRF به پروژه جنگو

ما از اینجا به بعد، و با هدف کمتر شدن استهلاک کیبورد عزیزمان (هنوز کیبورد مکانیکی نخریدم. اونو بخرم که دیگه هیچی براتون تایپ نمی کنم!) Django Rest Framework رو به اختصار DRF صدا میزنیم.

بعد از نصب DRF، باید اون رو به پروژه خودمون معرفی کنیم.

برای معرفی به پروژه، به فایل settings.py در پروژه اصلی میریم و در قسمت INSTALLED_APPS اون رو اضافه می کنیم.

در قسمت اول که اپ posts رو ساختیم هم اون رو به همینجای همین فایل اضافه کردیم که اون رو هم در تکه کد زیر مشاهده می کنید:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts',
    'rest_framework',
]

ساخت کلاسهای مدل دیتابیس

در این قسمت در اپ post که ساختیم به فایل models.py میریم تا کلاس اصلی اپ رو – که model ما هست – ایجاد کنیم.

جنگو از روی این کلاس model، یک جدول در دیتابیس میسازه و فیلدهای این کلاس بعنوان ستونهای اون جدول تعریف میشن.

from django.db import models
from django.contrib.auth.models import User

class Post(models.Model):
    title = models.CharField(max_length=200)
    url = models.URLField()
    poster = models.ForeignKey(User, on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add= True)

    class Meta:
        ordering = ['-created']

داخل کلاس اصلی، یک کلاس Meta هم تعریف میشه که در DRF ازش بعنوان تنظیمات کلاس اصلی استفاده میشه. در اینجا ما داخل کلاس Meta ترتیب سریالایز کردن رکوردهای جدول دیتابیس رو مشخص کردیم که بر اساس تاریخ و نزولی باشه.

DRF از این ترتیب بعنوان ترتیب درج رکوردها در جیسون استفاده می کنه و جدیدترین ها رو بعنوان اولین آبجکت های جیسون درج می کنه.

serializer چیست و چرا‌؟

serializer در DRF به معنی یک میان افزار (به انگلیسی: middleware) هست که وظیفه اش تبدیل داده های اپلیکیشن به JSON هست.

یعنی دیتایی که از دیتابیس میگیریم رو میدیم به سریالایزر، و اون زحمت میکشه و JSON اش رو درست میکنه و میده بهمون.

ایجاد serializers.py در اپ

ابتدا در اپ posts یک فایل به نام serializers.py ایجاد می کنیم. (این نام برای این فایل اجباری نیست. ولی رسم و عرف بر این اسم هست)

در این فایل ما باید یک کلاس ایجاد کنیم که یک model و فیلد های آن را بگیرد و آنها را سریالایز کند.

اطلاعات مربوط به کلاس model و فیلد های آن را داخل کلاس Meta ی کلاس serializer می نویسیم:

from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'url', 'poster', 'created']

دقت کنید که باید کلاس serializers از rest_framework و همچنین کلاس model که در فایل model.py ایجاد کرده بودیم را داخل این فایل ایمپورت کنیم.

ایجاد کلاس در فایل views.py

در این مرحله باید یک کلاس در views.py بنویسیم.

وظیفه این کلاس این است که در هنگام دریافت ریکوئست از کاربر، تمام رکوردهای دیتابیس را بخواند و با استفاده از کلاس سریالایزری که در مرحله قبل ساختیم، این اطلاعات را سریالایز (تبدیل به جیسون) نماید.

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

from django.shortcuts import render
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer


class PostList(generics.ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

دقت کنید که باید کلاس اصلی model (که در اینجا Post میباشد) و کلاس generics از rest_framework و کلاس سریالایزری که در مرحله قبل ایجاد کرده بودیم را ایمپورت کنیم.

استفاده از متد ListAPIView سبب نمایش api به کاربر میشود. درحالی که استفاده از متد ListCreateAPIVew یک فرم ایجاد می کند که کاربر از طریق آن بتواند یک رکورد دیتا وارد کرده و در دیتابیس ذخیره نماید!

این مورد را در بخش مربوطه به طور کامل آموزش دادم که در ادامه مطلب خواهید دید.

ایجاد مسیر api در فایل urls.py و هدایت به کلاس داخل views.py

در این قسمت باید یک مسیر برای درخواستهای از نوع api مشخص کنیم، و مدیریت این درخواست رو به کلاس PostList که در مرحله قبل در فایل views.py ایجاد کرده بودیم بسپاریم.

برای این کار فایل urls.py پروژه اصلی را ویرایش می کنیم:

from django.contrib import admin
from django.urls import path
from posts import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/posts', views.PostList.as_view()),
]

دقت کنید که ما در مرحله قبل، داخل قایل views.py بجای ایجاد یک فانکشن، یک کلاس ایجاد کردیم.

اما در اینجا باید یک فانکشن را بعنوان view صددا بزنیم که کاری برای ما انجام بدهد. بنابراین از متد as_view ی کلاس مورد نظر استفاده می کنیم. (مدیریت اینکه این متد چجوری یک ویو به کاربر برگرداند بر عهده خود نرم افزار است و آن را به بهترین شکل ممکن انجام می دهد. شما نگرانش نباشید!)

ضمناً دقت کنید که فایل views از اپ مورد نظر رو در این قسمت ایمپورت کرده باشید.

در اینجا کار ساخت api برای دریافت اطلاعات به پایان رسیده است

در مرحله بعد تعدادی اطلاعات بعنوان نمونه در دیتابیس وارد می کنیم که بتوانیم api خودمان را تست کنیم .

بعداز آن هم به سراغ ساخت api برای اعتبارسنجی و لاگین کاربر و همچنین ورود اطلاعات بوسیله api توسط کاربر خواهیم رفت.

ایجاد اطلاعات نمونه در دیتابیس جهت تست API

در این مرحله باید در قسمت ادمین جنگو لاگین کنید و در قسمت posts چند پست جدید ایجاد کنید.

۱- ساخت یوزر ادمین (سوپر یوزر) جهت لاگین در قسمت ادمین جنگو

پنل ادمین جنگو با اضافه کردن اسلش ادمین ( admin/ ) به انتهای آدرس سایت جنگوی شما در دسترس خواهد بود. و برای لاگین از شما یوزرنیم و پسورد میخواد.

این یوزرنیم و پسورد رو ما در خط فرمان و در حالتی که venv فعاله و داخل فولدر روت پروژه هستیم (همونجاییکه فایل manage.py هست) با دستور زیر تعریف می کنیم:

python manage.py createsuperuser

بعد از زدن دستور فوق، از شما نام کاربر، آدرس ایمیل و پسورد رو می پرسه و تمام!

۲- مایگریت کردن اپ های پیشفرض به دیتابیس

هر وقت پروژه جدید ایجاد کردین، یا دیتابیس پروژه رو عوض کردین (مثلا از sqlite به postgre رفتین)، وقتی سرور رو با استفاده از دستور puthon manage.py runserver اجرا کنید، یک وارنینگ دریافت می کنید که نوشته شما تعدادی مایگریشن دارید که انجام نشد و برای انجام مایگریشن دستور python manage.py migrate رو باید بزنید تا این مایگریشن انجام بشه.


خب شما هم بعد از اینکه تنظیمات مربوط به اپ ها تون رو انجام دادید، همین دستور رو بزنید تا مایگریشن انجام بشه براتون:

python manage.py migrate 

۳- مایگریت کردن اپ هایی که توسط خودتون به پروژه اضافه شده

برای این کار باید بعد از اینکه اپ ها رو به جنگو شناسوندین (که چند پاراگرف بالاتر کامل توضیحش دادم)، و دیپندنسی های لازم رو هم نصب کردین (مثلا اگر توی اپ از فیلد image استفاده کردین، باید حتما pillow رو هم نصب کنید که جنگو بتونه با تصاویر کار کنه) بعدش دستور زیر رو می زنین تا مایگریشن اپ های جدید شما هم به دیتابیس انجام بشه.
در حقیقت تیبل ها و فیلدهای اپ شما، در دیتابیس ایجاد میشه:

python manage.py makemigrations 

دقت کنید هر وقت هر تغییری در کاس و یا فیلد های کلاس اپ خودتون ایجاد کردید (مثلا اسم فیلد رو عوض کردین یا فیلد جدید اضافه کردین) می بایست makemigration رو انجام بدین که تغییرات توی دیتابیس هم اتفاق بیوفته.

حالا باید یکبار دیگه مایگریت رو روی پروژه انجام بدیم که اپ مون رو بشناسه :

python manage.py migrate

۴- اضافه کردن اپ به داشبورد ادمین

بعضی از اپ ها نیازی به نمایش فیلدها و رکورد ها در بخش ادمین ندارن و در پس زمینه فعالیت می کنن (مثل اپ های محاسباتی و غیره)


اما بیشتر اپ هایی که ما می نویسیم، نیاز به مدیریت از قسمت ادمین دارن. و میخوایم نتیجه اون اپ و رکوردهایی که دراون اپ وارد شده رو به ادمین نشون بدیم و مدیریت اونها رو به ادمین بسپاریم.

مثل یه ماژول مستندات که میخوایم فیلد ها و مقادیرش در قسمت ادمین در دسترس باشه.
در حالت عادی وقتی ما اپ رو می نویسیم، به ازای اون چیزی در قسمت ادمین اضافه نمیشه.


برای اینکه اپ ما و فیلدهاش در قشمت ادمین نمایش داده بشن، باید کلاس اصلی اپ رو در فایل admin.py که در روت خود اپ قرار گرفته، رجیستر کنیم.


برای این کار، ابتدا باید این کلاس رو از فایل models.py ایمپورت کنیم. پس داخل فایل admin.py که داخل فولدر اپ هست می نویسیم:

from .models import Job 

(مثلا Job اسم کلاسمون هستش)
و بعد می نویسیم :

 admin.site.register(Job) 

و تمام! حالا در قسمت ادمین، این اپ رو خواهید دید.

۵- ورود اطلاعات در قسمت ادمین

الان اگر به قسمت ادمین سایت خودتون برید، در قسمت اپ ها، posts رو می بینید. در اینجا میتونید یک post جدید ایجاد کنید.

من پیشنهاد می کنم حداقل ۲ پست جدید ایجاد کنید که بتونید نحوه نمایش api رو به صرت کامل مشاهده کنید.

۶- تست api

خب api شما به همراه دیتای تست کاملاً آماده است. همون آدرسی که در فایل urls.py برای api تعریف کرده بودین رو در مرورگر وارد کنید تا نتیجه کار رو ببینید.

یادتون نره که runserver رو انجام بدین!

این آدرس برای این مثال، به صورت زیر خواهد بود:

http://127.0.0.1:8000/api/posts

و نتیجه هم به صورت زیر نمایش داده میشود.

django rest api test

اضافه کردن قابلیت ورود اطلاعات از طریق API

ما در فایل views.py با استفاده از متد generics.ListAPIView به DRF گفتیم که در پاسخ به رکوئست کاربر، API رو بهش نشون بده.

اما میتونیم بجای اون، با استفاده از متد generics.ListCreateAPIView قابلیت ورود اطلاعات از طریق API رو هم اضافه کنیم. به همین سادگی!

کد ما در فایل views.py به صورت زیر در میاد:

from django.shortcuts import render
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer


class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

در این حالت با رفتن به آدرس API ، می بینیم که DRF برای ما یک فرم ورود اطلاعات هم آماده کرده که میتونیم از طریق اون یک رکورد به دیتابیس اضافه کنیم:

وود اطلاعات Django Rest Framework

تشخیص کاربر و پر کردن فیلد «ارسال کننده» به صورت اتوماتیک

همه چیز تا اینجا خوب به نظر میرسه. اما یک مشکلی وجود داره. اون هم اینه که کاربر وقتی از طریق لینک API میخواد ورود اطلاعات کنه، اون دراپ داون پایین (poster) وجود نخواهد داشت که از طریقش بشه کاربر رو انتخاب کرد.

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

علاوه بر اون، اصلا چرا کاربر باید امکان تغییر کاربر واردکننده اطلاعات رو داشته باشه؟

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

برای این کار باید تغییراتی در فایلهای views.py و serializers.py ایجاد کنیم.

ابتدا در فایل views.py و دز کلاس مربوط به api ، یک متد با نام رزرو شده ی perform_create به صورت زیر ایجاد می کنیم:

class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def perform_create(self, serializer):
        serializer.save(poster=self.request.user)

این متد باعث میشه اطلاعات کاربر درخواست دهنده، بعنوان poster در serializer ذخیره بشه و توی سریالایزر بتونیم از این اطلاعات کاربر استفاده کنیم.

پس به فایل serilizers.py میریم و poster و poster_id رو به صورت فیلد readonly تعریف می کنیم. (دقت کنید که ما poster_id رو به فیلدهای کلاس Meta هم اضافه کردیم)

from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    poster = serializers.ReadOnlyField(source='poster.username')
    poster_id = serializers.ReadOnlyField(source='poster.id')
    class Meta:
        model = Post
        fields = ['id', 'title', 'url', 'poster', 'poster_id', 'created']

در این حالت وقتی به آدرس api میریم، می بینیم که دراپ داون انتخاب کاربر به طرو کلی حذف شده.

django rest framework انتخاب کاربر

حالا وقتی که ورود اطلاعات انجام میشه، خودش به صورت اتوماتیک، کاربر ایجاد کننده رو درج می کنه.

اما مشکل وقتی پیش میاد که یک کاربر لاگین نکرده میاد و با استفاده از این api ورود اطلاعات انجام میده.

چون اطلاعات کاربر وجود نداره، ارور زیر بهش نمایش داده میشه ‌:

ارور کاربر نامشخص جنگو

محدود کردن قابلیت ورود اطلاعات برای کاربر غیرلاگین

با استفاده از کلاس permissions از لایبراری DRF، این کار رو انجام میدیم.

در فایل views.py، کلاس permissions رو ایمپورت می کنیم (همونجایی که جنریکز رو ایمپورت کردیم). و داخل کلاس اصلی ویوی خودمون این عبارت رو می نویسیم:

permission_classes = [permissions.IsAuthenticated]

این عبارت تعیین میکنه که فقط کسانیکه لاگین کردن بتونن از API استفاده کنن.

کد views.py ما به صورت زیر درمیاد:

from rest_framework import generics ,permissions
from .models import Post
from .serializers import PostSerializer


class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]

    def perform_create(self, serializer):
        serializer.save(poster=self.request.user)

اگر permissions رو به این صورت تنظیم کنیم، کاربری که لاگین نکرده، نه اجازه ورود اطلاعات رو خواهد داشت، نه حتی اجازه مشاهده API رو داره.

این برای محتواهایی مناسبه، که میخوایم کاربر برای دیدنش حتما لاگین کرده باشه.

اما ممکنه بخوایم API ما به طور عمومی در دسترس باشه، اما فقط کاربر لاگین بتونه ورود اطلاعات توش انجام بده. در این حالت، permissions رو به صورت زیر تنظیم می کنیم:

permission_classes = [permissions.IsAuthenticatedOrReadOnly]

قابلیت لاگین کردن کاربر از طریق API

برای این کار کافیست یک مسیر دلخواه در فایل urls.py اضافه کنید و مقدار rest_frameawork.urls رو داخلش include کنید.

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/posts', views.PostList.as_view()),
    path('api/posts/<int:pk>/vote', views.VoteCreate.as_view()),
    path('api-auth/' ,include('rest_framework.urls'))
]

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

و با کلیک روی اون،‌به صفحه لاگین وارد خواهید شد.

رای دادی (لایک کردن) یک پست (یا مطلب یا محتوا) توسط کاربر در DRF

نوشتن یک کلاس برای vote یا لایک در models.py

برای اینکه بحث نرمال سازی پایگاه داده رو رعایت کرده باشیم، نباید لایک ها رو در جدول کاربر و یا جدول پست ها ذخیره کنیم.

بلکه یک جدول جدید به نام vote در دیتابیس میسازیم که دوتا فیلد داره: کاربر و پست.

وجود یک رکورد کاربر-پست به معنی اینه که اون کاربر، اون پست رو لایک کرده.

برای اینکه جنگو این جدول رو در دیتابیس ایجاد کنه، باید کلاس Model متناظرش رو در فایل models.py ایجاد کنیم.

class Vote(models.Model):
    voter = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)

نوشتن یک serializer برای کلاس vote در فایل serializers.py

علاوه بر اون، برای کار با API ، باید یک کلاس برای سریالایز کردن این کلاس Model هم در فایل serializers.py بنویسیم.

از اونجایی که کلاس vote کلا بجز id فیلد داخلی دیگه ای نداره، فقط همون id رو به عنوان فیلد در سریالایزرش تعریف می کنیم. (یوزر و پست هردو foreign key هستن)

class VoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Vote
        fields = ['id']

ایجاد یک مسیر برای درخواست ثبت vote در فایل urls.py

برنامه باید به یک طریقی متوجه بشه که کاربر قصد لایک کردن پست رو داره.

برای همین ما از یک فرمت path برای تشخیص درخواست لایک کاربر استفاده می کنیم.

نوع فرمت این مسیر دلخواه هست و خودتون میتونید اون رو یک مدل دیگه پیاده سازی کنید.

ما این مسیر رو به شکل api/posts/[post id]/vote در نظر می گیریم.

یعنی وقتی بعد از شماره id پست، کلمه vote میاد، یعنی کاربر درخواست لایک کردن اون پست رو داده.

و اون post id رو به صورت زیر می نویسیم که جنگو هم حالیش بشه قضیه رو:‌

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/posts', views.PostList.as_view()),
    path('api/posts/<int:pk>/vote', views.VoteCreate.as_view()),
]

و مدیریت این path (و در حقیقت مدیریت درخواست های لایک) رو به کلاس VoteCreate از فایل views.py سپردیم.

البته هنوز این کلاس رو نساختیم و در مرحله بعد میریم سراغ ایجاد این کلاس.

نکته: اون pk که جلوی int نوشتیم، اسم ماشین یا آدامس نیست، بلکه اسم متغیر رو گذاشتیم pk (مثلا مخفف primary key) هستش و از این متغیر در فایل views.py برای تشخیص پست لایک شده استفاده خواهیم کرد.

ایجاد یک کلاس برای مدیریت درخواست لایک، در فایل views.py

class VoteCreate(generics.CreateAPIView):
    serializer_class = VoteSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        post = Post.objects.get(pk=self.kwargs['pk'])
        return Vote.objects.filter(voter = user, post = post)
    
    def perform_create(self, serializer):
        serializer.save(voter = self.request.user , post = Post.objects.get(pk=self.kwargs['pk']))

یک کلاس با نام VoteCreate میسازیم و چون فقط میخوایم اطلاعات مورد نظر رو ایجاد کنه، generics.CreateAPIView را به آن پاس می دهیم.

سریالایزری که برایش ایجاد کرده بودیم رو بهش معرفی می کنیم.

و permission اون رو بر روی IsAuthenticated قرار میدیم که فقط کاربر لاگین بتونه ازش استفاده کنه.

برای query set اش هم یک متد می نویسیم که بر اساس pk دریافتی از url و همچنین نام کاربری کاربر، روی جدول vote فیلتر انجام بده و نتیجه رو برگردونه.

یک متد perform_create هم ایجاد می کنیم که کار نوشتن اطلاعات در دیتابیس و ذخیره رو انجام بده . و کاربر رو بعنوان فیلد voter و پستی که id آن از طریق url دریافت شده رو بعنوان post در جدول vote ذخیره می کنه.

در این مرحله اگر آدرس url مورد نظر رو (http://127.0.0.1:8000/api/posts/1/vote) در مرورگر وارد کنید، باید ببینید که نوشته get امکانپذیر نیست، اما اون پایین که دکمه post برای شما گذاشته که با کلیک کردنش، پست رو لایک خواهد کرد.

django rest framework upvote

و وقتی روی post کلیک می کنیم، لایک رو انجام میده و نتیجه به صورت زیر درمیاد:

django framework upvoted

جلو گیری از لایک کردن به دفعات (هر کاربر فقط یکبار یک پست را بتواند لایک کند)

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

برای همین ما هربار که درخواست لایک فرستاده شد، چک می کنیم که لایک این کاربر برای این پست در جدول vote وجود داره یا خیر.

و اگر وجود داشته باشه اجازه ثبت نمیده (یک سناریو هم میتونید در نظر بگیرید که اگر این لایک وجود داشته باشه،‌با کلیک مجدد بتونه اون رو پس بگیره)

پس در متد perform_create و قبل از اینکه دیتا ذخیره بشه، چک می کنیم که اگر query_set ما وجود داره، یک ارور از نوع validation به کاربر برگردونه.

پس در فایل views.py :

from rest_framework.exceptions import ValidationError

...

class VoteCreate(generics.CreateAPIView):
    serializer_class = VoteSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        post = Post.objects.get(pk=self.kwargs['pk'])
        return Vote.objects.filter(voter = user, post = post)

    def perform_create(self, serializer):
        if self.get_queryset().exists():
            raise ValidationError('you already voted for this post')
        serializer.save(voter = self.request.user , post = Post.objects.get(pk=self.kwargs['pk']))

و ارور رو اینشکلی بر می گردونه:

Django rest framework vote

نمایش تعداد لایک ها در خروجی api

برای این کار باید کاری کنیم که کلاس PostSerializer بتونه تعداد لایک های هر پست رو محاسبه کنه و نمایش بده.

و برای اینکه این کلاس این قابلیت رو داشته باشه، باید یک متد داخلش ایجاد کنیم.

همچنین باید یک متغیر هم ایجاد کنیم و بواسطه اون به سریالایزر بگیم که اطلاعات مربوط به این متغیر رو باید از اون متد بخونه. (لازمه که حتماً به صورتی که خواهید دید، نام متغیر داخل نام متد درج شده باشه)

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

نتیجه کار به این صورت خواهد بود:

from rest_framework import serializers
from .models import Post, Vote

class PostSerializer(serializers.ModelSerializer):
    poster = serializers.ReadOnlyField(source='poster.username')
    poster_id = serializers.ReadOnlyField(source='poster.id')
    votes = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ['id', 'title', 'url', 'poster', 'poster_id', 'created', 'votes']

    def get_votes(self,post):
        # این post همونیه که در فایل views.py در get_queryset تعریف شده
        return Vote.objects.filter(post=post).count()

class VoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Vote
        fields = ['id']
DRF votes show

ایجاد قابلیت پاک کردن لایک توسط خود کاربر از طریق api

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

در مرحله اول، mixins و ststus رو از لایبراری rest_framework و همینطور response رو داخل فایل views.py ایمپورت می کنیم.

from rest_framework import generics ,permissions, mixins, status
from rest_framework.response import Response

و mixins.DestroyModelMixin رو به ورودی کلاس VoteCreate اضافه می کنیم که بتونیم ازش داخل این کلاس استفاده کنیم.

class VoteCreate(generics.CreateAPIView, mixins.DestroyModelMixin):

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

    def delete(self, requests, *args, **kwargs):
        if self.get_queryset().exists():
            self.get_queryset().delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            raise ValidationError('You never voted for this post!')

و حالا در کمال تعجب می بینیم که در آدرسی که به vote ختم میشه، دکمه delete هم اضافه شده!!

این هم از معجزات DRF !

Django vote delete

حامد عسکریان

برنامه نویس و عاشق تکنولوژی

دیدگاهتان را بنویسید

دکمه بازگشت به بالا
بستن
بستن