Django Rest Framework中的save()、create()和update()有什么不同?

24

在使用 Django REST Framework 的序列化器(serializers)创建 API 时,我对 save()、create() 和 update() 方法之间的确切区别感到困惑,请告诉我它们之间的差异。以下是我的代码示例:

View.py

class AddUser(views.APIView):
    serializer_class = UserForAdminSerializer

    def post(self, request, *args, **kwargs):

        serializer = UserForAdminSerializer(data=request.data)

        if serializer.is_valid():

            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

序列化器.py

class UserForAdminSerializer(serializers.ModelSerializer):

    first_name = serializers.CharField(max_length=30)
    last_name = serializers.CharField(max_length=30)
    name = serializers.CharField(max_length=30)
    password = serializers.CharField(max_length=20, style={'input_type': 'password'})

    class Meta:
        model = User
        fields = ('id', 'url', 'first_name', 'last_name', 'name', 'username', 'email', 'password',
                  'total_exp_year', 'total_exp_month', 'voteup_count', 'is_featured',
                  'is_active', 'headline', 'description', 'profile_picture', )

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

在view.py文件中的上述代码中,我使用了save()方法,而在serializers.py中则使用了save()或update()方法,请解释一下它们是如何工作的,并帮助我澄清save()和create()之间的区别。

2个回答

44
通常了解代码的最好方式是实际阅读它,所以让我们来看一下源代码:source
class BaseSerializer(Field):
    ...
    def update(self, instance, validated_data):
        raise NotImplementedError('`update()` must be implemented.')

    def create(self, validated_data):
        raise NotImplementedError('`create()` must be implemented.')

    def save(self, **kwargs):
        ...
        ... a lot of assertions and safety checks ...
        ... 

        validated_data = dict(
            list(self.validated_data.items()) +
            list(kwargs.items())
        )

        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            ....
        else:
            self.instance = self.create(validated_data)
            ...
        return self.instance

好的,所以在这个基类中,方法updatecreate被留给具体的子类来实现(因为序列化器,如ListSerializerModelSerializer的细节会有所不同)。

然而,save 已经实现,它基本上只是检查对象是新的还是已经存在的(if self.instance is not None),并分别调用updatecreate这段代码将会在其他任何序列化器中被调用。

让我们看一下具体的子类:

def create(self, validated_data):
    ...
    ... some stuff happening
    ...

    try:
        # Here is the important part! Creating new object!
        instance = ModelClass.objects.create(**validated_data)
    except TypeError:
        raise TypeError(msg)

    # Save many-to-many relationships after the instance is created.
    if many_to_many:
        for field_name, value in many_to_many.items():
            set_many(instance, field_name, value)

    return instance


def update(self, instance, validated_data):
    raise_errors_on_nested_writes('update', self, validated_data)
    info = model_meta.get_field_info(instance)

    # Simply set each attribute on the instance, and then save it.
    # Note that unlike `.create()` we don't need to treat many-to-many
    # relationships as being a special case. During updates we already
    # have an instance pk for the relationships to be associated with.
    for attr, value in validated_data.items():
        if attr in info.relations and info.relations[attr].to_many:
            set_many(instance, attr, value)
        else:
            setattr(instance, attr, value)
    instance.save()

    return instance

如您所见,createupdate 都调用了 set_many(instance, attr, value) 来设置对象属性的值。但是,在此之前,create 还会进行一个关键的调用:ModelClass.objects.create(**validated_data)。这实际上创建了一个新实例。
希望这能让您更加清楚。

谢谢Siegmeyer :) - Ashok Kumar
1
你能解释一下在更新定义中self和instance的区别吗? - Little Brain
2
@LittleBrain,以及未来有疑问的任何人,self实际上是Serializer的当前实例,因为instance是Serializer引用的Model的实例。因此,Serializer的update()方法实现了Serializer应如何操作模型实例。 - SinLey

10
在Django Rest Framework的文档中,他们非常清楚地解释了什么时候应该覆盖save方法和create方法。
为了方便起见,我在这里贴出他们的解释:
有些情况下,.create() 和 .update() 方法名可能不太具有意义。例如,在联系表单中,我们可能不是创建新实例,而是发送电子邮件或其他消息。在这些情况下,您可能选择直接覆盖.save(),因为这更易读、更有意义。
示例:
class ContactForm(serializers.Serializer):
    email = serializers.EmailField()
    message = serializers.CharField()

    def save(self):
        email = self.validated_data['email']
        message = self.validated_data['message']
        send_email(from=email, message=message)

感谢Arpit SVT :) - Ashok Kumar
请看一下这个链接:https://stackoverflow.com/questions/45119536/how-to-get-data-from-another-tablemodel-in-rest-api-html-form-using-django-res - Ashok Kumar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接