Menu
CoddyTech

פרויקט: ניקוי משימות שהושלמו

חלק מהיחידה ממשקי API של JSON במסלול ה-Django של Coddy. שיעור 16 מתוך 19.

משימות שהושלמו מצטברות, ולכן כלי המעקב צריך דרך למחוק את כולן בבת אחת. המחיקה פועלת על משימות רבות בבת אחת, ולא על משימה אחת, ולכן יש לה כתובת URL משלה, ומכיוון שהיא משנה נתונים היא מקבלת רק POST: דפדפנים, תצוגות מקדימות של קישורים ומנועי חיפוש שולחים בקשות GET בלי לשאול אף אחד, לכן בקשת GET לעולם לא צריכה למחוק.

פרט אחד חדש. המתודה delete() של QuerySet מחזירה זוג: מספר השורות שנמחקו ומילון של ספירות לפי מודל:

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

את שאר הדברים כבר בנית בעבר: תצוגה, הנתיב שלה, שני דקורטורים ותגובה בפורמט JSON.

challenge icon

אתגר

קשה

הוסף נקודת קצה שמוחקת את כל המשימות שהושלמו.

  1. בקובץ tasks/views.py, הוסף תצוגה בשם clear_done. היא מוחקת את כל המשימות שהושלמו ומחזירה {"deleted": 1}, כלומר את מספר המשימות שנמחקו. כל שיטה מלבד POST מקבלת 405, והנקודה פועלת ללא אסימון CSRF.
  2. בקובץ tasks/urls.py, הוסף את הנתיב "api/tasks/clear-done/" בשם clear_done.

הרץ את השורה למטה בלשונית Shell: היא מדפיסה {'deleted': 1}, כי Call the bank הייתה המשימה היחידה שהושלמה.

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)

כל השיעורים ביחידה ממשקי API של JSON

תרגלו בעצמכם: Django אונליין