简体   繁体   中英

add parameter to get_queryset request in Django REST Framework

I'm using Django 2.0 and Django REST Framework .

I have two models in contacts app

contacts/models.py

class Contact(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100, blank=True, null=True, default='')


class ContactPhoneNumber(models.Model):
    contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
    phone = models.CharField(max_length=100)
    primary = models.BooleanField(default=False)

    def __str__(self):
        return self.phone

contacts/serializers.py

class ContactPhoneNumberSerializer(serializers.ModelSerializer):
    class Meta:
        model = ContactPhoneNumber
        fields = ('id', 'phone', 'primary', 'created', 'modified')

and contacts/views.py

class ContactPhoneNumberViewSet(viewsets.ModelViewSet):
    serializer_class = ContactPhoneNumberSerializer

    def get_queryset(self):
        return ContactPhoneNumber.objects.filter(
            contact__user=self.request.user
        )

urls.py

router.register(r'contact-phone', ContactPhoneNumberViewSet, 'contact_phone_numbers')

What I want is following endpoints

  • GET: /contact-phone/{contact_id}/ list phones numbers of particular contact
  • POST: /contact-phone/{contact_id}/ add phone numbers to particular contact
  • PUT: /contact-phone/{contact_phone_number_id}/ update particular phone number
  • DELETE: /contact-phone/{contact_phone_number_id}/ delete particular phone number

PUT and Delete can be achieved as default action of ModelViewSet but how to make get_queryset to accept contact_id as required parameter?

Edit 2

I followed doc Binding ViewSets to URLs explicitly

update app/urls.py

router = routers.DefaultRouter()
router.register(r'contacts', ContactViewSet, 'contacts')
contact_phone_number_view_set = ContactPhoneNumberViewSet.as_view({
    'get': 'list/<contact_pk>/',
    'post': 'create/<contact_pk>/',
    'put': 'update',
    'delete': 'destroy'
})
router.register(r'contact-phone-number', contact_phone_number_view_set, 'contact_phone_numbers')

urlpatterns = [
    path('api/', include(router.urls)),
    url(r'^admin/', admin.site.urls),
]

But it is giving error

AttributeError: 'function' object has no attribute 'get_extra_actions'

You can add extra actions to the viewset using @action decorator:

class ContactPhoneNumberViewSet(viewsets.ModelViewSet):
    serializer_class = ContactPhoneNumberSerializer

    def get_queryset(self):
        return ContactPhoneNumber.objects.filter(
            contact__user=self.request.user
        )

    @action(methods=['post'], detail=False)
    def add_to_contact(self, request, contact_id=None):
        contact = Contact.objects.get(id=contact_id)
        serializer = ContactPhoneNumberSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save(contact=contact)
            return Response(serializer.data)
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @action(methods=['get'], detail=False)
    def set_password(self, request, contact_id=None):
        contact = Contact.objects.get(id=contact_id)
        serializer = PasswordSerializer(contact.contactphonenumber_set.all(), many=True)
        return Response(serializer.data)

UPD

Since you don't need additional actions, you can override retrieve and create defaults methods:

class ContactPhoneNumberViewSet(viewsets.ModelViewSet):
        serializer_class = ContactPhoneNumberSerializer

        def get_queryset(self):
            return ContactPhoneNumber.objects.filter(
                contact__user=self.request.user
            )

        def create(self, request, pk=None):
            contact = Contact.objects.get(id=contact_id)
            serializer = ContactPhoneNumberSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save(contact=contact)
                return Response(serializer.data)
            else:
                return Response(serializer.errors,
                                status=status.HTTP_400_BAD_REQUEST)

        def retrieve(self, request, pk=None):
            contact = Contact.objects.get(pk=pk)
            serializer = PasswordSerializer(contact.contactphonenumber_set.all(), many=True)
            return Response(serializer.data)

To change standard create url use explicitly url binding:

contact_list = ContactPhoneNumberViewSet.as_view({
    'get': 'list',
    'post': 'create',
    'put': 'update',
    'delete': 'destroy'
})

urlpatterns = [
    path('api//contact-phone/<int:pk>/', contact_list, name='contact-list'),
    url(r'^admin/', admin.site.urls),
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM