DRF Фильтрация модели по идентификатору другой модели

У меня есть эти модели:

class Organization(models.Model):
    name = models.CharField(
        "Organization name", max_length=100, unique=True
    )
    description = models.TextField("Organization description")
    
    districts = models.ManyToManyField(
        "District",
        related_name="organizations",
        blank=True,
    )
    

class District(models.Model):
    name = models.CharField("District name", max_length=100, unique=True)

И мне нужно отфильтровать организации по идентификатору района, поэтому url должен быть:

organizations/<district_id>/

Какой способ лучше?

В настоящее время у меня есть два решения, первое:

urls.py

router.register("organizations", OrganizationViewSet, basename="organizations")
router.register(
    "organizations/(?P<district_id>\d+)",
    OrganizationViewSet,
    basename="organizations_by_district",
)

views.py

class OrganizationViewSet(viewsets.ModelViewSet):
    serializer_class = OrganizationSerializer
   
    def get_queryset(self):
        district = self.kwargs.get("district_id")
        if district:
            return Organization.objects.filter(districts__in=[district])
        return Organization.objects.all()

Второе решение:

urls.py

urlpatterns = [
    path(r"organizations/<int:district_id>/",
                get_orgs_by_district, name="org_by_district"),
    ...
]

views.py

@api_view(http_method_names=['GET'])
def get_orgs_by_district(request, district_id):
    objs = Organization.objects.filter(districts__in=[district_id])
    data = OrganizationSerializer(objs, many=True).data
    return Response(data, status=200)

Оба решения работают, но какое из них лучше? И может быть есть другое решение, о котором я не знаю?

Если ваш параметр необязателен, я бы просто использовал параметры запроса, чтобы у вас был только один url для обоих случаев:

# views.py
from rest_framework.generics import ListAPIView

class OrganizationsList(ListAPIView):
    serializer_class = OrganizationSerializer
   
    def get_queryset(self):
        queryset = Organization.objects.all()
        district = self.request.query_params.get('district_id')
        if district:
            queryset = queryset.filter(districts__in=[district])
        return queryset

# urls.py
urlpatterns = [
    path('organizations/', views.OrganizationsList.as_view(), name='organizations_list'),
    ...
]

Вы бы вызвали свой API следующим образом:

{{base_url}}/organizations/  # Retrieve all organizations
{{base_url}}/organizations/?district_id=5  # Filter against district_id

Больше подробностей в официальном документе здесь.

Вернуться на верх