I have this views.py where I've implemented Class based views, but let's say I want to call a function get_featured_products when the url is https://localhost:8000/featured_products. What sould my urls.py look like to call the get_features_products function?
from django.shortcuts import render
from rest_framework import views
from rest_framework.response import Response
from .models import *
from .serializers import *
# Create your views here.
class ProductView(views.APIView):
model= Product
serializer = ProductSerializer
def get(self,request):
qs = self.model.objects.all()
serializer = self.serializer(qs, many=True)
return Response(serializer.data)
def post(self,request):
serializer = self.serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.error_messages())
def put(self,request,pk):
product = Product.objects.get(id=pk)
serializer = self.serializer(instance=product, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.error_messages)
def get_featured_products(self,request):
#some code here
return None
How should I refer to get_featured_products in my urls.py
You can list it as any other class-based view in your urls.py using .as_view() but you will always have to have queryset as a class variable in your class that inherits from views.APIView.
Your view has to be defined like this
class ProductView(APIView):
queryset = Product.objects.all() # Here put your own queryset, for me if I dont i get error
def get(self, request):
print("GET REQUEST")
return Response({"hello" : "world"})
# you can similarly define other methods such as post, put like you already have
And then in your urls.py
urlpatterns = [
path('admin/', admin.site.urls),
# You'll obviously have to import it if it is not in the same file
path('featured_products', ProductView.as_view())
]
For any class-based view, the urls.py can only contain ProductView.as_view(). It is not possible to directly refer to a method within a class.
as_view() function calls an instance of the class and returns a response based on the following methods:'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'
If your method doesn't match the above, as is the case, you will get a HTTPresponseNotAllowed error.
You may place the method within one of the allowed methods and make it run.
You may check this link to know more: https://djangodeconstructed.com/2020/01/03/mental-models-for-class-based-views/
You can make the method as static and refer to this method from urls.py as:
# views.py
class ProductView(views.APIView):
#staticmethod
def get_featured_products(request):
#some code here
return None
# urls.py
urlpatterns = [
path('featured_products', ProductView.get_featured_products)
]
Related
I was trying out logging all URLs accessed by user along with user id and date time when it was accessed using django middleware as explained here.
For some URLs it was not logging user id. I checked and found that the request.user.username was empty string. I checked views corresponding to those URL and found that those views did not have desired decorators. For example, I changed this:
def getXyz_forListView(request):
# view body ...
to this:
#api_view(['GET'])
#authentication_classes([TokenAuthentication,])
def getXyz_forListView(request):
# view body ...
and it started working.
However some views are created from classes:
class XyzView(View):
def get(self, request):
# view body ...
I added same decorators:
class XyzView(View):
#api_view(['GET'])
#authentication_classes([TokenAuthentication,])
def get(self, request):
# view body ...
But it is still not working. What I am missing?
PS:
It is added to urls.py as follows:
urlpatterns = [
# ...
url(r'^xyz/', XyzView.as_view(), name="xyz"),
]
I think you should try to inherit from APIView class:
from rest_framework.views import APIView
I've always written data into database when posting via Django Rest Framework endpoints. This time I would like to process received data and send it somewhere else without writing into DB. I switched from ModelViewSet to ViewSet, I can issue GET request OK but receiving Bad Request 400 when I curl or POST via DRF URL. Here's a working minimal code (removed need for authentication etc):
urls.py
from django.urls import path, include
from .views import ContactView
from rest_framework import routers
router = routers.DefaultRouter()
router.register('message', ContactView, basename='message')
urlpatterns = [
path('', include(router.urls)),
]
serializers.py
from rest_framework import serializers
class ContactSerializer(serializers.Serializer):
text = serializers.CharField(max_length=250)
views.py
from rest_framework.response import Response
from .serializers import ContactSerializer
from rest_framework import viewsets
class ContactView(viewsets.ViewSet):
def list(self, request):
return Response('Got it')
def create(self, request):
serializer = ContactSerializer(data=request.data)
if serializer.is_valid():
return Response(serializer.data)
else:
return Response('Invalid')
Would greatly appreciate your suggestions.
You can use GenericAPIView for get or post request and do some logic in validate method, for example do something with signals or edit something. Also u can use #detailt_route or #list_route for any ModelViewSet for write special url for instance, example for edit extra data.
how i did rarely:
in urls.py
urlpatterns = [
url('v1/message', ContactAPIView.as_view(), name='message'),
]
in view.py
class ContactAPIView(GenericAPIView):
serializer_class = ContactSerializer
permission_classes = ()
def post(self, request, *args, **kwargs):
serializer_class = self.get_serializer_class()
serializer = serializer_class(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
data = {"status": True}
return Response(data)
in serializers.py
class ContactSerializer(serializers.Serializer):
text = serializers.TextField()
def validate(self, attrs):
write some logic
you are getting this error because you are using Viewsets which uses DefaultRouter to register routers for you. What it does is that it creates 2 urls for your viewset
message
message/id
so in your current case; i.e. viewset you need to send some dummy number in your url to access this post function (which is not a good approach).
So, you should use any class which parent doesn't include ViewSetMixin (which gives functionality of Router Registration) like in your case inherit your view from these classes
ListModelMixin
CreateModelMixin
GenericAPIView
I want to return a custom response to the user when they hit the API with a POST request and it's a success. Here are the code snippets :
views.py
class BlogPostAPIView(mixins.CreateModelMixin,generics.ListAPIView):
# lookup_field = 'pk'
serializer_class = BlogPostSerializer
def get_queryset(self):
return BlogPost.objects.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def post(self,request,*args,**kwargs):
return self.create(request,*args,**kwargs)
urls.py
app_name = 'postings'
urlpatterns = [
re_path('^$', BlogPostAPIView.as_view(),name='post-create'),
re_path('^(?P<pk>\d+)/$', BlogPostRudView.as_view(),name='post-rud'),
]
Right now it's returning the details of the post request as successful response, is there any way I can return some other response based on my own custom queryset?
You can write custom api on views.py. I want to for example;
from rest_framework.views import APIView
from rest_framework.response import Response
class Hello(APIView):
#csrf_exempt
def post(self, request):
content = "Hi"
type = "message"
return Reponse({"content":content,"type":type})
and than define url.
app_name = 'postings'
urlpatterns = [
re_path('^$', BlogPostAPIView.as_view(),name='post-create'),
re_path('^(?P<pk>\d+)/$', BlogPostRudView.as_view(),name='post-rud'),
re_path('^hello/$', Hello.as_view(),name='Hello'),
]
That's it.
Also you can manage permessions : https://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy
and you can use serializer on views : https://www.django-rest-framework.org/api-guide/serializers/#saving-instances
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?
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:
https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset
https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
Hope it helps.
BR.
Eduardo
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"),
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.
My appurls.py:
use path method
urlpatterns = [
path('movies', MoviesView.as_view(), name="MoviesView"),]
Maybe it works
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)
I have this weird problem where when I try to retrieve one object from my database I receive the message {"detail":"Not found."}. I know the object is there because I can see it in my django admin. And when I try to retrieve another object it locates it fine.
urls.py
router = routers.DefaultRouter()
router.register(r'recalls', views.Recalls)
admin.autodiscover()
from rest_framework import generics, permissions, serializers
from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope, TokenHasScope
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
path('api/v1/recalls/rundate/<str:run_date>/', views.Recalls.as_view({'get': 'retrieve'}), name='retrieve_by_rundate'),
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),,
]
views.py
class Recalls(viewsets.ModelViewSet):
'''
This view will be fore retrieving a recall for a car from the database
'''
queryset = CarFax.objects.all()
serializer_class = RecallsSerializer
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
def list(self, request, **kwargs):
queryset = GetRecalls.objects.all()
serializer = RecallsSerializer(queryset, many=True)
print('LIST')
return Response(serializer.data)
def retrieve(self, request, pk=None, *args, **kwargs):
queryset = CarFax.objects.all()
#record = get_list_or_404(queryset, self.kwargs)
record = get_list_or_404(queryset, vin__exact=pk)
serializer = RecallsSerializer(record, many=True)
print('RETRIEVE')
return Response(serializer.data)
def retrieve_by_rundate(self, request, run_date=None):
queryset = CarFax.objects.all()
#record = get_list_or_404(queryset, self.kwargs)
record = get_list_or_404(queryset, run_date__exact=run_date)
serializer = RecallsSerializer(record, many=True)
print('RETRIEVE RUNDATE')
return Response(serializer.data)
I am using the URL 'http://127.0.0.1:8000/api/v1/recalls/(?P<pk>[\w-]+)/$/' (which is automatically created by DefaultRouter(). The issue is that when I look up with one parameter, let's say test1 so
'http://127.0.0.1:8000/api/v1/recalls/test1' it works, and in my console I can see the message "RETRIEVE" from my "retrieve" method being printed. However, when I replace that with another object I created and I know exists, it isn't located and that "RETRIEVE" message isn't printed. So that view obviously isn't even being reached, why is that?
Thanks in advance