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'),
])
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")
...
J'ai rencontré le même problème. Mon approche consiste à supprimer "url" de Meta.fields dans serializer.py.
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)
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)}