Menu
CoddyTech

Thay đổi với PATCH

Thuộc phần API JSON trong hành trình Django của Coddy. Bài học 9 trên 19.

Chương trình đánh dấu một nhiệm vụ là hoàn tất bằng cách gửi {"done": true} đến URL của nhiệm vụ đó, và không gửi gì khác. URL đó đại diện cho một nhiệm vụ, được gọi là tài nguyên (resource). Phương thức cho biết cần làm gì với tài nguyên: GET đọc tài nguyên, còn PATCH thay đổi một phần tài nguyên.

Vì vậy, một view PATCH chỉ thay đổi các trường trong phần thân. Hãy chú ý đến các giá trị mặc định. Trong API của một hiệu sách:

book.title = data.get("title", "")

Chương trình chỉ gửi {"available": false} sẽ xóa tiêu đề. Trước tiên, hãy kiểm tra xem khóa có được gửi hay không: "title" in data chỉ là True khi khóa đó được gửi. Hãy đặt từng trường có trong dữ liệu, lưu một lần rồi phản hồi bằng đối tượng đã thay đổi. View cần có @csrf_exempt và phương thức mới trong require_http_methods, giống như task_list.

challenge icon

Thử thách

Trung bình

Cho phép chương trình thay đổi một công việc.

  1. Trong tasks/views.py, thay @require_GET trên task_detail bằng một decorator cho phép GET và PATCH, rồi thêm @csrf_exempt phía trên.
  2. Với yêu cầu PATCH, đọc phần thân JSON, chỉ đặt title và done nếu chúng có trong đó, lưu lại rồi trả về công việc.

Chạy dòng bên dưới trong thẻ Shell: dòng này in ra công việc có 'done': True.

from django.test import Client; print(Client().patch("/api/tasks/1/", {"done": True}, content_type="application/json").json())

Tự làm thử

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))

Tất cả bài học trong API JSON

Tự luyện tập: Playground Django