المشروع: مسح المهام المنجزة
جزء من قسم واجهات API بصيغة JSON في رحلة Django على Coddy. الدرس 16 من 19.
تتراكم المهام المنجزة، لذا يحتاج متعقّب المهام إلى طريقة لمسحها دفعة واحدة. هذا الإجراء ليس موردًا خاصًا بمهمة واحدة، لذلك يكون له عنوان URL خاص به، وبما أنه يغيّر البيانات، فهو يقبل POST فقط: ترسل المتصفحات ومعاينات الروابط ومحركات البحث طلبات GET من دون سؤال أحد، لذا يجب ألا يحذف طلب GET أي شيء أبدًا.
هناك تفصيل جديد. تُرجع delete() الخاصة بمجموعة الاستعلامات زوجًا يتكوّن من عدد الصفوف المحذوفة وقاموسٍ بأعداد السجلات لكل نموذج:
>>> Book.objects.filter(year__lt=1900).delete()
(2, {'books.Book': 2})التحدي
صعبأضف POST /api/tasks/clear-done/، بمسار اسمه clear_done:
- يحذف كل مهمة مكتملة، ويُرجع
{"deleted": 1}، وهو عدد المهام المحذوفة. - تُرجع أي طريقة أخرى الرمز 405.
- يعمل دون رمز CSRF.
اضغط على Run، ثم في علامة تبويب Shell: from django.test import Client; print(Client().post("/api/tasks/clear-done/").json())
جرّب بنفسك
import json
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, render
from django.utils.decorators import method_decorator
from django.views import View
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")
def task_page(request):
return render(request, "tasks/list.html", {"tasks": Task.objects.all()})
def task_row(request, pk):
task = get_object_or_404(Task, pk=pk)
return render(request, "tasks/list.html#task-row", {"task": task})
@require_GET
def stats(request):
total = Task.objects.count()
done = Task.objects.filter(done=True).count()
data = {
"total": total,
"done": done,
"open": total - done,
}
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."
priority = data.get("priority", "normal")
if priority not in Task.Priority.values:
errors["priority"] = "Choose low, normal or high."
if errors:
return JsonResponse({"errors": errors}, status=400)
task = Task.objects.create(
title=title,
priority=priority,
)
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)
priority = request.GET.get("priority")
if priority:
tasks = tasks.filter(priority=priority)
return JsonResponse([task_to_dict(task) for task in tasks], safe=False)
def not_found():
return JsonResponse({"error": "Task not found"}, status=404)
@method_decorator(csrf_exempt, name="dispatch")
class TaskDetail(View):
def get(self, request, pk):
task = Task.objects.filter(pk=pk).first()
if task is None:
return not_found()
return JsonResponse(task_to_dict(task))
def patch(self, request, pk):
task = Task.objects.filter(pk=pk).first()
if task is None:
return not_found()
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))
def delete(self, request, pk):
task = Task.objects.filter(pk=pk).first()
if task is None:
return not_found()
task.delete()
return HttpResponse(status=204)
جميع دروس واجهات API بصيغة JSON
تدرّب بنفسك: Playground لـ Django