DRF request is not defined for getting current user id - python-3.x

So What I've been trying to do is to have my API view only return objects that have their attributes post_user to the current id of the logged in user. These post_user attributes are populated as whenever I post it populates the variable with the current user's id through my serializer.
However, I am not successful as it says request is not defined. I just want to get the current user's id so that I can use it to filter my object returns
views.py
# To retrieve and list all posts with DRF
class ListPosts(generics.ListCreateAPIView):
queryset = Posts.objects.get(post_user=request.user.id)
serializer_class = PostsSerializer
permission_classes = (permissions.IsAuthenticated,)
serializers.py
# serializer for posts to be taken
class PostsSerializer(serializers.ModelSerializer):
class Meta:
model = Posts
fields = ('id','post_title','post_content',)
def create(self, validated_data):
posts = Posts.objects.create(
post_title=validated_data['post_title'],
post_content=validated_data['post_content'],
# gets the id of the current user
post_user=self.context['request'].user.id,
)
posts.save()
return posts

error is in line
queryset = Posts.objects.get(post_user=request.user.id)
here request is not define at class declaration time. Solution is you can override the get_queryset method.
class ListPosts(generics.ListCreateAPIView):
queryset = Posts.objects.all()
serializer_class = PostsSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self, *args, **kwargs):
return Posts.objects.filter(post_user=self.request.user)

Inherit CreateModelMixin's features inside PostsSerializer and try to define your create() method like def create(request, *args, **kwargs).
Finally, you can try to get user id using request.user.id.
For a better documentation, you can check https://www.django-rest-framework.org/api-guide/generic-views/.
Also check what are Mixins and why do we use it (if you do not know).
For a little and brief definition, Mixins are just class with methods that can be mostly inherited and used by our views.
If you have any doubt, please comment.

Related

DRF ViewSet extra action (`#action`) serializer_class

When I try to use Django Rest Framework extra actions on a viewset, I can not make the decorator's serializer_class work.
class ClientViewSet(ModelViewSet):
queryset = Client.objects.all()
serializer_class = ClientSerializer
def get_queryset(self):
# Do things
def get_serializer_class(self):
if self.action in ["create"]:
return CreateClientSerializer
elif self.action in ["retrieve"]:
return ClientDetailSerializer
return self.serializer_class
#action(detail=True, methods=["get"], serializer_class=ClientDetailSerializer)
def get_by_name(self, request, name=None):
"""
Get one Client searching by name.
#param request:
#param name: Client code
#return: Response
"""
queryset = get_object_or_404(Client, name__iexact=name)
serializer = self.get_serializer(queryset)
return Response(serializer.data)
So, even if the extra action is supposedly overriding the ViewSet default serializer class, I still get ClientSerializer instead of ClientDetailSerializer.
The official documentation states that...
The decorator allows you to override any viewset-level configuration such as permission_classes, serializer_class, filter_backends...:
My get_serializer_class override defaults to the ViewSet serializer_class attribute for my extra actions. If I understand correctly, this is basically what GenericAPIView get_serializer_class does under the hood:
def get_serializer_class(self):
"""
(...)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
I guess I'm missing something obvious here. Just can not figure out what...
Any help is appreciated. Thanks in advance :)
Why not use it like this? I'm guessing you're doing something wrong in get_serializer_class.
#action(detail=True, methods=["get"], serializer_class=ClientDetailSerializer)
def get_by_name(self, request, name=None):
"""
Get one Client searching by name.
#param request:
#param name: Client code
#return: Response
"""
object = get_object_or_404(Client, name__iexact=name)
serializer = ClientDetailSerializer(object)
return Response(serializer.data)
When you override the get_serializer_class without calling the super of this class, the super class doesn't run.
user this:
def get_serializer_class(self):
if self.action in ["create"]:
return CreateClientSerializer
elif self.action in ["retrieve"]:
return ClientDetailSerializer
return super().get_serializer_class()

List Class View didn't return an HttpResponse

I've been trying to get a class-based list view to display all entries under a user's account (applicant), but when loading the page I'm given the following error:
The view jobassessment.views.view didn't return an HttpResponse object. It returned None instead.
To me that sounds like the URL dispatcher isn't running the correct view, but this is my URL file for both the whole site and the jobassessment application and I can't seem to spot the fault.
Site URL.py:
urlpatterns = [
path('admin/', admin.site.urls, name="admin"),
path('accounts/', include('django.contrib.auth.urls'), name="accounts"),
path('applicant/', include('userprofile.urls'), name="applicant"),
path('assessments/', include('jobassessment.urls')),
]
JobAssessment App's URL.py:
from django.urls import path
from . import views
urlpatterns = [
path("", views.AssessmentListView.as_view(), name="assessment"),
]
This is my ListView that is called:
class AssessmentListView(LoginRequiredMixin, generic.ListView):
model = Assessment
template_name ='assessments_index.html'
paginate_by = 5
def get(self, request, *args, **kwargs):
# Ensure they have first created an Applicant Profile
if not Applicant.objects.filter(user=self.request.user).exists():
messages.info(request, "You must create a profile before you can view any assessments.")
return redirect('profile_create_form')
def get_queryset(self):
return Assessment.objects.all().filter(applicant=Applicant.objects.filter(user=self.request.user)).order_by('-assessment_stage')
If Applicant of current login user not exists then your if condition fails and since there is no else
part in there so there is no HttpResponse returned from the view. So please add else part if applicant exists and return HttpResponse()
class AssessmentListView(LoginRequiredMixin, generic.ListView):
model = Assessment
template_name ='assessments_index.html'
paginate_by = 5
def get(self, request, *args, **kwargs):
# Ensure they have first created an Applicant Profile
if not Applicant.objects.filter(user=self.request.user).exists():
messages.info(request, "You must create a profile before you can view any assessments.")
return redirect('profile_create_form')
else:
return HttpResponse() #<------ add corresponding HttpResponse if Applicant exists.
def get_queryset(self):
return Assessment.objects.all().filter(applicant=Applicant.objects.filter(user=self.request.user)).order_by('-assessment_stage')
Following the django document on ListView filter it's better to handle it within get_queryset. So for your case it would be something like this:
class AssessmentListView(LoginRequiredMixin, generic.ListView):
model = Assessment
template_name ='assessments_index.html'
paginate_by = 5
def get_queryset(self):
# Ensure they have first created an Applicant Profile
if not Applicant.objects.filter(user=self.request.user).exists():
messages.info(request, "You must create a profile before you can view any assessments.")
return redirect('profile_create_form')
else:
return Assessment.objects.all().filter(applicant=Applicant.objects.filter(user=self.request.user)).order_by('-assessment_stage')

DRF permissions best practise DRY

Whats the best way to do view permissions in DRF based on user type currently?
In my structure there are several user_types and for example TEAM_LEADER cant create a team object but can see the list of teams. Which means for the same class view i want to use different permissions for POST and GET for example.
I'm looking to do this as dry as possible and i'm trying to follow the skinny view fat models design principle(also wondering if that's good practice to follow in 2021).
models.py for the user model
class User(AbstractBaseUser):
...fields here
objects = UserManager()
USERNAME_FIELD = "email"
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
if perm.Meta.verbose_name=="worksite" and perm.request.method =="POST":
if self.user_type <= self.DEPARTMENT_MANAGER:
return True
else:
return False
return True
views.py
class DashboardPermissions(BasePermission):
message="You dont have permission for this action"
def has_permission(self, request, view):
return request.user.has_perm(view.Meta.verbose_name)
class ViewName(CreateAPIView):
permission_classes = (IsAuthenticated,DashboardPermissions)
authentication_classes = ()
serializer_class = WorksiteSerializer
queryset = Worksite.objects.all()
class Meta:
verbose_name="view_name"
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
Bonus question would my solution create any performance issues?
Creating your custom Permission class is good practice. So that part looks OK to me. We could debate on whether the logic should be in the Permission or the User (like you did), but that's not a big deal.
If you want to have different permissions for different endpoints within your view, simply override the get_permissions method.
# Inherited method from APIView
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
As you can see, for ALL services, it will fetch the permissions from self.permission_classes.
To use different permissions between GET/CREATE, you could create a dict of endpoint: [...permissions] and override get_permissions to fetch the one matching the current action
permissions = {
"create": [P1, P2,],
"get": [P1,]
}
def get_permissions(self):
action = self.it_is_somewhere_in_there
return [permission() for permissions in self.permissions[action]]
#JordanKowal's answer is correct, but as mentioned in the comments,
Also then i'd be repeating the permissions dict a lot ? in order to do it for every class of view right
For this you can create a mixin class. What it essentially allows you to do is move some code/feature that is to duplicated in multiple views to a standalone class and just inherit from it as per your convenience.
To extend on Jordan's answer, here's what a mixin class would look like:
class DefaultPermissionsMixin(object):
permissions = {
"create": [IsAuthenticated, DashboardPermissions],
"get": [DashboardPermissions]
}
def get_permissions(self):
# default `get_permissions` method
# reads `self.permission_classes`
perms = super().get_permissions()
if self.action in self.permissions.keys():
return perms + [p() for p in self.permissions[self.action]]
else:
return perms
class View1(CreateAPIView, DefaultPermissionsMixin):
# ...snip...
class View2(CreateAPIView, DefaultPermissionsMixin):
# i can overwrite here per my convenience
permissions = {
"create": [DashboardPermissions],
"delete": [],
}
# i can also define permissions the default way
# that will be enabled on all actions
permission_classes = [IsAuthenticated]
# ...snip...

How to get a request object from inside the generic.ListAPIView class for django-filter?

I have a django-filter query running using djangorestframework. The below view function is used for url. But when I get objects at the starting, I want to filter them not only by the parameters the search query has which are username and userid. But I also want to filter based upon who is logged in, which I can get through request object coming from URL.
The function getUsername() works independently but not with other code.
Hence is there a way to access self request in below code. Any help is greatly appreciated.
class userSearch(generics.ListAPIView):
def getUsername(self):
return self.kwargs['username']
serializer_class = UserSerializer
queryset = UserProfile.objects.filter(professor=User.objects.get(username=self.getUsername()).id).order_by('username')
filter_backends = (DjangoFilterBackend,SearchFilter)
filter_fields = (username,userid,userType)
search_fields = ('username', 'first_name')
Instead of defining the queryset attribute directly, you can override the get_queryset(self) method:
class userSearch(generics.ListAPIView):
serializer_class = UserSerializer
filter_backends = (DjangoFilterBackend, SearchFilter)
filter_fields = ('username', 'userid')
search_fields = ('username', 'first_name')
def get_queryset(self):
username = self.kwargs['username']
professor = User.objects.get(username=username)
# Here you can do the following thing:
current_user = self.request.user
# And use it as you wish in the filtering below:
return UserProfile.objects.filter(professor=professor).order_by('username')

How to implement django admin queryset filter with from extended user model class

I have extended Django default User model (just to save user state_id) class with following model.
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,)
state_id = models.IntegerField(blank=False)
def __str__(self):
return self.user.username
And now I want to filter the data by logged user's state_id. I tried to implement queryset filter but it does not do anything.
class VisVisitsAdmin(admin.ModelAdmin):
list_per_page = 10
list_display = ('visit_id','visit_no','user_name','mobile_number','program_name','state_name','district_name','block_name','school_name',)
list_filter = ('date_of_visit',)
def queryset(self, request):
qs = super(VisVisitsAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(state_id=request.user.UserProfileInfo.state_id)
def state_name(self, obj):
if obj.school_program:
return obj.school_program.school.cluster.block.district.state.name_of_state
state_name.short_description = 'state name'
You are trying to fetch the state_id from the userprofileinfo instance related to user object.
Everything seems fine but the instance is available as userprofileinfo and not UserProfileInfo , so change qs as :
qs.filter(state_id=request.user.userprofileinfo.state_id)

Resources