i need to add action decorator from rest_framework.decorators to view on generics
views.py
class AnswerDetailView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsOwnerOrAdminOnly]
queryset = Answer.objects.all()
serializer_class = AnswerSerializer
#action(['POST'], detail=True)
def like(self, request, pk=None):
answer = self.get_object()
author = request.user
serializer = AnswerReviewSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
try:
answer_review = AnswerReview.objects.get(answer=answer, author=author)
answer_review.delete()
message = 'disliked'
except AnswerReview.DoesNotExist:
AnswerReview.objects.create(answer=answer, author=author)
message = 'liked'
return Response(message, status=200)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('answers/', views.AnswerCreateView.as_view()),
path('answers/<int:pk>/', views.AnswerDetailView.as_view()),
path('comments/', views.CommentCreateView.as_view()),
path('comments/<int:pk>/', views.CommentDetailView.as_view()),
path('answers/<int:pk>/like/', views.AnswerDetailView.like),
]
i've tried on ModelViewSet and it works but how can i do it on generics?
also i need my urls for like button look like this:
urlpatterns = [
path('answers/<int:pk>/like/', <view>),
]
#action doesn't work with GenericAPIView. it's only working with ViewSet. I will suggest you should use GenericViewSet.
Related
Currently, the API I'm pulling is as such:
http://127.0.0.1:8000/api/locs/data/2
and the output is as such:
{
"id": 2,
"date": "2019-01-07",
"hour": null,
"measurement": null,
"location": 6
}
What I want is actually filtering by the location value, so from the API url above, I would love to pull all the data that is from location: 2. How can I achieve that?
What I have so far:
views.py
class LocationDataView(viewsets.ModelViewSet):
serializer_class = LocationDataSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
def get_queryset(self):
queryset = LocationData.objects.all()
pk = self.kwargs['pk']
queryset = queryset.filter(location=pk)
def perform_create(self, serializer):
serializer.save()
and urls.py
from django.urls import path, include
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register(r'locs', views.LocationListView, 'locs')
router.register(r'locs/data', views.LocationDataView, 'locs/data')
urlpatterns = [
path('', include(router.urls)),
path('locs/forecast-data/',
views.getForecastData, name='forecast-data'),
]
My question is that, how can I access the pk for the location?
I think you need to add the retrieve function in the LocationDataView.
class LocationDataView(viewsets.ModelViewSet):
...
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializers = self.get_serializer(instance)
return Response(serializers.data)
And in urls.py
...
router = routers.DefaultRouter()
router.register(r'locs', views.LocationListView, 'locs')
router.register(r'locs/<int:pk>', views.LocationDataView, 'locs/data')
...
I have the following views:
class NotificationAPI(viewsets.ModelViewSet):
authentication_classes = (CustomJWTAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
queryset = Notification.objects.all()
def get_queryset(self):
qs = self.queryset.all()
queryset = qs.filter(recipient=self.request.user.id)
return queryset
class MarkAsReadView(APIView):
permission_classes = (IsAuthenticated,)
authentication_classes = [CustomJWTAuthentication, SessionAuthentication]
#swagger_auto_schema(manual_parameters=[authorization])
def post(self, request, notification_id):
# mark single notification as read
class MarkAllAsReadView(APIView):
permission_classes = (IsAuthenticated,)
authentication_classes = [CustomJWTAuthentication, SessionAuthentication]
#swagger_auto_schema(manual_parameters=[authorization])
def post(self, request):
#mark all notifications as read
and here is my urls.py routing:
router = routers.DefaultRouter(trailing_slash=False)
router.register('notifications', NotificationAPI, basename='notifications')
urlpatterns = [
path('', include(router.urls)),
# notifications
path('notifications/mark-as-read/<int:notification_id>',
MarkAsReadView.as_view(), name='mark-as-read'), # this work
path('notifications/mark-all-as-read',
MarkAllAsReadView.as_view(), name='mark-all-as-read'), #this doesn't work
]
Currently whenever i call mark-as-read it work fine, but when i try to call mark-all-as-read api it always return the following 405 error:
{
"detail": "Method \"POST\" not allowed."
}
I checked and it seem like the notifications ModelViewSet routing messing up the other mark-all-as-read ApiView route.
I checked by commenting the ModelViewSet out and the mark-all-as-read now work.
Why is this happening? shouldn't routing be separate by name and basename ? Why it's when i call mark-all-as-read it go into the ModelViewSet route, but the mark-as-read work normally?
UPDATE:
i had update my NotificationAPI modelviewset api with custom route as suggestion from Risadinha but it's displaying serializer in body, how do i remove it? because there no need for body data in my mark-as-read and mark-all-as-read requests
My updated NotificationAPI
class NotificationAPI(mixins.ListModelMixin,
viewsets.GenericViewSet):
authentication_classes = (CustomJWTAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
queryset = Notification.objects.all().order_by('-id')
serializer_class = NotificationSerializer
search_fields = ('verb__unaccent', 'description__unaccent')
pagination_class = ListPagination
filter_backends = [DjangoFilterBackend, SearchFilter]
filterset_fields = ['unread']
def get_queryset(self):
qs = self.queryset.all()
queryset = qs.filter(recipient=self.request.user.id)
return queryset
#action(
basename='mark-all-as-read',
detail=False,
methods=['post'],
name='mark-all-as-read',
url_path=r'mark-all-as-read',
url_name='Mark All As Read'
)
def mark_all_as_read(self, request):
#do some stuffs
#action(
basename='mark-as-read',
detail=False,
methods=['put'],
name='mark-as-read',
url_path=r'mark-as-read/(?P<pk>\d+)'
)
def mark_as_read(self, request, pk=None):
#do some stuffs
Models.py
from django.db import models
# Create your models here.
class reviewData(models.Model):
building_name = models.CharField(max_length=50)
review_content = models.TextField()
star_num = models.FloatField()
class buildingData(models.Model):
building_name = models.CharField(max_length=50)
building_loc = models.CharField(max_length=50)
building_call = models.CharField(max_length=20)
views.py
# Create your views here.
from django.shortcuts import render
from rest_framework.response import Response
from .models import reviewData
from .models import buildingData
from rest_framework.views import APIView
from .serializers import ReviewSerializer
class BuildingInfoAPI(APIView):
def get(request):
queryset = buildingData.objects.all()
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
class ReviewListAPI(APIView):
def get(request):
queryset = reviewData.objects.all()
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
urls.py
from django.contrib import admin
from django.urls import path
from crawling_data.views import ReviewListAPI
from crawling_data.views import BuildingInfoAPI
urlpatterns = [
path('admin/', admin.site.urls),
path('api/buildingdata/', BuildingInfoAPI.as_view()),
#path('api/buildingdata/(I want to put building name here)', ReviewListAPI.as_view())
]
I am making review api.
I want to use building name as url path to bring reviews for specific buildings
For example, there are a, b, c reviews
a, b reviews are for aaabuilding
c reviews are for xxxbuilding
api/buildingdata/aaabuilding (only shows aaabuilding review)
{
building_name = aaabuilding
review_content = a
star_num = 5
building_name = aaabuilding
review_content = b
star_num = 3
}
api/buildingdata/xxxbuilding (only shows xxxbuilding review)
{
building_name = xxxbuilding
review_content = c
star_num = 4
}
I've searched some dynamic url posts, but they were not that i want.
Is there any way to bring building name into url from db?
In urls.py
path('api/buildingdata/<str:building>/', BuildingInfoAPI.as_view()),
And in views.py
class BuildingInfoAPI(APIView):
def get(request, building):
queryset = buildingData.objects.filter(building_name__contains=building)
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
Hope this will solve the issue
I suggets you take a look at the django documentation, this is pretty well explained there, here is a useful link
In your urls.py, you should put:
from django.contrib import admin
from django.urls import path
from crawling_data.views import ReviewListAPI
from crawling_data.views import BuildingInfoAPI
urlpatterns = [
path('admin/', admin.site.urls),
path('api/buildingdata/', BuildingInfoAPI.as_view()),
# added <str:building> to url
path('api/buildingdata/<str:building>', ReviewListAPI.as_view())
]
And in your views.py:
# Create your views here.
from django.shortcuts import render
from rest_framework.response import Response
from .models import reviewData
from .models import buildingData
from rest_framework.views import APIView
from .serializers import ReviewSerializer
class BuildingInfoAPI(APIView):
def get(request):
queryset = buildingData.objects.all()
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
class ReviewListAPI(APIView):
# added another argument, retrieved from url
def get(request, building):
# changed .all() to .filter()
queryset = reviewData.objects.filter(building_name = building)
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
Hello developer I'm beginner to django , im following a tutorial in which he create concrete views classes and convert them to viewset.ModelViewSet classes. and he use default router in urls.py
my app showing list of articles with viewset but not perform the post method with the ArticleViewSet(viewset.ModelViewSet)
so im confused about it to use viewset
This is my api/view.py file im using Concrete View Classes in which i using concrete view classses **
class ArticleListView(ListAPIView):
queryset = Articles.objects.all()
serializer_class = ArticleSerializer
class ArticleDetailView(RetrieveAPIView):
queryset = Articles.objects.all()
serializer_class = ArticleSerializer
class ArticleUpdateView(UpdateAPIView):
queryset = Articles.objects.all()
serializer_class = ArticleSerializer
class ArticleDeleteView(DestroyAPIView):
queryset = Articles.objects.all()
serializer_class = ArticleSerializer
class ArticleCreateView(CreateAPIView):
permission_classes = []
#parser_classes = (FormParser,MultiPartParser, FileUploadParser )
serializer_class = ArticleSerializer
queryset = Articles.objects.all()
#serializer = ArticleSerializer(queryset, many=True)
def post(self, request):
serializer = ArticleSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(True, status=200)
# class ArticleViewSet(viewsets.ModelViewSet):
# parser_classes = (FormParser,MultiPartParser, FileUploadParser )
# serializer_class = ArticleSerializer
# queryset = Articles.objects.all()
**this are my urls patterns api/url.py in Article app **
urlpatterns = [
path('articles', ArticleListView.as_view() name=""),
path('xyz', ArticleCreateView.as_view()),
path('<pk>', ArticleDetailView.as_view()),
path('<pk>/update/', ArticleUpdateView.as_view()),
path('<pk>/delete/', ArticleDeleteView.as_view())
]
#from articles.api.views import *
# from rest_framework.routers import DefaultRouter
# router = DefaultRouter()
# router.register(r'', ArticleViewSet, basename='articles')
# urlpatterns = router.urls
**These are my url patterns of urls.py in settings folder **
urlpatterns = [
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('rest-auth/', include('rest_auth.urls')),
path('rest-auth/registration/', include('rest_auth.registration.urls')),
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
path('api/', include('articles.api.urls')),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
I'm not sure, I did only a rapid search and your given info was not complete but I think you should define the below functions to give the functionality to your viewSet.
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
or you can write your own viewsets rather than overwriting them
class UserViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving users.
"""
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
fro more accurate details you can see the documentation
I would like to filter the Post objects also with the slug, how should one go about this?
The model looks as follows:
class Post(models.Model):
slug = models.SlugField()
title = models.CharField(max_length=100)
post_json = models.CharField(max_length=100000, blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
and the view looks like this.
def load_post_json(request):
obj = Post.objects.filter(author_id=request.user.id)
data = list(obj.values())
return JsonResponse({'posts': data})
In the url you can capture the slug:
# app/urls.py
from django.urls import path
from app.views import load_post_json
urlpatterns = [
# …
path('post/<slug:slug>/', load_post_json, name='post_json'),
# …
]
Then in your view, you can filter in the slug:
# app/views.py
from django.contrib.auth.decorators import login_required
#login_required
def load_post_json(request, slug):
obj = Post.objects.filter(author_id=request.user.id, slug=slug)
data = list(obj.values())
return JsonResponse({'posts': data})
That being said, it might be better to use the Django REST framework to define an API. This has tooling for serializers, etc. and thus makes it more convenient to define sets of views to obtain a list of items, create new ones, etc.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
You mean you want to filter by slug too?
If you submit it as a query parameter, simply write:
def load_post_json(request):
slug_name= request.data.get('slug_name')
obj = Post.objects.filter(author_id=request.user.id, slug=slug_name)
data = list(obj.values())
return JsonResponse({'posts': data})
If you want to extract it from the URL,capture it on urlpatterns in urls.py file:
urlpatterns = [
# …
path('post/<slug_name:slug_name>/', load_post_json),
# …
]
then, get it as a function parameter in your load_post_json function:
def load_post_json(request, slug_name):
...
Here another alternative way,
# app/urls.py
from django.urls import path
from app.views import load_post_json
urlpatterns = [
# …
path('post/', load_post_json, name='post_json'),
# …
]
and the views.py
# app/views.py
from django.contrib.auth.decorators import login_required
#login_required
def load_post_json(request):
slug = request.GET.get('slug', None)
obj = Post.objects.filter(author_id=request.user.id, slug=slug)
data = list(obj.values())
return JsonResponse({'posts': data})
and the url endpoint,
/post/?slug=<your slug here>