python - Custom Hyperlinked URL field for more than one lookup field in a serializer of DRF -


i using django rest framework developing web api project. in project need build nested api's endpoint this:

   /users/ - users    /users/<user_pk> - details of particular user    /users/<user_pk>/mails/ - mails sent user    /users/<user_pk>/mails/<pk> - details of mail sent user 

so, using drf-nested-routers ease of writing & maintaing these nested resources.

i want output of endpoints have hyperlink getting details of each nested resource alongwith other details this:

[     {         "url" : "http://localhost:8000/users/1",         "first_name" : "name1",         "last_name": "lastname"         "email" : "name1@xyz.com",         "mails": [             {                  "url": "http://localhost:8000/users/1/mails/1",                  "extra_data": "this data",                  "mail":{                      "url": "http://localhost:8000/mails/3"                      "to" : "abc@xyz.com",                      "from": "name1@xyz.com",                      "subject": "this subject text",                      "message": "this message text"                  }             },             {              ..........             }            ..........          ]     }     ......... ] 

to this, write serializers inherit hyperlinkedmodelserializer per drf docs, automatically adds url field in response during serialization.

but, default drf serializers not support generation of url nested resource above mentioned or can more single lookup field. handle situation, recommended create custom hyperlinked field.

i followed doc, , write custom code handling url generation of nested resource. code snippets follows:

models.py

from django.contrib.auth.models import abstractuser django.db import models  # user model class user(models.abstractuser):     mails = models.manytomanyfield('mail', through='usermail',                                       through_fields=('user', 'mail'))  # mail model class mail(models.model):     = models.emailfield()     = models.emailfield()     subject = models.charfield()     message = models.charfield()  # user mail model class usermail(models.model):     user = models.foreignkey('user')     mail = models.foreignkey('mail')     extra_data = models.charfield() 

serializers.py

from rest_framework import serializers .models import user, mail, usermail .serializers_fields import usermailhyperlink  # mail serializer class mailserializer(serializers.hyperlinkedmodelserializer):     class meta:         model = mail         fields = ('url', 'to', 'from', 'subject', 'message' )  # user mail serializer class usermailserializer(serializers.hyperlinkedmodelserializer):     url = usermailhyperlink()     mail = mailserializer()      class meta:         model = usermail         fields = ('url', 'extra_data', 'mail')     # user serializer class userserializer(serializers.hyperlinkedmodelserializer):     mails = usermailserializer(source='usermail_set', many=true)      class meta:         model = user         fields = ('url', 'first_name', 'last_name', 'email', 'mails') 

serializers_fields.py

from rest_framework import serializers rest_framework.reverse import reverse .models import usermail  class usermailhyperlink(serializers.hyperlinkedrelatedfield):     view_name = 'user-mail-detail'     queryset = usermail.objects.all()      def get_url(self, obj, view_name, request, format):         url_kwargs = {             'user_pk' : obj.user.pk,             'pk' : obj.pk         }         return reverse(view_name, kwargs=url_kwargs, request=request,                            format=format)      def get_object(self, view_name, view_args, view_kwargs):         lookup_kwargs = {            'user_pk': view_kwargs['user_pk'],            'pk': view_kwargs['pk']         }         return self.get_queryset().get(**lookup_kwargs) 

views.py

from rest_framework import viewsets rest_framework.response import response django.shortcuts import get_object_or_404 .models import user, usermail .serializers import userserializer, mailserializer  class userviewset(viewsets.modelviewset):     queryset = user.objects.all()     serializer_class = userserializer  class usermailviewset(viewsets.viewset):     queryset = usermail.objects.all()     serializer_class = usermailserializer      def list(self, request, user_pk=none):         mails = self.queryset.filter(user=user_pk)         serializer = self.serializer_class(mails, many=true,             context={'request': request}         )         return response(serializer.data)      def retrieve(self, request, pk=none, user_pk=none):         queryset = self.queryset.filter(pk=pk, user=user_pk)         mail = get_object_or_404(queryset, pk=pk)         serializer = self.serializer_class(mail,             context={'request': request}         )         return response(serializer.data) 

urls.py

from rest_framework.routers import defaultrouter rest_framework_nested import routers django.conf.urls import include, url import views  router = defaultrouter() router.register(r'users', views.userviewset, base_name='user')  user_router = routers.nestedsimplerouter(router, r'users',     lookup='user' ) user_router.register(r'mails', views.usermailviewset,     base_name='user-mail' )   urlpatterns = [     url(r'^', include(router.urls)),     url(r'^', include(user_router.urls)),  ] 

now, after doing when run project , ping /users/ api endpoint, got error:

attributeerror : 'usermail' object has no attribute 'url'

i couldn't understand why error came, because in usermailserializer added url field attribute of serializer, when has serialize why takes url field attribute of usermail model. please me out away problem.

p.s: please don't suggest refactoring in models. as, here disguised project real idea user & mail thing. so, take test case , suggest me solution.

i needed similar lately. solution ended making custom relations field. save space, ill (and shamelessly) point source code. important part adding lookup_fields , lookup_url_kwargs class attributes used internally both lookup objects , construct uris:

class multiplepkshyperlinkedidentityfield(hyperlinkedidentityfield):     lookup_fields = ['pk']     def __init__(self, view_name=none, **kwargs):         self.lookup_fields = kwargs.pop('lookup_fields', self.lookup_fields)         self.lookup_url_kwargs = kwargs.pop('lookup_url_kwargs', self.lookup_fields)         ... 

that in turn allows usage like:

class myserializer(serializers.modelserializer):     url = multiplepkshyperlinkedidentityfield(         view_name='api:my-resource-detail',         lookup_fields=['form_id', 'pk'],         lookup_url_kwargs=['form_pk', 'pk']     ) 

here how use source code.

hopefully can started.


Comments

Popular posts from this blog

php - Admin SDK -- get information about the group -

dns - How To Use Custom Nameserver On Free Cloudflare? -

Python Error - TypeError: input expected at most 1 arguments, got 3 -