0

I have a list of all my objects when I use get method by api/movies in my api, and this is ok. I want also to get only one, specyfic object when use get method by api/movies/1 but now I still have a list of all my objects... What to change in my MoviesView or in urls?

My views.py:

class MoviesView(APIView):
    def get(self, request):
        movies = Movie.objects.all()
        serializer = MovieSerializer(movies, many=True)
        return Response(serializer.data)

My appurls.py:

urlpatterns = [
    url('movies', MoviesView.as_view(), name="MoviesView"),
]

And my project urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include("api.urls")),
]

When I use routers everythig crushes... Could you help me?

3
  • can you please show the error and logs Commented Dec 27, 2019 at 12:29
  • urlpatterns = [ url('movies', MoviesView.as_view(), name="MoviesView"), ] Instead of Url use path method maybe it works Commented Dec 27, 2019 at 12:30
  • There is no error. When I getting api/movies/1 I have a full list of all objects in database - the same as get to api/movies. Commented Dec 27, 2019 at 12:55

5 Answers 5

3

You can simply use viewsets.ModelViewSet that natively implements list and retrieve.

You declare something like router.register('movies', my_views.MoviesViewSet) in you urls.py and

class MoviesViewSet(viewsets.ModelViewSet):
    queryset            = Movie.objects.all()
    serializer_class    = MovieSerializer
    permission_classes  = [IsAuthenticated, ]

    def get_queryset(self):
        return self.queryset

    def get_object(self):
        movie_id = self.kwargs['pk']
        return self.get_queryset().filter(id=movie_id)

    def retrieve(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
        except (Movie.DoesNotExist, KeyError):
            return Response({"error": "Requested Movie does not exist"}, status=status.HTTP_404_NOT_FOUND)
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    def list(self, request, *args, **kwargs):
        queryset    = self.get_queryset()
        serializer  = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

This approach implies that you declare a Serializer, just like:

class MovieSerializer(serializers.ModelSerializer):
    class Meta:
        model   = Movie
        fields  = '__all__'

Django simply maps HOST/movies/ to list (multiple objects) and HOST/movies/PK/ to retrieve method (one single object).

Docs:

Hope it helps.

BR. Eduardo

Sign up to request clarification or add additional context in comments.

Comments

1

I would suggest you if you want to retrieve just 1 element to use a Generic View, i.e RetrieveAPIView

It would give you all you need for getting 1 element.

from rest_framework import generics

class MoviesView(generics.RetrieveAPIView):
    queryset = Movie.objects.all()
    serializer_class = MovieSerializer

but you need also to change urls.py

url(r'movies/(?P<pk>[0-9]+)/$', MoviesView.as_view(), name="MoviesView"),

Comments

1

When you make a GET request to "api/movies/1", the url is matched to the "api/movies" path (read more in the docs), and the MoviesView's get method is called. And your get() implementation just fetches all the movies (movies = Movie.objects.all()), serializes and returns them - that's why you get the entire list.

If you want to retrieve one specific object, you need to somehow specify which object you have in mind, using its primary key (in your case, id).
1. You have to define a separate path: movies/<int:pk>/ (btw, which Django version are you using? url has been deprecated, use path instead!)
2. You have to define a detail view to handle this new case, and pass it to the path function as the second argument.

This general problem can really be solved in many ways, and depending on your app you may want to use a ViewSet instead of views. Then you don't have to define paths (urls) separately - you can use a router. You can't use routers with your view, because router needs a viewset class as its argument.

If you provide more details, I could try to suggest something more specific.

Comments

0

My appurls.py: use path method

urlpatterns = [
path('movies', MoviesView.as_view(), name="MoviesView"),]

Maybe it works

Comments

0

Start by adding a format keyword argument to both of the views, like so

def snippet_list(request, format=None):

and

def snippet_detail(request, pk, format=None):

Now update the snippets/urls.py file slightly, to append a set of format_suffix_patterns in addition to the existing URLs

from django.urls import path

from rest_framework.urlpatterns import format_suffix_patterns

from snippets import views

urlpatterns = [

path('snippets/', views.snippet_list),

path('snippets/<int:pk>', views.snippet_detail),

]

urlpatterns = format_suffix_patterns(urlpatterns)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.