在嵌套序列化程序 - 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

这可能应该返回属于类别 = 4 并在 exam=21 上出现的所有问题。

我单独按category过滤没有问题,但似乎无法过滤exam子外键。

我已经在SO上尝试了许多解决方案,但似乎没有一个可以做到上述操作。

更新:

感谢大家建议的解决方案。

我最终使用了这个解决方案

添加了一个列表序列化程序类并修改了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)

然后在我的答案序列化程序中,我称之为:


class AnswerSerializer(serializers.ModelSerializer):
related_name = 'answer'
class Meta:
model = Value
list_serializer_class = FilteredAnswerSerializer
fields = ('id', 'value', 'question', 'exam')

一种方法是使用django-filterViewSet创建自定义FilterSet

这是更具可读性和首选的方法,因为代码将来会更清晰,更容易更改。

实现此目的的一种非常简单、扩展性较低的方法是重写ViewSet类的get_queryset方法。

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_answersget_queryset基于 更新了对评论中问题的理解。大多改编自这个答案 这里。

您可以创建过滤器类,在该过滤器类中,您可以记下自定义字段及其查询。

from django_filters import rest_framework as filters
class QuestionFilter(filters.FilterSet):
exam = filters.IntegerField(method="filter_exam")
class Meta:
fields = ('category', 'exam')
def filter_exam(self, queryset, name, value):
return queryset.filter(answer__exam_id=value)

在视图中

class QuestionAnswerViewSet(BaseCertViewSet):
queryset = Question.objects.all()
serializer_class = serializers.NestedQuestionAnswerSerializer
filter_backends = [filters.DjangoFilterBackend]
filter_class = QuestionFilter

感谢大家建议的解决方案。

我最终使用了这个解决方案

添加了一个列表序列化程序类并修改了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)

然后在我的答案序列化程序中,我称之为:


class AnswerSerializer(serializers.ModelSerializer):
related_name = 'answer'
class Meta:
model = Value
list_serializer_class = FilteredAnswerSerializer
fields = ('id', 'value', 'question', 'exam')

我将用解决方案更新我的问题。

相关内容

  • 没有找到相关文章

最新更新