Django Rest Framework 3 Сериализаторы на немодельных объектах?

Я делаю обновление до DRF3.1.1 с версии 2.4. Я использовал собственный сериализатор для создания экземпляра объекта, а не модели.

В 2.4 это было достаточно просто сделать, потому что в сериализаторе я бы создал объект в restore_object(). В представлении я бы назвал serializer.is_valid(), а затем вытащил экземпляр объекта из сериализатора с помощью serializer.object. Тогда я мог делать все, что захочу.

С изменениями 3.x сложнее получить экземпляр из объекта, потому что методы создания и обновления должны выполнять сохранение, а "serializer.object" больше не доступен.

В качестве примера я использовал это для своего объекта "UserRegistration". Это не модель, потому что это удобный объект, который сервер анализирует и хранит данные в ряде других объектов/таблиц db.

class UserRegistration(object):
    def __init__(self, full_name, stage_name, password="", email="", locale="en_US"):
        self.full_name = full_name
        self.password = password
        self.locale = locale
        self.email = email
        self.stage_name = stage_name

Здесь связанный сериализатор DRF-2.4:

class UserRegistrationSerializer(serializers.Serializer):
    full_name = serializers.CharField(max_length=128, required=False)
    stage_name = serializers.CharField(max_length=128)
    password = serializers.CharField(max_length=128, required=False)
    locale = serializers.CharField(max_length=10, required=False)
    # use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
    email = serializers.CharField(max_length=254, required=False)

    def restore_object(self, attrs, instance=None):
        if instance is not None:
            instance.full_name = attrs.get('full_name', instance.full_name)
            instance.password = attrs.get('password', instance.password)
            instance.locale = attrs.get('locale', instance.locale)
            instance.email = attrs.get('email', instance.email)
            instance.stage_name = attrs.get('stage_name', instance.stage_name)
            return instance
        return UserRegistration(**attrs)

Тогда, на мой взгляд, я делаю что-то вроде этого:

class UserRegistration(APIView):
    throttle_classes = ()
    serializer_class = UserRegistrationSerializer

    def post(self, request, format=None):
        event_type = "user_registration"
        serializer = UserRegistrationSerializer(data=request.DATA, context={'request': request})
        try:
            if serializer.is_valid():
            user_registration = serializer.object
            # save user_registration pieces in various places...

Однако в DRF3 я serializer.object исчез. Документы говорят, что делать "валидацию" с помощью serializer.validated_data, но это только хэш, а не реальный объект. Есть ли способ получить объект?

Все это кажется более влюбленным в объекты БД, которые в данном конкретном случае именно то, чего я пытаюсь избежать.

Я просто пропустил новую концепцию DRF3?

Ответ 1

Спасибо @levi за начало ответа, но, к сожалению, это не все, поэтому я думаю, что это более полный ответ.

Я изначально спросил:

Я просто пропустил новую концепцию DRF3?

Оказывается... Да. Я был. Документы говорят о новом Single-step object creation, что заставило меня подумать, что сериализация и модель стали более тесно связанными. Эта мысль была неправильной, потому что, если вы пишете собственный собственный сериализатор, вам нужно сделать фактическое сохранение объекта (или не) в новых методах serializer.update() и serializer.create().

Я также спросил:

В 2.4 это было достаточно просто сделать, потому что в сериализаторе я бы создал объект в restore_object(). В представлении я бы назвал serializer.is_valid(), а затем вытащил экземпляр объекта из сериализатора с помощью serializer.object. Тогда я мог делать все, что захочу.

С изменениями 3.x сложнее получить экземпляр из объекта, потому что методы создания и обновления должны выполнять сохранение, а "serializer.object" больше не доступен.

Хотя нет serializer.object, который вы можете использовать для вытягивания созданного объекта после вызова serializer.is_valid(), метод serializer.save() возвращает сам объект, что в моем случае было просто отлично.

Итак, получается, что смена кода была совсем невелика. Вот мой новый код, который очень доволен DRF-3:

class UserRegistration(object):
    def __init__(self, full_name, stage_name, password="", email="", locale="en_US", notification_pref="ask"):
        self.full_name = full_name
        self.password = password
        self.locale = locale
        self.email = email
        self.stage_name = stage_name


class UserRegistrationSerializer(serializers.Serializer):
    full_name = serializers.CharField(max_length=128, required=False)
    stage_name = serializers.CharField(max_length=128)
    password = serializers.CharField(max_length=128, required=False)
    locale = serializers.CharField(max_length=10, required=False)
    # use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
    email = serializers.CharField(max_length=254, required=False)

    def update(self, instance, validated_data):
        instance.full_name = validated_data.get('full_name', instance.full_name)
        instance.password = validated_data.get('password', instance.password)
        instance.locale = validated_data.get('locale', instance.locale)
        instance.email = validated_data.get('email', instance.email)
        instance.stage_name = validated_data.get('stage_name', instance.stage_name)
        return instance

    def create(self, validated_data):
        return UserRegistration(**validated_data)

обратите внимание, что нет сохранения объекта в какой-либо БД в Сериализаторе. Я просто создаю или обновляю объект, а затем возвращаю его.

Теперь представление выглядит так:

class UserRegistration(APIView):
    throttle_classes = ()
    serializer_class = UserRegistrationSerializer

    def post(self, request, format=None):
        event_type = "user_registration"
        serializer = UserRegistrationSerializer(data=request.DATA, context={'request': request})
        try:
            if serializer.is_valid():
                user_registration = serializer.save()
                # save user_registration pieces in various places...

Я также сказал в своем оригинальном посте:

Все это кажется более влюбленным в объекты БД, которые в данном конкретном случае именно то, чего я пытаюсь избежать.

Это утверждение также было неверным, поскольку тот факт, что методы создания и обновления не должны ничего сохранять в какой-либо БД.

Одно из предостережений заключается в том, что код является функциональным, но, очевидно, я просто обволакиваю некоторые из изменений DRF2.x- > 3.x, поэтому я мог бы делать это не-DRF-способом. Если да, то кто-то, кто знает, не стесняйтесь сказать мне, как это сделать лучше.:)

Ответ 2

Да, вы можете сами получить объект с помощью DRF 3. Ваш метод update должен иметь эту подпись update(self, instance, validated_data)

Ваш сериализатор должен выглядеть следующим образом:

class UserRegistrationSerializer(serializers.Serializer):
    full_name = serializers.CharField(max_length=128, required=False)
    stage_name = serializers.CharField(max_length=128)
    password = serializers.CharField(max_length=128, required=False)
    locale = serializers.CharField(max_length=10, required=False)
    # use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
    email = serializers.CharField(max_length=254, required=False)

    def update(self, instance, validated_data):
          // here instance is the object .