I have a puzzle.
These are my models:
class StatusGroup(models.Model):
name = models.TextField()
def __str__(self):
return self.name
class StatusDetail(models.Model):
action = models.CharField(choices=[("CORRECT", "CORRECT"),
("INCORRECT", "INCORRECT")],
max_length=64)
status_group = models.ForeignKey(to=StatusGroup,
on_delete=models.CASCADE,
related_name="status_details")
def __str__(self):
return f"Detail: {self.action}"
serializers:
class StatusDetailSerializer(serializers.ModelSerializer):
class Meta:
model= models.StatusDetail
fields = "__all__"
class StatusGroupSerializer(serializers.ModelSerializer):
status_details = StatusDetailSerializer(many=True)
class Meta:
model = models.StatusGroup
fields = [
"pk",
"status_details",
"name"
]
And a view:
class Status(viewsets.ModelViewSet):
queryset = models.StatusGroup.objects.all()
serializer_class = serializers.StatusGroupSerializer
authentication_classes = []
permission_classes = [permissions.AllowAny]
filter_backends = (DjangoFilterBackend,)
filterset_fields = ['status_details__action']
When I hit localhost:8000/api/status?status_details__action=INCORRECT
I get:
[
{
"pk": 2,
"status_details": [
{
"id": 3,
"action": "CORRECT",
"status_group": 2
},
{
"id": 4,
"action": "INCORRECT",
"status_group": 2
}
],
"name": "Mixed"
}
]
Whereas I would like to have:
[
{
"pk": 2,
"status_details": [
{
"id": 4,
"action": "INCORRECT",
"status_group": 2
}
],
"name": "Mixed"
}
]
How do I force Django to filter the related objects? I can get the result I want in SQL console, but Django adds, all the related objects that belong to the StatusGroup
.
I have a misconception, but I don't know what that is.
CodePudding user response:
try this way (using query set):
from django.db.models import Prefetch
queryset = StatusGroup.objects.prefetch_related(
Prefetch('status_details', queryset=StatusDetail.objects.filter(action='what_you_want'), to_attr='action'))
and in your serializer class:
class StatusGroupSerializer(serializers.ModelSerializer):
status_details = StatusDetailSerializer(source='action', many=True, read_only=True)
CodePudding user response:
First you can create a FilterSet
class like this:
from django_filters import rest_framework as filters
class StatusGroupFilter(filters.FilterSet):
action = filters.CharFilter(
field_name="status_details__action",
lookup_expr="iexact"
)
class Meta:
model = StatusGroup
fields = ["action"]
Then in your views:
class Status(viewsets.ModelViewSet):
queryset = models.StatusGroup.objects.all()
serializer_class = serializers.StatusGroupSerializer
authentication_classes = []
permission_classes = [permissions.AllowAny]
filter_backends = (DjangoFilterBackend,)
# Set the filterset class here
filterset_class = StatusGroupFilter
Then in the URL you can call localhost:8000/api/status?action=INCORRECT