التعديل باستخدام PATCH
جزء من قسم واجهات API بصيغة JSON في رحلة Django على Coddy. الدرس 9 من 19.
يُعلِّم البرنامج المهمة على أنها منجزة بإرسال {"done": true} إلى عنوان URL الخاص بالمهمة، ولا يرسل أي شيء آخر. يمثّل عنوان URL ذلك المهمة نفسها، أي موردًا، بينما تحدد الطريقة ما الذي ينبغي فعله به: تقرأ GET المورد، وتغيّر PATCH جزءًا منه. أما PUT الشقيقة لها، فتستبدل المورد بأكمله.
لذلك، لا تغيّر دالة العرض التي تستخدم PATCH سوى الحقول الموجودة في متن الطلب. والقيمة الافتراضية هي موطن المشكلة. في واجهة API لمتجر كتب:
book.title = data.get("title", "")البرنامج الذي يرسل {"available": false} فقط يمحو العنوان. تحقّق أولًا مما إذا كان المفتاح قد أُرسل: لا تكون "title" in data مساويةً لـ True إلا عندئذٍ. عيّن قيمة كل حقل موجود، واحفظ مرة واحدة، ثم أجب بالكائن الذي تغيّر. تحتاج دالة العرض إلى @csrf_exempt وإلى الطريقة الجديدة في require_http_methods، كما هو الحال في task_list.
التحدي
متوسطاجعل البرامج تغيّر مهمة باستخدام PATCH /api/tasks/<id>/:
- تقبل
task_detailالطلبينGETوPATCH، وهي معفاة من حماية@csrf_exempt. - يعيّن PATCH قيمتي
titleوdoneفقط عندما يحتوي الطلب على هاتين القيمتين، ثم يحفظ المهمة ويردّ بها. - إذا كانت المهمة غير موجودة، فسيظل الردّ بالرمز 404.
اضغط على Run، ثم في علامة تبويب Shell: from django.test import Client; print(Client().patch("/api/tasks/1/", {"done": True}, content_type="application/json").json())
جرّب بنفسك
import json
from django.http import 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)
@require_GET
def task_detail(request, pk):
task = Task.objects.filter(pk=pk).first()
if task is None:
return JsonResponse({"error": "Task not found"}, status=404)
return JsonResponse(task_to_dict(task))
جميع دروس واجهات API بصيغة JSON
تدرّب بنفسك: Playground لـ Django