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.
Desafío
IntermedioPermite que los programas cambien una tarea con PATCH /api/tasks/<id>/:
task_detailaceptaGETyPATCH, y está marcada con@csrf_exempt.- PATCH establece
titleydonesolo 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
2Lectura de solicitudes
Filtrar con una consultaCrear con POST5Proyecto: Organizar el trabajo
Proyecto: PrioridadesProyecto: Borrar las tareas completadasPractica por tu cuenta: Playground de Django