Veuillez considérer ces trois modèles:
class Movie(models.Model):
name = models.CharField(max_length=254, unique=True)
language = models.CharField(max_length=14)
synopsis = models.TextField()
class TimeTable(models.Model):
date = models.DateField()
class Show(models.Model):
day = models.ForeignKey(TimeTable)
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
Et chacun d'entre eux a son sérialiseur:
class MovieSerializer(serializers.HyperlinkedModelSerializer):
movie_id = serializers.IntegerField(read_only=True, source="id")
class Meta:
model = Movie
fields = '__all__'
class TimeTableSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TimeTable
fields = '__all__'
class ShowSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Show
fields = '__all__'
Et leurs routeurs
router.register(r'movie-list', views.MovieViewSet)
router.register(r'time-table', views.TimeTableViewSet)
router.register(r'show-list', views.ShowViewSet)
Maintenant, j'aimerais obtenir tous les objets TimeTable (c'est-à-dire une liste de dates) en filtrant tous les objets Afficher par un objet de film spécifique. Ce code semble être le travail et obtenir la liste comme je le veux
m = Movie.objects.get(id=request_id)
TimeTable.objects.filter(show__movie=m).distinct()
Mais je ne sais pas comment utiliser cela dans Django Rest Framework. J'ai essayé de faire de cette façon (ce dont je suis sûr que c'est faux), et j'obtiens une erreur:
views.py:
class DateListViewSet(viewsets.ModelViewSet, movie_id):
movie = Movie.objects.get(id=movie_id)
queryset = TimeTable.objects.filter(show__movie=movie).distinct()
serializer_class = TimeTableSerializer
urls.py:
router.register(r'date-list/(?P<movie_id>.+)/', views.DateListViewSet)
erreur:
classe DateListViewSet (viewsets.ModelViewSet, movie_id): NameError: le nom 'movie_id' n'est pas défini
Comment puis-je filtrer en utilisant des ensembles de vues dans Django Rest Framework? Ou s'il existe un autre moyen préféré que de le lister s'il vous plaît. Je vous remercie.
ModelViewSet
de par la conception suppose que vous souhaitez implémenter un CRUD (créer, mettre à jour, supprimer)
Il existe également une ReadOnlyModelViewSet
qui implémente uniquement la méthode GET
pour lire uniquement les points de terminaison.
Pour les modèles Movie
et Show
, une ModelViewSet
ou ReadOnlyModelViewSet
est un bon choix que vous souhaitiez ou non implémenter CRUD.
Mais une ViewSet
distincte pour une requête connexe d'une TimeTable
qui décrit la planification d'un modèle Movie
ne semble pas si bonne.
Une meilleure approche consisterait à attribuer directement ce paramètre à une MovieViewSet
. DRF l'a fourni par les décorateurs @detail_route
et @list_route
.
from rest_framework.response import Response
from rest_framework.decorators import detail_route
class MovieViewSet(viewsets.ModelViewset):
queryset = Movie.objects.all()
serializer_class = MovieSerializer
@detail_route()
def date_list(self, request, pk=None):
movie = self.get_object() # retrieve an object by pk provided
schedule = TimeTable.objects.filter(show__movie=movie).distinct()
schedule_json = TimeTableSerializer(schedule, many=True)
return Response(schedule_json.data)
Ce point de terminaison sera disponible avec un movie-list/:id/date_list
url
Documents sur les itinéraires supplémentaires
L'erreur
classe DateListViewSet (viewsets.ModelViewSet, movie_id): NameError: le nom 'movie_id' n'est pas défini.
se produit parce que movie_id
est transmis en tant que classe parent de DataListViewSet et non en tant que paramètre comme vous l'imaginiez
Cet exemple dans documentation devrait correspondre à ce que vous recherchez.
Ajustez votre URL:
url(r'date-list/(?P<movie_id>.+)/', views.DateListView.as_view())
Ajustez votre modèle:
class Show(models.Model):
day = models.ForeignKey(TimeTable, related_name='show')
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
Votre vue ressemblerait à ceci:
class DateListView(generics.ListAPIView):
serializer_class = TimeTableSerializer
def get_queryset(self):
movie = Movie.objects.get(id=self.kwargs['movie_id'])
return TimeTable.objects.filter(show__movie=movie).distinct()
Une autre façon de le faire serait:
Ajustez votre URL:
router.register(r'date-list', views.DateListViewSet)
Ajustez votre modèle:
class Show(models.Model):
day = models.ForeignKey(TimeTable, related_name='show')
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
Votre vue ressemblerait à ceci:
class DateListViewSet(viewsets.ModelViewSet):
serializer_class = TimeTableSerializer
queryset = TimeTable.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('show__movie_id')
Ce qui vous permettra de faire des demandes telles que:
http://example.com/api/date-list?show__movie_id=1
Voir documentation
Enregistrez votre itinéraire comme
router.register(r'date-list', views.DateListViewSet)
changez maintenant votre vue comme indiqué ci-dessous,
class DateListViewSet(viewsets.ModelViewSet):
queryset = TimeTable.objects.all()
serializer_class = TimeTableSerializer
lookup_field = 'movie_id'
def retrieve(self, request, *args, **kwargs):
movie_id = kwargs.get('movie_id', None)
movie = Movie.objects.get(id=movie_id)
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
Utilisez une méthode de récupération, qui fera correspondre les demandes GET au noeud final/date-list/<id>/
.
L'avantage est que vous n'avez pas à gérer explicitement la sérialisation et le renvoi de la réponse vous obligez ViewSet à effectuer cette partie difficile. Nous ne faisons que mettre à jour le jeu de requêtes pour qu'il soit sérialisé et le reste de l'infrastructure fait le reste.
ModelViewSet étant implémenté en tant que,
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
Son implémentation inclut les méthodes suivantes (verbe HTTP et noeud final sur crochet)
list()
(GET /date-list/
) create()
(POST /date-list/
) retrieve()
(GET date-list/<id>/
) update()
(PUT /date-list/<id>/
) partial_update()
(PATCH, /date-list/<id>/
destroy()
(DELETE /date-list/<id>/
) Si vous souhaitez uniquement implémenter retrieve()
( demandes GET au noeud final date-list/<id>/
), vous pouvez le faire à la place de `ModelViewSet),
from rest_framework import mixins, views
class DateListViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = TimeTable.objects.all()
serializer_class = TimeTableSerializer
lookup_field = 'movie_id'
def retrieve(self, request, *args, **kwargs):
movie_id = kwargs.get('movie_id', None)
movie = Movie.objects.get(id=movie_id)
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
Pour améliorer la réponse @ all-is-vanity, vous pouvez explicitement utiliser movie_id
en tant que paramètre de la fonction retrieve
puisque vous redéfinissez la propriété de classe lookup_field
:
def retrieve(self, request, movie_id=None):
movie = Movie.objects.get(id=movie_id)
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
Vous pouvez également appeler self.get_object()
pour obtenir l'objet:
def retrieve(self, request, movie_id=None):
movie = self.get_object()
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)