web-dev-qa-db-fra.com

AssertionError: `HyperlinkedIdentityField` requiert la demande dans le contexte du sérialiseur

Je veux créer un many-to-many relation où une personne peut être dans de nombreux clubs et un club peut avoir plusieurs personnes. J'ai ajouté le models.py et serializers.py pour la logique suivante mais lorsque j'essaie de le sérialiser dans l'invite de commande, j'obtiens l'erreur suivante - Que fais-je de mal ici? Je n'ai même pas de HyperlinkedIdentityField

Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Users\user\corr\lib\site-packages\rest_framework\serializers.py", line 503, in data
ret = super(Serializer, self).data
File "C:\Users\user\corr\lib\site-packages\rest_framework\serializers.py", line 239, in data
self._data = self.to_representation(self.instance)
File "C:\Users\user\corr\lib\site-packages\rest_framework\serializers.py", line 472, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File "C:\Users\user\corr\lib\site-packages\rest_framework\relations.py", line 320, in to_representation"the serializer." % self.__class__.__name__
AssertionError: `HyperlinkedIdentityField` requires the request in the serializer context. Add `context={'request': request}` when instantiating the serializer.

models.py

class Club(models.Model):
    club_name = models.CharField(default='',blank=False,max_length=100)

class Person(models.Model):
    person_name = models.CharField(default='',blank=False,max_length=200)
    clubs = models.ManyToManyField(Club)

serializers.py

class ClubSerializer(serializers.ModelSerializer):
    class Meta:
        model = Club
        fields = ('url','id','club_name','person')

class PersonSerializer(serializers.ModelSerializer):
    clubs = ClubSerializer()
    class Meta:
        model = Person
        fields = ('url','id','person_name','clubs')

views.py

class ClubDetail(generics.ListCreateAPIView):
serializer_class = ClubSerializer

def get_queryset(self):
     club = Clubs.objects.get(pk=self.kwargs.get('pk',None))
     persons = Person.objects.filter(club=club)
     return persons

class ClubList(generics.ListCreateAPIView):
    queryset = Club.objects.all()
    serializer_class = ClubSerializer


class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
    serializer_class = PersonSerializer


def get_object(self):
    person_id = self.kwargs.get('pk',None)
    return Person.objects.get(pk=person_id) 

L'inspection du sérialiseur créé me donne ceci -

PersonSerializer(<Person: fd>):
url = HyperlinkedIdentityField(view_name='person-detail')
id = IntegerField(label='ID', read_only=True)
person_name = CharField(max_length=200, required=False)
clubs = ClubSerializer():
    url = HyperlinkedIdentityField(view_name='club-detail')
    id = IntegerField(label='ID', read_only=True)
    club_name = CharField(max_length=100, required=False)

mais serializer.data me donne l'erreur

****************** Modifier ********************* J'ai réalisé que l'erreur pouvait être due à url modèles, j'ai donc ajouté les modèles d'URL suivants mais j'obtiens toujours l'erreur -

urlpatterns = format_suffix_patterns([
url(r'^$', views.api_root),
url(r'^clubs/$',
    views.ClubList.as_view(),
    name='club-list'),
 url(r'^clubs/(?P<pk>[0-9]+)/persons/$',
    views.ClubDetail.as_view(),
    name='club-detail'),
url(r'^person/(?P<pk>[0-9]+)/$',
    views.PersonDetail.as_view(),
    name='person-detail'),
])
23
qwertp

Vous obtenez cette erreur car HyperlinkedIdentityField s'attend à recevoir request dans context du sérialiseur afin qu'il puisse créer des URL absolues. Lorsque vous initialisez votre sérialiseur sur la ligne de commande, vous n'avez pas accès à la demande et recevez donc une erreur.

Si vous devez vérifier votre sérialiseur sur la ligne de commande, vous devez faire quelque chose comme ceci:

from rest_framework.request import Request
from rest_framework.test import APIRequestFactory

from .models import Person
from .serializers import PersonSerializer

factory = APIRequestFactory()
request = factory.get('/')


serializer_context = {
    'request': Request(request),
}

p = Person.objects.first()
s = PersonSerializer(instance=p, context=serializer_context)

print s.data

Votre champ d'URL ressemblerait à quelque chose comme http://testserver/person/1/.

J'ai deux solutions ...

urls.py

1) Si vous utilisez un router.register, vous pouvez ajouter base_name:

router.register(r'users', views.UserViewSet, base_name='users')
urlpatterns = [    
    url(r'', include(router.urls)),
]

2) Si vous avez quelque chose comme ça:

urlpatterns = [    
    url(r'^user/$', views.UserRequestViewSet.as_view()),
]

Vous devez passer le contexte au sérialiseur:

views.py

class UserRequestViewSet(APIView):            
    def get(self, request, pk=None, format=None):
        user = ...    
        serializer_context = {
            'request': request,
        }
        serializer = api_serializers.UserSerializer(user, context=serializer_context)    
        return Response(serializer.data)

Comme cela, vous pouvez continuer à utiliser l'URL sur votre sérialiseur: serializers.py

...
url = serializers.HyperlinkedIdentityField(view_name="user")
...
18
Slipstream

J'ai rencontré le même problème. Mon approche consiste à supprimer "url" de Meta.fields dans serializer.py.

9
Diansheng

Suite à la réponse de Slipstream, j'ai édité mon views.py présentation du contexte et maintenant ça marche.

class UserViewSet(viewsets.ModelViewSet):

    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all().select_related('profile').order_by('-date_joined')
    serializer_class = UserSerializer

    @list_route(methods=['get'], url_path='username/(?P<username>\w+)')
    def getByUsername(self, request, username):
        serializer_context = {
            'request': request,
        }
        user = get_object_or_404(User, username=username)
        return Response(UserSerializer(user, context=serializer_context).data, status=status.HTTP_200_OK)
4
MDT

Suite à la réponse de MDT, j'utilise Django-rest-framework, et je le résous en changeant request en request._request.

serializer_context = {'request': Request(request._request)}
0
Chestermomo