python - 过滤嵌套序列化程序 - Django REST
问题描述
我有以下内容models.py
:
class Question(models.Model):
code = models.CharField(max_length=12)
text = models.CharField(max_length=1000, null=True)
catgeroy = models.ForeignKey(
Category, on_delete=models.PROTECT, null=True, blank=True, db_index=True, related_name='category')
class Answer(models.Model):
question = models.ForeignKey(
Question, on_delete=models.PROTECT, null=True, blank=True, db_index=True, related_name='question')
exam = models.ForeignKey(Exam, on_delete=models.CASCADE)
value = models.FloatField(null=True, blank=True)
class Exam(models.Model):
year = models.IntegerField()
我的嵌套serializer
看起来像这样:
class AnswerSerializer(serializers.ModelSerializer):
related_name = 'answer'
class Meta:
model = Value
fields = ('id', 'value', 'question', 'exam')
class NestedQuestionAnswerSerializer(serializers.ModelSerializer):
answer = AnswerSerializer(many=True, read_only=True)
class Meta:
model = Question
fields = (
'id','code', 'text', 'answer'
)
我的views.py
样子是这样的:
class QuestionAnswerViewSet(BaseCertViewSet):
queryset = Question.objects.all()
serializer_class = serializers.NestedQuestionAnswerSerializer
filter_backends = [filters.DjangoFilterBackend]
filterset_fields = ('category',)
我的urls.py
样子是这样的:
router.register('question-answer', views.QuestionAnswerViewSet, 'question-answer')
我想做的是同时按类别和考试(这是一个子属性)进行过滤。所以是这样的:https://example.com/api/question-answer?category=4&exam=21
这可能会返回属于 category=4 并且出现在考试=21 中的所有问题。
我单独过滤没有问题category
,但似乎无法过滤exam
哪个是子外键。
我在 SO 上尝试了很多解决方案,但似乎没有一个能做到以上。
更新:
感谢大家提出的解决方案。
我最终使用了这个解决方案
添加了 List Serializer 类并修改了to_representation
函数:
class FilteredAnswerSerializer(serializers.ListSerializer):
def to_representation(self, data):
qry_exam = self.context['request'].GET.get('exam')
data = data.filter(exam=qry_exam)
return super(FilteredAnswerSerializer, self).to_representation(data)
然后在我的 Answer 序列化程序中我称之为:
class AnswerSerializer(serializers.ModelSerializer):
related_name = 'answer'
class Meta:
model = Value
list_serializer_class = FilteredAnswerSerializer
fields = ('id', 'value', 'question', 'exam')
解决方案
一种方法是为using django-filter创建一个自定义FilterSet
ViewSet
。
这是更易读、更受欢迎的方式,因为将来代码会更清晰、更容易修改。
实现此目的的一种非常简单、可扩展性较差的方法是覆盖类的get_queryset
方法ViewSet
。
from django.db.models import Prefetch
class NestedQuestionAnswerSerializer(serializers.ModelSerializer):
answer = AnswerSerializer(source="filtered_answers", many=True, read_only=True)
class Meta:
model = Question
fields = ('id', 'code', 'text', 'answer')
class QuestionAnswerViewSet(BaseCertViewSet):
queryset = Question.objects.all()
serializer_class = serializers.NestedQuestionAnswerSerializer
filter_backends = [filters.DjangoFilterBackend]
filterset_fields = ('category',)
def get_exam_param(self):
""" A helper to extract the exam id from the query_params. """
try:
return int(self.request.query_params["exam"])
except (KeyError, ValueError, TypeError):
return None
def get_queryset(self):
queryset = super().get_queryset()
exam = self.get_exam_param()
if exam is not None:
queryset = queryset.filter(answer__exam_id=exam).prefetch_related(
Prefetch(
"answers",
queryset=Answer.objects.filter(exam_id=exam),
to_attr="filtered_answers",
),
)
else:
queryset = queryset.prefetch_related(
Prefetch(
"answers",
queryset=Answer.objects.all(),
to_attr="filtered_answers",
),
)
return queryset
编辑:根据对评论问题的更新理解添加filtered_answers
到get_queryset
序列化程序类。主要改编自这里的答案。
推荐阅读
- docker - 使用 docker compose 为 Ninja 开具发票 - 错误 500
- python - ImportError:在运行 main.py 时尝试使用没有已知父包的相对导入
- azure-sqldw - 有没有办法监控 Azure Synapse Pipelines 的执行?
- javascript - 过滤后的mongoDB项目数组元素
- android - 每次调用 Activity 的 onCreate() 时都会调用 Repository 中的刷新(不在屏幕旋转中)
- javascript - 如何将对象推送到 vueJS 中的承诺?
- file-upload - 如果通过 setValue 设置要上传的文件,则不会触发 SAPUI5 Fileuploader 上传
- r - 在栅格上绘制坐标
- google-apps-script - 写入表格的 Google 脚本
- python - Django - 限制 Stripe SuccessUrl 访问