Define Django Rest Framework view with flaggit - django

I'm trying to use this django app https://github.com/TheBimHub/django-flaggit
I have installed the flaggit via pip. Then in views.py I have
import flaggit
def flag_thread(request, **kwargs):
thread_id = request.GET.get('thread_id')
thread = Thread.objects.find(id=thread_id)
flaggit.utils.flag(thread, user=None, ip=None, comment=None)
is this correct? what does urls.py look like?

First make a serializer
from flaggit.models import FlagInstance
...
class FlagInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = FlagInstance
fields = '__all__'
then use the serializer in a new endpoint
class ThreadViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
queryset = Thread.objects.all().order_by('-created_at')
pagination_class = ThreadViewSetPaginationClass
#action(detail=True, methods=['post'])
def flag(self, request, pk=None):
thread = self.get_object()
flag_instance = flaggit.utils.flag(thread, user=request.user, ip=None, comment=None)
serializer = FlagInstanceSerializer(data=flag_instance)
serializer.is_valid()
return Response(serializer.data)
I went ahead and fixed the migrations and admin panel in a fork https://github.com/morenoh149/django-flaggit

Related

partial update in django rest viewset(not modelviewset)

i am beginner in django and i was trying to learn work with djangorest viewset(not modelviewset)
and i cant find any resource to undrestand it.
i want to learn how to write a partial_update with viewset.
here is what i have tried:
models.py :
class UserIdentDocs(models.Model):
owner = models.ForeignKey(User,on_delete=models.CASCADE, related_name = "user_ident_Docs")
code = models.PositiveBigIntegerField(null=True)
img = models.ImageField(null=True)
video = models.FileField(null=True)
is_complete = models.BooleanField(default=False)
serializers.py:
class AdminUserIdentSerializer(serializers.ModelSerializer):
class Meta:
model = UserIdentDocs
fields = [
"owner",
"code",
"img",
"video",
"is_complete",
]
views.py:
from rest_framework.parsers import MultiPartParser, FormParser
class UserDocAdminViewSet(viewsets.ViewSet):
"""User docs admin view set"""
permission_classes = [AllowAny]
parser_classes = (MultiPartParser, FormParser)
serializer_class = AdminUserIdentSerializer
queryset = UserIdentDocs.objects.filter(is_complete=False)
def list(self, request):
serializer = self.serializer_class(self.queryset, many=True)
return Response(serializer.data)
def retrive(self, request, pk=None):
doc_object = get_object_or_404(self.queryset, pk=pk)
serializer = self.serializer_class(doc_object)
return Response(serializer.data)
def create(self, request ):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
def partial_update(self, request, pk=None):
doc_object = get_object_or_404(self.queryset, pk=pk)
serializer = self.serializer_class(doc_object,data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail":"item updated succesfuly"}, status=status.HTTP_205_RESET_CONTENT)
urls.py:
urlpatterns = [
...
# admin viewset for docs
# list of accounts with is_complete=False
path("admin/userdocs/", UserDocAdminViewSet.as_view({"get":"list", "post":"create"}), name="user-docs-list"),
path("admin/userdocs/<int:pk>", UserDocAdminViewSet.as_view({"get":"retrive","patch":"partial_update"}), name="user-docs-detail"),
]
i can create user in browsable api but when i want to use partial update i can't even see the fields in browsable api and i only see this :
media type: multipart/form-data
content:this is what i see

How to use Filterset and paginateion in ApiView?

I am trying to use PageNumberPagination and FilterSet in APIView.
But I have an error below in my code.
object of type 'ListSerializer' has no len()
How to implement this?
Here are the code:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
class MyFilter(filters.FilterSet):
class Meta:
model = MyModel
fields = '__all__'
class MyAPIView(views.APIView, PageNumberPagination):
def get(self, request, *args, **kwargs):
filterset = MyFilter(request.query_params, queryset=MyModel.objects.all())
if not filterset.is_valid():
raise ValidationError(filterset.errors)
serializer = MySerializer(instance=filterset.qs, many=True)
paginate_queryset = self.paginate_queryset(serializer, request, view=self)
return Response(paginate_queryset.data)
Django 3.2.6
django-filter 22.1
djangorestframework 3.12.4
Python 3.8
You should not try to make your object inherit from APIView and PageNumberPagination, as one is a view, and the other one, a paginator.
If you want to keep your code as simple as possible, just use a ListAPIView:
class MyAPIView(ListAPIView):
queryset = MyModel.objects.all()
serializer_class = MySerializer
filterset_class = MyFilter
pagination_class = PageNumberPagination
If you want to get this pagination as the default one, you can even set it as the default pagination style once so you don't have to define it in each of the views that use it.
Apply Pagination on ApiView
views.py
from rest_framework.pagination import PageNumberPagination
class EventNewsItems(APIView, PageNumberPagination):
def get(self, request, pk, format=None):
event = Event.objects.get(pk=pk)
news = event.get_news_items().all()
results = self.paginate_queryset(news, request, view=self)
serializer = NewsItemSerializer(results, many=True)
return self.get_paginated_response(serializer.data)
I changed the code and it works
class MyAPIView(views.APIView, PageNumberPagination):
def get(self, request):
filterset = MyFilter(request.query_params, queryset=MyModel.objects.all())
results = self.paginate_queryset(filterset.qs, request, view=self)
serializer = Serializer(results, many=True)
return self.get_paginated_response(serializer.data)

Increasing the view by 1 each time some url hits?

Here I want to increase the number of views by 1 each time the detail view is called.How can I do it ?
class Package(models.Model):
name = models.CharField(max_length=255,unique=True)
slug = AutoSlugField(populate_from='name')
package_desc = models.TextField()
views = models.IntegerField(default=0) #want to increase views by 1 when detail_package url hits
views.py
class DetailPackage(generics.RetrieveAPIView):
serializer_class = PackageDetailSerializer
lookup_field = 'slug'
queryset = Package.objects.all()
urls.py
path('<slug>/detail/', DetailPackage.as_view(), name='detail_package'),
I am very new to django rest framework.So am I going the right way by using the generics API view for such cases ?
You can override the retrieve method on (generics.RetrieveAPIView)..take a look at this http://www.cdrf.co/3.9/rest_framework.generics/RetrieveAPIView.html#retrieve
Here is an example
from django.db.models import F
class DetailPackage(generics.RetrieveAPIView):
...
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
# increment the value
type(instance).objects.filter(pk=instance.pk).update(
views=F('views') + 1,
)
return Response(serializer.data)
As #Aprimus suggested I solved this by overriding the retrieve() method like this:
Please correct me if I didn't do it correctly.
class DetailPackage(generics.RetrieveAPIView):
serializer_class = PackageDetailSerializer
lookup_field = 'slug'
queryset = Package.objects.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.views += 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)

How can I make a Django REST framework /me/ call?

Suppose I have a ViewSet:
class ProfileViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows a user's profile to be viewed or edited.
"""
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly)
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
...and a HyperlinkedModelSerializer:
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Profile
read_only_fields = ('user',)
I have my urls.py set up as:
router.register(r'profiles', api.ProfileViewSet, base_name='profile')
This lets me access e.g. /api/profile/1/ fine.
I want to set up a new endpoint on my API (similar to the Facebook API's /me/ call) at /api/profile/me/ to access the current user's profile - how can I do this with Django REST Framework?
Using the solution by #Gerard was giving me trouble:
Expected view UserViewSet to be called with a URL keyword argument named "pk". Fix your URL conf, or set the .lookup_field attribute on the view correctly..
Taking a look at the source code for retrieve() it seems the user_id is not used (unused *args)
This solution is working:
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from rest_framework import filters
from rest_framework import viewsets
from rest_framework import mixins
from rest_framework.decorators import list_route
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
serializer_class = UserSerializer
User = get_user_model()
queryset = User.objects.all()
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter)
filter_fields = ('username', 'email', 'usertype')
search_fields = ('username', 'email', 'usertype')
#list_route(permission_classes=[IsAuthenticated])
def me(self, request, *args, **kwargs):
User = get_user_model()
self.object = get_object_or_404(User, pk=request.user.id)
serializer = self.get_serializer(self.object)
return Response(serializer.data)
Accessing /api/users/me replies with the same data as /api/users/1 (when the logged-in user is user with pk=1)
You could create a new method in your view class using the list_route decorator, like:
class ProfileViewSet(viewsets.ModelViewSet):
#list_route()
def me(self, request, *args, **kwargs):
# assumes the user is authenticated, handle this according your needs
user_id = request.user.id
return self.retrieve(request, user_id)
See the docs on this for more info on #list_route
I hope this helps!
You can override the get_queryset method by filtering the queryset by the logged in user, this will return the logged in user's profile in the list view (/api/profile/).
def get_queryset(self):
return Profile.objects.filter(user=self.request.user)
or
def get_queryset(self):
qs = super(ProfileViewSet, self).get_queryset()
return qs.filter(user=self.request.user)
or override the retrieve method like so, this will return the profile of the current user.
def retrieve(self, request, *args, **kwargs):
self.object = get_object_or_404(Profile, user=self.request.user)
serializer = self.get_serializer(self.object)
return Response(serializer.data)
From Gerard's answer and looking at the error pointed out by delavnog, I developed the following solution:
class ProfileViewSet(viewsets.ModelViewSet):
#list_route(methods=['GET'], permission_classes=[IsAuthenticated])
def me(self, request, *args, **kwargs):
self.kwargs.update(pk=request.user.id)
return self.retrieve(request,*args, **kwargs)
Notes:
ModelViewSet inherits GenericAPIView and the logic to get an object is implemented in there.
You need to check if the user is authenticated, otherwise request.user will not be available. Use at least permission_classes=[IsAuthenticated].
This solution is for GET but you may apply the same logic for other methods.
DRY assured!
Just override the get_object()
eg.
def get_object(self):
return self.request.user
Just providing a different way. I did it like this:
def get_object(self):
pk = self.kwargs['pk']
if pk == 'me':
return self.request.user
else:
return super().get_object()
This allows other detail_routes in the ViewSet to work like /api/users/me/activate
I've seen quite a few fragile solutions so I thought I'll respond with something more up-to-date and safer. More importantly you don't need a separate view, since me simply acts as a redirection.
#action(detail=False, methods=['get', 'patch'])
def me(self, request):
self.kwargs['pk'] = request.user.pk
if request.method == 'GET':
return self.retrieve(request)
elif request.method == 'PATCH':
return self.partial_update(request)
else:
raise Exception('Not implemented')
It's important to not duplicate the behaviour of retrieve like I've seen in some answers. What if the function retrieve ever changes? Then you end up with a different behaviour for /me and /<user pk>
If you only need to handle GET requests, you could also use Django's redirect. But that will not work with POST or PATCH.
Considering a OneToOneField relationship between the Profile and the User models with related_name='profile', I suggest the following as the #list_route has been deprecated since DRF 3.9
class ProfileViewSet(viewsets.GenericViewSet):
serializer_class = ProfileSerializer
#action(methods=('GET',), detail=False, url_path='me', url_name='me')
def me(self, request, *args, **kwargs):
serializer = self.get_serializer(self.request.user.profile)
return response.Response(serializer.data)

Django Rest Framework - How to get request.user on views.py?

I'm using the Django rest framework. I need to get the user on views.py but I not able to.
from app.models import SmsToSend
from app.serializers import SmsToSendSerializer
from rest_framework import generics
from app.permissions import IsOwner
from rest_framework import permissions
class SmsToSendList(generics.ListCreateAPIView):
queryset = SmsToSend.objects.all()
serializer_class = SmsToSendSerializer
permission_classes = (IsOwner, permissions.IsAuthenticated)
def pre_save(self, obj):
obj.owner = self.request.user
How can I call request.user here?
Best Regards,
You are very close. I think a lot of people spend time trying to figure this out. Define request as a parameter in your view method and you will have access to it like this.
def pre_save(self, request, obj):
obj.owner = request.user
You can get your queries like this.
request.REQUEST.get('<query>')
In a serializer method it is slightly different, you can get data like this.
request = self.context['request']
user = request.user
query = request.GET['<query>']
Hope this is helpful!
class SmsToSendList(generics.ListCreateAPIView):
#queryset = SmsToSend.objects.all()
serializer_class = SmsToSendSerializer
permission_classes = (IsOwner, permissions.IsAuthenticated)
def get_queryset(self):
user = self.request.user
return SmsToSend.objects.filter(owner=user)
def pre_save(self, obj):
obj.owner = self.request.user
Instead of the queryset I had to use get_queryset method
def get_queryset(self, *args, **kwargs):
user_id = self.request.user.id