使用具有外键的模型的序列化程序在Django Rest Framework中的父表上执行CRUD [英] Use serializer of model having foreign key to do CRUD on parent table in Django Rest Framework
问题描述
在我的API中,我有两个模型Question
和Option
,如下所示
In my API, I have two models Question
and Option
as shown below
class Question(models.Model):
body = models.TextField()
class Options(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
option = models.CharField(max_length=100)
is_correct = models.SmallIntegerField()
在创建问题时,可以同时创建选项会更好.并且已经存在的问题不应创建,但是如果选项与先前的选项不同,则可以更改选项.
我正在使用ModelSerializer
和ModelViewSet
.我为Question
和Option
使用了不同的URL和视图.
While creating a question it would be nicer the options can be created at the same time. And already existed question should not be created but the options can be changed if the options are different from previous.
I am using ModelSerializer
and ModelViewSet
. I use different urls and views for Question
and Option
.
serializers.py
serializers.py
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = '__all__'
class OptionReadSerializer(serializers.ModelSerializer):
question = QuestionSerializer(read_only=True)
class Meta:
model = Option
fields = ('question', 'option', 'is_correct')
class OptionWriteSerializer(serializer.ModelSerializer):
class Meta:
model = Option
fields = ('question', 'option', 'is_correct')
views.py
class QuestionViewSet(ModelViewSet):
seriaizer_class = QuestionSerializer
queryset = Question.objects.all()
class OptionViewSet(ModelViewSet):
queryset = Option.objects.all()
def get_serializer_class(self):
if self.request.method == 'POST':
return OptionWriteSerializer
return OptionReadSerializer
urls.py
from django.urls import include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('api/question', QuestionViewset, base_name='question')
router.register('api/option', OptionViewSet, base_name='option')
urlpatterns = [
path('', include(router.urls))
]
通过这种方式,我总是必须先创建问题,然后才能单独添加该问题的选项.我认为这可能不是一种实用的方法.
最好同时添加问题和选项,并且与所有CRUD操作类似,这样更好.
In this way, I always have to create questions first and then I can individually add the option for that question. I think this may not be a practical approach.
It would be nicer that question and option can be added at the same time and similar to all CRUD operations.
预期结果和JSON格式的发布数据如下所示:
The expected result and posting data in JSON format are as shown below:
{
"body": "Which country won the FIFA world cup 2018",
"options": [
{
"option": "England",
"is_correct": 0
},
{
"option": "Germany",
"is_correct": 0
},
{
"option": "France",
"is_correct": 1
}
]
}
推荐答案
在models
中,我在Option
模型的外键字段中添加了related_name='options'
In models
I added related_name='options'
in foreign key field of Option
model
models.py
models.py
class Question(models.Model):
body = models.TextField()
class Options(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='options')
option = models.CharField(max_length=100)
is_correct = models.SmallIntegerField()
在QuestionWriteSerializer
中,我重写了update()
和create()
方法.为了创建和更新逻辑,已从QuestionWriteSerialzer
处理.
In QuestionWriteSerializer
I override the update()
and create()
method. For creating and updating the logic was handled from QuestionWriteSerialzer
.
serializers.py
serializers.py
class OptionSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = Option
fields = ('id', 'question', 'option', 'is_correct')
class QuestionReadSerializer(serializers.ModelSerializer):
options = OptionSerializer(many=True, read_only=True)
class Meta:
model = Question
fields = ('id', 'body', 'options')
class QuestionWriteSerializers(serializers.ModelSerializer):
options = OptionSerializer(many=True)
class Meta:
model = Question
fields = ('id', 'body', 'options')
def create(self, validated_data):
options_data = validated_data.pop('options')
question_created = Questions.objects.update_or_create(**validated_data)
option_query = Options.objects.filter(question=question_created[0])
if len(option_query) > 1:
for existeding_option in option_query:
option_query.delete()
for option_data in options_data:
Options.objects.create(question=question_created[0], **option_data)
return question_created[0]
def update(self, instance, validated_data):
options = validated_data.pop('options')
instance.body = validated_data.get('body', instance.body)
instance.save()
keep_options = []
for option_data in options:
if 'id' in option_data.keys():
if Options.objects.filter(id=option_data['id'], question_id=instance.id).exists():
o = Options.objects.get(id=option_data['id'])
o.option = option_data.get('option', o.option)
o.is_correct = option_data.get('is_correct', o.is_correct)
o.save()
keep_options.append(o.id)
else:
continue
else:
o = Options.objects.create(**option_data, question=instance)
keep_options.append(o.id)
for option_data in instance.options.all():
if option_data.id not in keep_options:
Options.objects.filter(id=option_data.id).delete()
return instance
QuestionViewSet
几乎相同,我删除了OptionViewSet
并控制了QuestionViewSet
The QuestionViewSet
is almost the same and I removed the OptionViewSet
and controlled all things from QuestionViewSet
views.py
class QuestionViewSet(ModelViewSet):
queryset = Question.objects.all()
def get_serializer_class(self) or self.request.method == 'PUT' or self.request.method == 'PATCH':
if self.request.method == 'POST':
return QuestionWriteSerializer
return QuestionReadSerializer
def create(self, request, *args, **kwargs):
"""
Overriding create() method to change response format
"""
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response({
'message': 'Successfully created question',
'data': serializer.data,
'status': 'HTTP_201_CREATED',
}, status=status.HTTP_201_CREATED, headers=headers)
else:
return Response({
'message': 'Can not create',
'data': serializer.errors,
'status': 'HT',
}, status=status.HTTP_400_BAD_REQUEST)
这篇关于使用具有外键的模型的序列化程序在Django Rest Framework中的父表上执行CRUD的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!