我是DRF的新手。我读了API文档,也许这是显而易见的,但我找不到一个方便的方法来做。
我有一个Answer
对象,它与Question
有一对一的关系。
在前端,我使用POST方法创建发送到api/answers
的答案,使用PUT方法更新发送到api/answers/24
的答案
但是我想在服务器端处理它,我只发送一个POST方法到api/answers
,DRF将根据answer_id
或question_id
(因为是一对一)检查对象是否存在,如果存在,它将更新现有的对象,如果不存在,它将创建一个新的答案。
我不知道我应该在哪里实现它。我应该在序列化器或ViewSet或其他地方重写create()
吗?
下面是我的模型、序列化器和视图:
class Answer(models.Model):
question = models.OneToOneField(
Question, on_delete=models.CASCADE, related_name="answer"
)
answer = models.CharField(
max_length=1, choices=ANSWER_CHOICES, null=True, blank=True
)
class AnswerSerializer(serializers.ModelSerializer):
question = serializers.PrimaryKeyRelatedField(
many=False, queryset=Question.objects.all()
)
class Meta:
model = Answer
fields = ("id", "answer", "question")
class AnswerViewSet(ModelViewSet):
queryset = Answer.objects.all()
serializer_class = AnswerSerializer
filter_fields = ("question", "answer")
9条答案
按热度按时间avkwfej41#
很遗憾,您提供并接受的答案并没有回答您的原始问题,因为它不会更新模型。不过,这可以通过另一种方便的方法轻松实现:更新或创建
如果
question=validated_data['question']
的对象不存在,并且答案取自validated_data['answer']
,那么应该在数据库中创建一个Answer
对象,如果已经存在,django会将其answer属性设置为validated_data['answer']
。正如Nirri的回答所指出的,这个函数应该驻留在序列化器内部,如果使用通用的ListCreateView,它将在发送post请求时调用create函数,并生成相应的响应。
vlurs2pr2#
@Nirri发布的答案也帮助了我,但我发现了一个更优雅的解决方案,使用Django的QuerySet API快捷方式:
它的作用完全相同-如果
Question
的Answer
不存在,则将创建它,否则将通过question
字段查找按原样返回。但是,此快捷方式不会更新对象。QuerySet API有另一个用于
update
操作的方法,称为update_or_create
,并在线程的其他应答中发布。l2osamch3#
我将使用序列化程序的create方法。
在其中,您可以检查问题(使用您在“question”主键相关字段中提供的ID)是否已经有答案,如果有,则获取对象并更新它,否则创建一个新的。
所以第一个选项应该是这样的:
第二个选项是检查具有答案id的答案是否存在。
答案ID不会显示在发布请求的验证数据中,除非您使用某种变通方法并手动将其定义为read_only = false字段:
但是您应该重新考虑这一点,PUT方法和POST方法作为单独的实体存在是有充分理由的,您应该在前端分离请求。
x6yk4ghg4#
更一般的答案是,我认为这应该在视图集中而不是在序列化程序中,因为序列化程序只需要序列化,仅此而已。
这模拟了
update
将id从request.data
传递到kwargs
的情况,因此如果示例不存在,UpdateModelMixin.update()
将引发Http404
异常,该异常由except块捕获并调用create()
。4xy9mtcn5#
一个更好、更通用的方法是用一个潜在的示例更新ModelSerializer对象(如果它存在的话),这允许DRF遵循标准协议,并且可以很容易地跨模型进行抽象。
为了保持通用性,首先创建一个UpdateOrCreate类,在示例化时与modelSerializer一起继承,其中添加def
update_or_create_helper
。然后,为每个您希望其具有功能的串行器继承
UpdateOrCreate
类,并添加一个特定于该模型的简单is_valid
def。serializers.py
fkaflof66#
还有:
wdebmtf27#
我尝试了序列化器解决方案,但似乎在命中序列化器函数
create(self, validated_data)
之前引发了异常。这是因为我使用了ModelViewSet
(它反过来使用了class CreatedModelMixin
)。进一步的研究表明,在以下位置引发了异常:因为我想保留框架提供的所有特性,所以我更喜欢捕获异常并进行更新:
我的应用始终可以使用参数调用
POST /api/my_model/
(此处uuid =主键)。但是,如果我们在
update
函数中处理这个问题会不会更好?4smxwvx58#
此混合将允许在ListSerializer中使用创建或更新
使用方法:
nr7wwzry9#
如果在models.ForeignKey中使用to_field(例如:task_id),则需要将lookup_field = 'task_id'添加为以下内容。