我有以下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-filter为ViewSet
创建自定义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_answers
到get_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')
我将用解决方案更新我的问题。