Django – #35 – REST API cz. 10 – Nadpisywanie zachowań metod przed/po operacji zapisu/usuwania

Wprowadzenie.

Kolejną ważną rzeczą, którą należy znać, jest poznanie sposobu nadpisywania metod, które są wykonywane przed lub po operacji zapisu/usunięcia rekordu. Dzięki tej funkcjonalności jesteśmy w stanie na przykład przypisać lub nadpisać dane pole określoną wartością, możemy również z tego miejsca wysłać maila potwierdzającego jakąś czynność lub też wykonać inne działanie, które będzie wymagane przez nasze API.

  • perform_create(self, serializer)
  • perform_update(self, serializer)
  • perform_destroy(self, instance)

Informacji o powyższych funkcjach znajdziemy pod tym linkiem.

Zakres artykułu.

  • Nadpisywanie zachowań metod przed/po operacji zapisu/usuwania.

Nadpisywanie zachowań metod przed/po operacji zapisu/usuwania.

Załóżmy sobie, że mechanizm zwracania książek ma działać w taki sposób, aby data zwrotu była uzupełniana automatycznie po wysłaniu formularza dotyczącego zwrotu książki. W tym celu stwórzmy nowy Serializer, który będzie zawierał tylko jedno pole ‘id’ z modelu Borrow

class BorrowReturnBookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Borrow
        fields = ['id']

Następnie zaimportujmy nowo napisany serializer oraz jednocześnie zaimportujmy wbudowany moduł odpowiedzialny za działania na czasie i dacie from django.utils import timezone

from django.db.models import query
from django.shortcuts import render
from rest_framework import generics, serializers
import rest_framework
from rest_framework import permissions
from .models import Author, Book, Borrow
from .serializers import AuthorSerializer, BookSerializer, BorrowSerializer, BorrowReturnBookSerializer # new
from rest_framework.permissions import IsAuthenticated, BasePermission, SAFE_METHODS, IsAdminUser, IsAuthenticatedOrReadOnly
from rest_framework.exceptions import ValidationError
from django.utils import timezone # new

Napiszmy teraz klasę widoku odpowiedzialną za zwracanie książek. Wprowadźmy ograniczenie dostępu tylko dla autoryzowanych osób oraz nadpiszmy klasę put(), w której dodamy filtr, abyśmy mogli tylko zwrócić tylko nasze pozycje. Nowością w tym miejscu jest nadpisanie klasy perform_update(), w której to zamieścimy wyrażenie odpowiedzialne za przypisanie aktualnej daty do pola instancji return_date serializera BorrowReturnBookSerializer. Następnie musimy pamiętać o koniecznym wywołaniu metody zapisu save().

# new
class BorrowReturnBookUpdate(generics.UpdateAPIView):
    queryset = Borrow.objects.all()
    serializer_class = BorrowReturnBookSerializer
    permission_class = [IsAuthenticated]

    def put(self, request, *args, **kwargs):
        borrow = Borrow.objects.filter(pk=kwargs['pk'], user_id=self.request.user)
        if borrow.exists():
            return self.update(request, *args, **kwargs)
        else:
            raise ValidationError('You are not the owner')

    def perform_update(self, serializer):
        serializer.instance.return_date = timezone.now()
        serializer.save()

Na koniec napiszmy ścieżkę do widoku nowo stworzonej klasy path(‘api/borrows//return/’, views.BorrowReturnBookUpdate.as_view()).

from django.contrib import admin
from django.urls import path, include
from restapiapp import views

urlpatterns = [
    path('admin/', admin.site.urls),

    # API
    path('api/authors/', views.AuthorList.as_view()),
    path('api/books/', views.BookList.as_view()),
    path('api/borrows/', views.BorrowList.as_view()),
    path('api/borrows/<int:pk>/', views.BorrowRetrieveDestroy.as_view()),
    path('api/borrows/<int:pk>/return/', views.BorrowReturnBookUpdate.as_view()), # new
    path('api/borrows/<int:pk>/edit/', views.BorrowRetrieveUpdate.as_view()),

    # DRF
    path('api-auth/', include('rest_framework.urls')),
]

W tym momencie możemy przejść do przeprowadzenia testów funkcji zwrotu książek. W tym celu wprowadźmy do przeglądarki adres 127.0.0.1:8000/api/borrows/.

Załóżmy, że chcemy oddać pozycję o id 11, wówczas w przeglądarce musimy wprowadzić adres 127.0.0.1:8000/api/borrows/11/return/.

Jak widźmy w miejscu formularza znajduje się jedynie przycisk PUT, który jest odpowiedzialny za wysłanie formularza metodą PUT

Po wysłaniu formularza otrzymamy dane zwrotne z numerem id, na której została wykonana metoda PUT. Następnie wprowadźmy adres z listą wypożyczeń.

Jak widzimy data zwrotu do pozycji z id 11, została wprowadzona automatycznie. 

W tym miejscu możemy jeszcze sprawdzić, czy czasem nie możemy zwrócić książki, która nie należy do nas.

Wybierzmy teraz pozycję o id 2 i naciśnijmy przycisk PUT.

Jak możemy zobaczyć, otrzymaliśmy informację zwrotną, że nie jesteśmy właścicielami tego wypożyczenia. Dla pewności przejdźmy jeszcze do listy wypożyczeń. 

Jak widzimy, data zwrotu nadal ma wartość null.

Na koniec możemy jeszcze wrócić do serializera odpowiedzialnego za wypożyczenie, gdzie wprowadźmy dla porządku, abyśmy nie mogli wpisywać daty zwrotu.

class BorrowSerializer(serializers.ModelSerializer):
    book = serializers.ReadOnlyField(source='book_id.title')
    author_first_name = serializers.ReadOnlyField(source='book_id.author_id.first_name')
    author_last_name = serializers.ReadOnlyField(source='book_id.author_id.last_name')
    user_firts_name = serializers.ReadOnlyField(source='user_id.first_name')
    user_last_name = serializers.ReadOnlyField(source='user_id.last_name')
    user_email = serializers.ReadOnlyField(source='user_id.email')
    user_id = serializers.ReadOnlyField(source='user_id.id')
    return_date = serializers.ReadOnlyField() # new

    class Meta:
        model = Borrow
        fields = ['id', 'user_id', 'user_firts_name', 'user_last_name', 'user_email', 'book_id', 'book', 'author_first_name', 'author_last_name', 'borrow_date', 'return_date']

Sprawdźmy, czy teraz podczas wypożyczenia nie ma pola z datą zwrotu.

Jak możemy zauważyć, teraz gdy wypożyczamy książkę, mamy jedynie pozycję Book id, która dla przypomnienia wskazuje, którą książkę mamy zamiar wypożyczyć.

Autor artykułu
Dominik Bednarski

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.