Menu
CoddyTech

Projekt: Erledigte Aufgaben löschen

Teil des Abschnitts JSON-APIs der Django-Journey von Coddy. Lektion 16 von 19.

Erledigte Aufgaben häufen sich, deshalb braucht der Tracker eine Möglichkeit, sie alle auf einmal zu löschen. Das Löschen betrifft viele Aufgaben auf einmal, nicht nur eine einzelne Aufgabe, daher bekommt es eine eigene URL. Da dabei Daten geändert werden, akzeptiert es nur POST: Browser, Linkvorschauen und Suchmaschinen senden GET-Anfragen, ohne jemanden zu fragen, daher darf ein GET niemals etwas löschen.

Ein Detail ist neu. Das delete() eines QuerySets gibt ein Paar zurück: die Anzahl der gelöschten Zeilen und ein Wörterbuch mit den Zählwerten pro Modell:

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

Den Rest hast du schon früher erstellt: eine Ansicht, ihre Route, zwei Decorators und eine JSON-Antwort.

challenge icon

Aufgabe

Schwer

Füge einen Endpunkt hinzu, der alle erledigten Aufgaben löscht.

  1. Füge in tasks/views.py eine View clear_done hinzu. Sie löscht alle erledigten Aufgaben und gibt {"deleted": 1} zurück, also die Anzahl der gelöschten Aufgaben. Bei jeder Methode außer POST wird 405 zurückgegeben, und der Endpunkt funktioniert ohne CSRF-Token.
  2. Füge in tasks/urls.py die Route "api/tasks/clear-done/" mit dem Namen clear_done hinzu.

Führe die folgende Zeile im Tab „Shell“ aus: Sie gibt {'deleted': 1} aus, da „Call the bank“ die einzige erledigte Aufgabe war.

from django.test import Client; print(Client().post("/api/tasks/clear-done/").json())

Probier es selbst

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)

Alle Lektionen in JSON-APIs

Übe selbstständig: Django-Playground