Menu
CoddyTech

PATCH로 변경하기

Coddy Django 여정의 JSON API 섹션에 포함된 레슨. 19개 중 9번째.

프로그램은 작업의 URL에 {"done": true}만 보내 작업을 완료 처리합니다. 그 URL은 작업 자체인 리소스를 가리키며, 메서드는 해당 리소스에 수행할 작업을 나타냅니다. GET은 리소스를 읽고, PATCH는 리소스의 일부를 변경합니다. 이에 대응하는 PUT은 리소스 전체를 대체합니다.

따라서 PATCH 뷰는 본문에 있는 필드만 변경합니다. 기본값을 사용하는 것이 함정입니다. 서점 API에서는 다음과 같습니다.

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

{"available": false}만 보내는 프로그램은 제목을 지워 버립니다. 먼저 키가 전송되었는지 확인하세요. "title" in data는 키가 전송된 경우에만 True입니다. 존재하는 각 필드를 설정하고, 한 번만 저장한 다음, 변경된 객체를 응답으로 보내세요. 뷰에는 @csrf_exempt와 task_list에 있는 것처럼 require_http_methods에 새 메서드가 필요합니다.

challenge icon

챌린지

중급

프로그램에서 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))

JSON API의 모든 레슨

직접 연습해 보세요: Django 플레이그라운드