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. Diese Aktion ist nicht die Ressource einer einzelnen Aufgabe, also bekommt sie eine eigene URL. Da sie Daten verändert, akzeptiert sie nur POST: Browser, Linkvorschauen und Suchmaschinen senden GET-Anfragen, ohne jemanden zu fragen, deshalb darf eine GET-Anfrage niemals etwas löschen.
Ein Detail ist neu. Die Methode delete() eines QuerySets gibt ein Wertepaar zurück: die Anzahl der gelöschten Zeilen und ein Wörterbuch mit den Anzahlen pro Modell:
>>> Book.objects.filter(year__lt=1900).delete()
(2, {'books.Book': 2})Den Rest hast du schon einmal erstellt: eine View, ihre Route, zwei Dekoratoren und eine JSON-Antwort.
Aufgabe
SchwerFüge POST /api/tasks/clear-done/ hinzu, mit der Routennamen clear_done:
- Sie löscht alle erledigten Aufgaben und gibt
{"deleted": 1}zurück, die Anzahl der gelöschten Aufgaben. - Bei jeder anderen Methode wird 405 zurückgegeben.
- Sie funktioniert ohne CSRF-Token.
Drücke auf „Run“ und gib dann im Tab „Shell“ Folgendes ein: 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
2Anfragen lesen
Mit einer Query filternMit POST erstellen5Projekt: Arbeit sortieren
Projekt: PrioritätenProjekt: Erledigte Aufgaben löschenÜbe selbstständig: Django-Playground