Menu
CoddyTech

Proyecto: Borrar las tareas completadas

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

Las tareas completadas se acumulan, así que el rastreador necesita una forma de borrarlas todas de una vez. Esa acción no es un recurso de una tarea, por lo que tiene su propia URL y, como modifica datos, solo acepta POST: los navegadores, las vistas previas de enlaces y los motores de búsqueda envían solicitudes GET sin pedir permiso, así que una solicitud GET nunca debe borrar nada.

Hay un detalle nuevo. El método delete() de un QuerySet devuelve un par: la cantidad de filas eliminadas y un diccionario con los recuentos por modelo:

>>> Book.objects.filter(year__lt=1900).delete()
(2, {'books.Book': 2})

El resto ya lo has creado antes: una vista, su ruta, dos decoradores y una respuesta JSON.

challenge icon

Desafío

Difícil

Añade POST /api/tasks/clear-done/, con la ruta llamada clear_done:

  • Elimina todas las tareas completadas y responde {"deleted": 1}, el número de tareas eliminadas.
  • Cualquier otro método responde con el código 405.
  • Funciona sin un token CSRF.

Pulsa Run y, después, en la pestaña Shell: from django.test import Client; print(Client().post("/api/tasks/clear-done/").json())

Pruébalo tú mismo

import json

from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, render
from django.utils.decorators import method_decorator
from django.views import View
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")


def task_page(request):
    return render(request, "tasks/list.html", {"tasks": Task.objects.all()})


def task_row(request, pk):
    task = get_object_or_404(Task, pk=pk)
    return render(request, "tasks/list.html#task-row", {"task": task})


@require_GET
def stats(request):
    total = Task.objects.count()
    done = Task.objects.filter(done=True).count()
    data = {
        "total": total,
        "done": done,
        "open": total - done,
    }
    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."
        priority = data.get("priority", "normal")
        if priority not in Task.Priority.values:
            errors["priority"] = "Choose low, normal or high."
        if errors:
            return JsonResponse({"errors": errors}, status=400)
        task = Task.objects.create(
            title=title,
            priority=priority,
        )
        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)
    priority = request.GET.get("priority")
    if priority:
        tasks = tasks.filter(priority=priority)
    return JsonResponse([task_to_dict(task) for task in tasks], safe=False)


def not_found():
    return JsonResponse({"error": "Task not found"}, status=404)


@method_decorator(csrf_exempt, name="dispatch")
class TaskDetail(View):
    def get(self, request, pk):
        task = Task.objects.filter(pk=pk).first()
        if task is None:
            return not_found()
        return JsonResponse(task_to_dict(task))

    def patch(self, request, pk):
        task = Task.objects.filter(pk=pk).first()
        if task is None:
            return not_found()
        data = json.loads(request.body)
        if "title" in data:
            task.title = data["title"]
        if "done" in data:
            task.done = data["done"]
        task.save()
        return JsonResponse(task_to_dict(task))

    def delete(self, request, pk):
        task = Task.objects.filter(pk=pk).first()
        if task is None:
            return not_found()
        task.delete()
        return HttpResponse(status=204)

Todas las lecciones de API JSON

Practica por tu cuenta: Playground de Django