Проект: Очистка выполненных задач
Часть раздела JSON API путешествия по Django на Coddy. Урок 16 из 19.
Выполненные задачи накапливаются, поэтому трекеру нужен способ удалить их все разом. Это действие не относится к ресурсу одной задачи, поэтому для него нужен отдельный URL, а поскольку оно изменяет данные, оно принимает только POST: браузеры, предварительные просмотры ссылок и поисковые системы отправляют GET-запросы, никого не спрашивая, поэтому GET-запрос никогда не должен удалять данные.
Появилась одна новая деталь. Метод delete() у QuerySet возвращает пару значений: количество удалённых строк и словарь с количеством удалённых объектов для каждой модели:
>>> Book.objects.filter(year__lt=1900).delete()
(2, {'books.Book': 2})Остальное вы уже делали: представление, маршрут для него, два декоратора и ответ в формате JSON.
Задание
СложноДобавь POST /api/tasks/clear-done/, маршрут с именем clear_done:
- Он удаляет все выполненные задачи и возвращает
{"deleted": 1}— количество удалённых задач. - При любом другом методе возвращается ответ 405.
- Он работает без CSRF-токена.
Нажми Run, затем во вкладке Shell: from django.test import Client; print(Client().post("/api/tasks/clear-done/").json())
Попробуйте сами
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)
Все уроки раздела JSON API
Потренируйтесь самостоятельно: Песочница Django