Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework - Adding 2 PKs in URL

I am designing a REST API for an application that has both companies and departments. Some number of users can be members of a company. This leads to the following API structure:

/companies/ - Can GET, POST.

/companies/<pk>/ - Can GET, POST, PUTCH, PATCH, DELETE.

/companies/<pk>/membership/ - Can GET (gives all users that are members of a company), POST.

/companies/<pk>/membership/<pk>/ - Can DELETE.

I've managed to implement the first 3 of the endpoints, but am having trouble with implementing the last one -- how do I implement an endpoint that has multiple <pk> values in the URL? Here's what I have so far:

Currently have a urls.py file in the api app that looks as follows:

...
    url(r'^company', include(company_urls.company_router.urls,
    namespace="company")),
...

urls.py in the company application.

from rest_framework import routers

from .views import CompanyViewSet


company_router = routers.DefaultRouter()

company_router.register(r'^', CompanyViewSet)

serializers.py file:

from rest_framework import serializers

from .models import Company, CompanyMembership
from My_App.users.models import Profile


class CompanySerializer(serializers.ModelSerializer):

    class Meta:
        model = Company
        fields = ('pk', 'name', 'departments', 'members')
        read_only_fields = ('pk', 'departments', 'members')


class CompanyMembershipSerializer(serializers.Serializer):
    user = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all())

    def create(self, validated_data):
        pass

    def delete(self, instance, validated_data):
        pass

And the views.py file:

from .models import Company, CompanyMembership
from .serializers import CompanySerializer, CompanyMembershipSerializer

from My_Appc.users.models import Profile


class CompanyViewSet(viewsets.ModelViewSet):
    queryset = Company.objects.all()
    serializer_class = CompanySerializer

    @decorators.detail_route(methods=['get', 'post', 'delete'])
    def membership(self, request, pk):
        company = self.get_object()
        if request.method == 'GET':
            serializer = CompanyMembershipSerializer(company)
        elif request.method == 'POST':

            serializer = CompanyMembershipSerializer(data=request.data)
            if serializer.is_valid():
                try:
                    user = Profile.objects.get(pk=request.data.get('user'))
                    user_company_membership = CompanyMembership(user=user,
                                                                company=company)
                    user_company_membership.save()
                    return Response({'status': 'User added to Company.'},
                                    status=status.HTTP_201_CREATED)
                except IntegrityError:
                    result = {
                        'status': 'Failed to add user to Company.',
                        'reason': 'User already part of Company.'
                    }
                    status=settings.ADDITIONAL_HTTP_STATUS_CODES[
                        '422_UNPROCESSABLE_ENTITY']
                    return Response(result, status)
            else:
                return Response(serializer.errors,
                                status=status.HTTP_400_BAD_REQUEST)
like image 482
orange1 Avatar asked Mar 13 '23 02:03

orange1


1 Answers

Use different names for parameters in you url:

/companies/<company_pk>/membership/<membership_pk>/

And in you ViewSet add lookup_field and lookup_url_kwarg to point to company pk field/parameter:

class CompanyViewSet(viewsets.ModelViewSet):
    lookup_field = 'pk'
    lookup_url_kwarg = 'company_pk'

get_object method uses this two lookups to filter queryset so you will get company based on first pk in url.

In your membership method and custom logic to manage membership object, you can access membership pk with:

membership_pk = self.kwargs.get('membership_pk', None)
like image 173
dikamilo Avatar answered Mar 19 '23 21:03

dikamilo