Методы save(), create() и update() класса Serializer

Курс по Django: https://stepik.org/a/183363

Сериализатор WomenSerializer, который мы определили на предыдущем занятии, отвечает сейчас только за конвертацию данных в JSON-формат и обратно. Однако, хорошей практикой считается, когда здесь же в сериализаторе определяется алгоритм сохранения или изменения данных в БД. О чем речь? Смотрите, сейчас добавление новой записи происходит в методе post() представления WomenAPIView. Так вот, этот функционал лучше перенести в сериализатор. Для этого каждый класс сериализаторов имеет два специальных метода:

  • create(self, validated_data) – для добавления (создания) записи (данных);
  • update(self, instance, validated_data) – для изменения данных (записи).

Давайте для начала определим метод create() для переноса кода из метода post() класса представления в сериализатор. В классе WomenSerializer мы определяем метод create(), который должен возвращать экземпляр объекта сериализации:

class WomenSerializer(serializers.Serializer):
    ...
 
    def create(self, validated_data):
        return Women.objects.create(**validated_data)

В нашем случае – это объект модели класса Women. Причем коллекция validated_data – это словарь с проверенными данными, полученными в результате POST-запроса после выполнения метода serializer.is_valid().

Теперь нам нужно вызвать метод create(). Для этого в представлении WomenAPIView в методе post() удаляем все строчки, связанные с созданием новой записи и вместо них вызываем метод save():

class WomenAPIView(APIView):
    ...
 
    def post(self, request):
        serializer = WomenSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
 
        return Response({'post': serializer.data})

И ниже, при формировании JSON-ответа, указываем коллекцию serializer.data – данные, которые были возвращены методом create(). В результате у нас получился полноценный класс сериализатора, который автоматически преобразовывает данные и сохраняет их в БД.

Давайте проверим, как будет работать такая конструкция. Запустим тестовый веб-сервер:

python manage.py runserver

Откроем программу Postman и на адрес:

http://127.0.0.1:8000/api/v1/womenlist/

отправляем POST-запрос с неполными данными:

{
    "content": "Sergei Balakirev",
    "cat_id": 2
}

Получаем ожидаемый ответ, что поле title обязательно. Пропишем его в POST-запросе:

{
    "title": "Sergei 2",
    "content": "Sergei Balakirev",
    "cat_id": 2
}

Получаем ответ:

{"post":{"title":"Sergei 5","content":"Sergei Balakirev","time_create":"2021-12-07T10:13:08.709763+03:00","time_update":"2021-12-07T10:13:08.709763+03:00","is_published":true,"cat_id":2}}

То есть, данные были успешно добавлены в БД и наш сериализатор работает, как и было задумано.

Давайте теперь расширим функционал нашего API и добавим возможность изменять уже существующую запись в БД. Для этого в сериализаторе нужно прописать метод update(), например, следующим образом:

class WomenSerializer(serializers.Serializer):
    ...
 
    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.content = validated_data.get("content", instance.content)
        instance.time_update = validated_data.get("time_update", instance.time_update)
        instance.is_published = validated_data.get("is_published", instance.is_published)
        instance.cat_id = validated_data.get("cat_id", instance.cat_id)
        instance.save()
        return instance

В этот метод передается ссылка instance на объект, который следует изменить и словарь validated_data с проверенными данными. Далее, мы, используя ORM Django меняем локальные атрибуты объекта instance и вызываем метод save() для изменения данных в таблице БД. В конце мы должны возвратить объект instance.

Далее, нам нужно в классе представления прописать метод put() для PUT-запроса, через который и происходят изменения данных:

class WomenAPIView(APIView):
    ...
 
    def put(self, request, *args, **kwargs):
        pk = kwargs.get("pk", None)
        if not pk:
            return Response({"error": "Method PUT not allowed"})
 
        try:
            instance = Women.objects.get(pk=pk)
        except:
            return Response({"error": "Object does not exists"})
 
        serializer = WomenSerializer(data=request.data, instance=instance)
        serializer.is_valid(raise_exception=True)
        serializer.save()
 
        return Response({"post": serializer.data})

Здесь метод put() ожидает входной параметр pk – идентификатор записи, по которому она выбирается из БД, а затем, изменяется сериализатором WomenSerializer(). Так как мы в сериализаторе WomenSerializer дополнительно указали параметр instance, то при вызове метода serializer.save() будет автоматически вызываться метод update() сериализатора.

Нам осталось только прописать еще один маршрут для представления, где передавался бы параметр pk. Переходим в файл drfsite/urls.py и в коллекции urlpatterns добавляем строчку:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/womenlist/', WomenAPIView.as_view()),
    path('api/v1/womenlist/<int:pk>/', WomenAPIView.as_view()),
]

Протестируем работу метода PUT. Для этого запустим веб-сервер, перейдем в программу Postman и попробуем отправить PUT-запрос на адрес:

http://127.0.0.1:8000/api/v1/womenlist/

На выходе видим ожидаемый ответ:

{"error": "Method PUT not allowed"}

Давайте теперь попробуем изменить запись с id=5 в таблице women. Для этого нужно отправить PUT-запрос по адресу:

http://127.0.0.1:8000/api/v1/womenlist/5/

например, со следующими данными для изменения:

{
    "title": "Ariana Grande-Butera",
    "content": "Ариана Гранде-Бутера[3] (англ. Ariana Grande-Butera; род. 26 июня 1993, Бока-Ратон, Флорида, США[1][4]) — американская певица, актриса, автор песен, музыкальный продюсер[5][6], обладательница премии «Грэмми»[7].",
    "cat_id": 2
}

Видим, что никаких ошибок не произошло, запрос отработал в штатном режиме и в БД была изменена запись с id=5.

Давайте укажем несуществующий идентификатор записи, например, 50:

http://127.0.0.1:8000/api/v1/womenlist/50/

При отправке PUT-запроса мы получаем ожидаемый ответ:

{"error": "Object does not exists"}

То есть, наш сериализатор корректно работает на извлечение, добавление и изменение записей в БД.

По идее, вы можете в классе представления WomenAPIView прописать еще один метод delete() для реализации DELETE-запроса на удаление указанной записи в параметре pk:

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get("pk", None)
        if not pk:
            return Response({"error": "Method DELETE not allowed"})
 
        # здесь код для удаления записи с переданным pk
 
        return Response({"post": "delete post " + str(pk)})

Делается это достаточно просто средствами ORM Django, предлагаю реализовать это самостоятельно в качестве небольшого практического задания.

Курс по Django: https://stepik.org/a/183363