صنف واحد لكل مورد
جزء من قسم واجهات API بصيغة JSON في رحلة Django على Coddy. الدرس 11 من 19.
أصبح task_detail الآن سلسلة من كتل if request.method == ... بالإضافة إلى قائمة بالطرق التي يجب إبقاؤها متزامنة معها. تتولى الفئة الأساسية View في Django، التي تقع أسفل كل عرض عام استخدمته، تسجيل هذه المعلومات.
أضف إلى الفئة طريقة واحدة لكل طريقة HTTP، وسمِّها بحروف صغيرة. تستدعي View الطريقة التي تحمل اسم الطلب، وترد بالرمز 405 على أي طلب آخر. تصل القيم من عنوان URL بعد request، كما في عرض الدالة، ويستخدم المسار .as_view(). المورد هنا هو سمة الألوان الخاصة بالزائر، المحفوظة في الجلسة:
from django.utils.decorators import method_decorator
from django.views import View
@method_decorator(csrf_exempt, name="dispatch")
class Theme(View):
def get(self, request):
return JsonResponse({"theme": request.session.get("theme", "light")})
def put(self, request):
data = json.loads(request.body)
request.session["theme"] = data["theme"]
return JsonResponse({"theme": data["theme"]})يعمل @csrf_exempt مع الدوال. أما مع الفئة، فتضعه method_decorator على dispatch، وهي الطريقة التي يمر بها كل طلب.
التحدي
متوسطحوّل task_detail إلى صنف.
- في
tasks/views.py، استوردViewمنdjango.viewsوmethod_decoratorمنdjango.utils.decorators. - استبدل الدالة وكلا المُزخرفَين بـ
class TaskDetail(View). تستقبل الدوالgetوpatchوdeleteالوسائط(self, request, pk)، وتنفّذ كل واحدة منها عمل الفرع القديم المقابل لها. - استثنِ الصنف من CSRF بوضع
method_decorator(csrf_exempt)علىdispatch. - في
tasks/urls.py، وجّه المسار إلىviews.TaskDetail.as_view()، مع الإبقاء على اسمهtask_detail.
افتح /api/tasks/1/: ستظهر مهمة Buy milk بتنسيق JSON.
جرّب بنفسك
import json
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET, require_http_methods
from .models import Task
def task_to_dict(task):
return {
"id": task.id,
"title": task.title,
"done": task.done,
"priority": task.priority,
}
def home(request):
return render(request, "tasks/home.html", {"tasks": Task.objects.all()})
@require_GET
def stats(request):
data = {
"total": Task.objects.count(),
"done": Task.objects.filter(done=True).count(),
}
return JsonResponse(data)
@csrf_exempt
@require_http_methods(["GET", "POST"])
def task_list(request):
if request.method == "POST":
try:
data = json.loads(request.body)
except json.JSONDecodeError:
return JsonResponse({"error": "The body is not valid JSON."}, status=400)
errors = {}
title = data.get("title", "").strip()
if not title:
errors["title"] = "This field is required."
if errors:
return JsonResponse({"errors": errors}, status=400)
task = Task.objects.create(
title=title,
priority=data.get("priority", "normal"),
)
return JsonResponse(task_to_dict(task), status=201)
tasks = Task.objects.all()
done = request.GET.get("done")
if done == "true":
tasks = tasks.filter(done=True)
elif done == "false":
tasks = tasks.filter(done=False)
return JsonResponse([task_to_dict(task) for task in tasks], safe=False)
@csrf_exempt
@require_http_methods(["GET", "PATCH", "DELETE"])
def task_detail(request, pk):
task = Task.objects.filter(pk=pk).first()
if task is None:
return JsonResponse({"error": "Task not found"}, status=404)
if request.method == "DELETE":
task.delete()
return HttpResponse(status=204)
if request.method == "PATCH":
data = json.loads(request.body)
if "title" in data:
task.title = data["title"]
if "done" in data:
task.done = data["done"]
task.save()
return JsonResponse(task_to_dict(task))
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس واجهات API بصيغة JSON
تدرّب بنفسك: Playground لـ Django