1

I have a query that retrieves all the data but I would like to make another query with another url by adding query parameters in order to target my data search. For example when I request the following url:

http://localhost:8000/api/calendars

I have this result:

 [
  {
    "id": 1,
    "plant_step_id": [
      {
        "id": 3,
        "plant_id": {
          "id": 1,
          "name": "Agropyre"
        },
        "step_title": "Sowing"
      }
    ],
    "start": [
      1
    ],
    "end": [
      3
    ]
  },
  {
    "id": 2,
    "plant_step_id": [
      {
        "id": 6,
        "plant_id": {
          "id": 6,
          "name": "Aubergine"
        },
        "step_title": "Planting"
      }
    ],
    "start": [
      6
    ],
    "end": [
      7
    ]
  }
]

And I would like by requesting this url:

http://localhost:8000/api/plant/life/calendars?plant_id=1&step_title=Sowing

I would like to have the data concerning what I requested in the url.

I tried to achieve this in the view but didn't work.

Here is the model:

from django.db import models
from multiselectfield import MultiSelectField

MONTHS = (
    (1, 'January'),
    (2, 'February'),
    (3, 'March'),
    (4, 'April'),
    (5, 'May'),
    (6, 'June'),
    (7, 'July'),
    (8, 'August'),
    (9, 'September'),
    (10, 'October'),
    (11, 'November'),
    (12, 'December')
)

class PlantLifeCalendar(models.Model):
    plant_step_id = models.ManyToManyField('perma_plant_steps.PlantStep')
    start = MultiSelectField(choices=MONTHS, max_choices=3, max_length=6)
    end = MultiSelectField(choices=MONTHS, max_choices=3, max_length=6)

Here is the serializer:

class PlantSerializer(serializers.ModelSerializer):
    class Meta:
        model = Plant
        fields = ('id', 'name',)

class PlantStepSerializer(serializers.ModelSerializer):
    plant_id = PlantSerializer()

    class Meta:
        model = PlantStep
        fields = ('id', 'plant_id', 'step_title')
        
class ReadPlantLifeCalendarSerializer(serializers.ModelSerializer):
    plant_step_id = PlantStepSerializer(read_only=True, many=True)
    start = fields.MultipleChoiceField(choices=MONTHS)
    end = fields.MultipleChoiceField(choices=MONTHS)

    class Meta:
        model = PlantLifeCalendar
        fields = '__all__'
        read_only_fields = [fields]

class WritePlantLifeCalendarSerializer(serializers.ModelSerializer):
    class Meta:
        model = PlantLifeCalendar
        fields = '__all__'

Here is the view:

class PlantLifeCalendarViewSet(viewsets.ModelViewSet):
    # permission_classes = (IsAuthenticated,)
    permission_classes = (AllowAnonymous,)
    queryset = PlantLifeCalendar.objects.prefetch_related('plant_step_id').all()

    def create(self, request, *args, **kwargs):
        serializer = WritePlantLifeCalendarSerializer(data=request.data, many=isinstance(request.data, list))
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def get_serializer_class(self):
        if self.action in ("list", "retrieve"):
            return ReadPlantLifeCalendarSerializer
        return WritePlantLifeCalendarSerializer


class PlantLifeCalendarLinkAPIView(ListAPIView):
permission_classes = (AllowAnonymous,)
serializer_class = ReadPlantLifeCalendarSerializer

def get_queryset(self):
    try:
        plant_id = int(self.request.GET.get('plant_id'))
    except (ValueError, TypeError):
        plant_id = None
    step_title = self.request.GET.get('step_title')

    params = {}
    if plant_id:
        params['plant_id'] = plant_id
    if step_title:
        params['step_title'] = step_title
    if params:
        return PlantLifeCalendar.objects.filter(**params)
    return PlantLifeCalendar.objects.all()

1 Answer 1

2

To make your example work you need to replace plant_step_id for plant_step_id__plant_id and step_title for plant_step_id__step_title, because these are nested properties that are part of PlantStep not PlantLifeCalendar.

However and easier way is to use Django Rest Framework Filtering Guide.

First install pip install django-filter.

...
from django_filters.rest_framework import DjangoFilterBackend

...

class PlantLifeCalendarLinkAPIView(ListAPIView):
    permission_classes = (AllowAnonymous,)
    serializer_class = ReadPlantLifeCalendarSerializer
    queryset = PlantLifeCalendar.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['plant_step_id__plant_id', 'plant_step_id__step_title']

This means that you would need to use plant_step_id__plant_id and plant_step_id__step_title in your query params to obtained the desired result.

The entry at DRF: https://www.django-rest-framework.org/api-guide/filtering/

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for your help !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.