Menu
CoddyTech

Cambios con PATCH

Parte de la sección API JSON del Journey de Django de Coddy. Lección 9 de 19.

Un programa marca una tarea como completada enviando {"done": true} a la URL de la tarea, y nada más. Esa URL representa la tarea en sí, un recurso, y el método indica qué hacer con él: GET lo lee, PATCH cambia una parte. Su método hermano, PUT, reemplaza todo el recurso.

Por eso, una vista PATCH cambia solo los campos del cuerpo. Un valor predeterminado es la trampa. En una API de librería:

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

Un programa que envía solo {"available": false} borra el título. Primero comprueba si se envió la clave: "title" in data es True solo entonces. Establece cada campo que esté presente, guarda una sola vez y responde con el objeto modificado. La vista necesita @csrf_exempt y el método nuevo en require_http_methods, como tiene task_list.

challenge icon

Desafío

Intermedio

Permite que los programas cambien una tarea con PATCH /api/tasks/<id>/:

  • task_detail acepta GET y PATCH, y está marcada con @csrf_exempt.
  • PATCH establece title y done solo si están en el cuerpo de la solicitud, guarda los cambios y responde con la tarea.
  • Si falta una tarea, la respuesta sigue siendo 404.

Pulsa Ejecutar y, después, en la pestaña Shell: from django.test import Client; print(Client().patch("/api/tasks/1/", {"done": True}, content_type="application/json").json())

Pruébalo tú mismo

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

Todas las lecciones de API JSON

Practica por tu cuenta: Playground de Django