How to get authorized user object in django-tastypie - django

I need to get authorized user object in hydrate method, something like that:
class SalepointResource(ModelResource):
def hydrate(self, bundle):
user = bundle.request.user
But request here is empty HttpRequest object, and doesn't have user method, although user is authorized. Is there any way to get user object?

With TastyPie 0.9.15, I find this works:
def hydrate_user(self, bundle):
bundle.obj.user = bundle.request.user
return bundle
with no need for subclassing ModelResource. Here user is a ForeignKey of the model and the resource.
I'm posting this as an answer because although it looks simple, it took me a long time to figure out.

Have you set up authentication/authorization properly in tastypie?

Not sure if this is the best approach, but I got around this problem by subclassing the ModelResource class and overriding some of its methods. In ModelResource the request object (which contains user) is a parameter to the obj_update method but it is not passed on to the full_hydrate method, which in turn calls hydrate. You have to make a few small changes to each of these methods to pass the request object all the way down the chain.
The method modifications are trivial. In detail:
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, ValidationError
from tastypie.resources import ModelResource
from tastypie.exceptions import NotFound, BadRequest, InvalidFilterError, HydrationError, InvalidSortError, ImmediateHttpResponse
class MyModelResource(ModelResource):
def obj_create(self, bundle, request=None, **kwargs):
...
bundle = self.full_hydrate(bundle, request)
...
def obj_update(self, bundle, request=None, **kwargs):
...
bundle = self.full_hydrate(bundle, request)
...
def full_hydrate(self, bundle, request=None):
...
bundle = self.hydrate(bundle, request)
...
def hydrate(self, bundle, request=None):
...
return bundle
Then make your resource a subclass of this new class and override the new version of hydrate:
class MyModelResource(MyModelResource):
class Meta:
queryset = MyModel.objects.all()
def hydrate(self, bundle, request):
bundle.obj.updated_by_id = request.user.id
return bundle
I haven't tested this thoroughly but it seems to work so far. Hope it helps.

Related

Django rest framework creating Orders and order items

I want to create a Order and order items.
For this i am simply creating new model object in views.py using CreateApiView but i am receiving error that "Serializer_class" should be included but i don't need serializer for this.
//views.py
class CreateOrder(CreateAPIView):
def Post(self,request):
header_token = request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
access_token = header_token.split(' ')[1]
status,user = validate_token(access_token)
cart=Cart.objects.get(user=user)
print(cart)
if cart:
total=cart.total
userprofile=UserProfile.objects.get(user=user)
order,created=Order.objects.get_or_create(billing_profile=userprofile,total=total)
cart_items=CartItem.objects.get(cart=cart)
print(cart_items)
for item in cart_items:
itemid=item.item_id
qty=item.quantity
item_instance = Items.objects.get(item_id=item)
order_item,created = OrderItems.objects.get_or_create(order=order, product=item_instance,quantity=qty)
order.save()
order_item.save()
if created:
item.delete()
return Response (status=rt_status.HTTP_200_OK)
I want to understand how to achieve this with or without serializer
You are overriding the incorrect post method. If you look at the source code of CreateAPIView you will see the method named as shown below.
class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
NOTE: The method is all lower case.
This method calls self.create which is derived from the CreateModelMixin and this method needs a serializer.
If you need something light weight where a serializer is not needed I would suggest using APIView.
from rest_framework.views import APIView
class CreateOrder(APIView):
def post(self, request):
....

Django: access to user info from admin.py for methods with no request object?

Django 1.11.
In admin.py I have:
class AuditAdmin(DeactivateMixin, admin.ModelAdmin):
"""all the superclass stuff"""
from which I subclass my model's stuff with various custom methods:
class SomeModelAdmin(AuditAdmin):
list_display = ["filed1", "filed2", "field3"]
def get_queryset(self, request):
if request.user.is_superuser:
#do something extra
def inline_add_somemodelattribute1(self, my_object):
#how to access user if I don't have request ?
So inside inline_add_somemodelattribute1 method I need to make decision based on the user but that method does not take request as an argument. How do I get my user data in this case? I did not find anything relevant in self or my_object
Thanks
Easiest way of access current request is using crequest. You can use it like this:
from crequest.middleware import CrequestMiddleware
class SomeModelAdmin(...):
...
def inline_add_somemodelattribute1(self, my_object):
crequest = CrequestMiddleware.get_request()
# your logics here for example: if crequest.user.pk == 1:
You may consider setting some values that you may require in your custom methods related to request user in changelist_view method as :
def changelist_view(self, request, extra_context=None):
# setting is_superuser attribute to be used in custom methods.
setattr(self, 'is_superuser', request.user.is_superuser)
return super().changelist_view(request, extra_context)

Django rest framework- calling another class-based view

I have pored over several similar posts (and Calling a class-based view of an app from another app in same project seemed promising, but does not work), but some are older and none quite work for me. Here's my setup (using Django==2.0.6, djangorestframework==3.8.2)
I have a basic model (simplified here):
from django.db import models
class Resource(models.Model):
name = models.CharField(max_length=100, null=False)
I have a basic endpoint where I can list and create Resource instances:
from rest_framework import generics, permissions
from myapp.models import Resource
from myapp.serializers import ResourceSerializer
class ListAndCreateResource(generics.ListCreateAPIView):
queryset = Resource.objects.all()
serializer_class = ResourceSerializer
permission_classes = (permissions.IsAuthenticated,)
(afaik, the details of the serializer are not relevant, so that is left out).
Anyway, in addition to that basic endpoint, I have another API endpoint which performs some actions, but also creates some Resource objects in the process. Of course, I would like to make use of the functionality encapsulated in the ListAndCreateResource class so I only have to maintain one place where Resources are created.
I have tried:
Attempt 1:
class SomeOtherView(generics.CreateAPIView):
def post(self, request, *args, **kwargs):
# ... some other functionality...
# ...
response = ListAndCreateResource().post(request, *args, **kwargs)
# ... more functionality...
return Response({'message': 'ok'})
Unfortunately, that does not work for me. In my trace, I get:
File "/home/projects/venv/lib/python3.5/site-packages/rest_framework/generics.py", line 111, in get_serializer
kwargs['context'] = self.get_serializer_context()
File "/home/projects/venv/lib/python3.5/site-packages/rest_framework/generics.py", line 137, in get_serializer_context
'request': self.request,
AttributeError: 'ListAndCreateResource' object has no attribute 'request'
Attempt 2:
This attempt tries to use the as_view method which is part of all Django class-based views:
class SomeOtherView(generics.CreateAPIView):
def post(self, request, *args, **kwargs):
# ... some other functionality...
# ...
response = ListAndCreateResource.as_view()(request, *args, **kwargs)
# ... more functionality...
return Response({'message': 'ok'})
But that gives up with:
AssertionError: The `request` argument must be an instance of `django.http.HttpRequest`, not `rest_framework.request.Request`
So my question is...is there a straightforward way to do this? I can access the _request attribute of the rest_framework.request.Request object (which is of type django.http.HttpRequest, but then I do not have any of the authentication details that are contained in the DRF Request object (indeed, my ListAndCreateResource returns a 403 if I use response = ListAndCreateResource().as_view()(request._request, *args, **kwargs) in attempt #2 above).
Thanks in advance!
This seems a bit late, but in case anyone is wondering.
class SomeOtherView(generics.CreateAPIView):
def post(self, request, *args, **kwargs):
# ... some other functionality...
# ...
response = ListAndCreateResource.as_view()(request, *args, **kwargs)
# ... more functionality...
return Response({'message': 'ok'})
The as_view() is a function that when called, returns a function that takes a request, *args, **kwargs. So basically, a class view is an encapsulated function view.
I think you can use request._request. The DRF keeps a protected member _request, as is, received from the API call.
You can access the request with self.request in class based views.

Add object level permission to generic view

The situation is pretty simple:
I'm writing a multi-user blog system. The system should prevent non-owner to edit or delete a blog post. In my view I use generic view.
class BlogUpdateView(UpdateView):
...
I know I should use #method_decorator to decorate dispatch method. However, most example is just #method_decorator(login_required) or model level permission. How can apply object level permission to check whether request.user is the author of this blog post?
For example, I tried to use django-authority apps, and I have a BlogPermission class in this file. and I tried to define a method in this class e.g.
def blog_edit(self, ??, ??)
what should I put into this method?
And then call this like:
#method_decorator(permission_required('blog_permission.blog_edit(???)'))
What should I pass in here?
Update: After read method_decorator code, I find it can only accept function without argument. I think that's why permission_required doesn't work here. But what's the work around about this?
Update solution:
In dispatch method, I check the user permission and then return HttpResponseForbidden() if the user does not meet the permission.
You can do it using class-based-views:
class BlogEdit(UpdateView):
model = Blog
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm('blog_permission.blog_edit'):
return HttpResponseForbidden()
return super(BlogEdit, self).dispatch(request, *args, **kwargs)
# OR (for object-level perms)
def get_object(self, *args, **kwargs):
obj = super(BlogEdit, self).get_object(*args, **kwargs)
if not obj.user == self.request.user:
raise Http404 # maybe you'll need to write a middleware to catch 403's same way
return obj
Another option is to use UserPassesTestMixin (or user_passes_test for function-based).
class UserPassesTestMixin
When using class-based views, you can use the
UserPassesTestMixin to do this.
test_func()
You have to override the test_func() method of the class to
provide the test that is performed. Furthermore, you can set any of
the parameters of AccessMixin to customize the handling of
unauthorized users:
from django.contrib.auth.mixins import UserPassesTestMixin
class MyView(UserPassesTestMixin, View):
def test_func(self):
return self.request.user.email.endswith('#example.com')
We can now check if the self.request.user is allowed to process the details passed into the self.request.GET or self.request.POST.
class MyView(UserPassesTestMixin, View):
raise_exception = True # To not redirect to the login url and just return 403. For the other settings, see https://docs.djangoproject.com/en/3.2/topics/auth/default/#django.contrib.auth.mixins.AccessMixin
def test_func(self):
return (
self.request.user.is_staff
or self.request.user.has_perm('app.change_blog')
or self.request.user.email.endswith('#company.staff.com')
or is_requested_object_accessible(self.request.user, self.request.GET, self.request.POST) # If you have a custom checker
)
...

Get current user log in signal in Django

I am just using the admin site in Django. I have 2 Django signals (pre_save and post_save). I would like to have the username of the current user. How would I do that? It does not seem I can send a request Or I did not understand it.
Thanks
Being reluctant to mess around with thread-local state, I decided to try a different approach. As far as I can tell, the post_save and pre_save signal handlers are called synchronously in the thread that calls save(). If we are in the normal request handling loop, then we can just walk up the stack to find the request object as a local variable somewhere. e.g.
from django.db.models.signals import pre_save
from django.dispatch import receiver
#receiver(pre_save)
def my_callback(sender, **kwargs):
import inspect
for frame_record in inspect.stack():
if frame_record[3]=='get_response':
request = frame_record[0].f_locals['request']
break
else:
request = None
...
If there's a current request, you can grab the user attribute from it.
Note: like it says in the inspect module docs,
This function relies on Python stack frame support in the interpreter, which isn’t
guaranteed to exist in all implementations of Python.
If you are using the admin site why not use a custom model admin
class MyModelAdmin( admin.ModelAdmin ):
def save_model( self, request, obj, form, change ):
#pre save stuff here
obj.save()
#post save stuff here
admin.site.register( MyModel, MyModelAdmin )
A signal is something that is fired every time the object is saved regardless of if it is being done by the admin or some process that isn't tied to a request and isn't really an appropriate place to be doing request based actions
You can use a middleware to store the current user: http://djangosnippets.org/snippets/2179/
Then you would be able to get the user with get_current_user()
We can solve this problem using middleware classes.
Create singleton class in where will be storing user variable.
class Singleton(type):
'''
Singleton pattern requires for GetUser class
'''
def __init__(cls, name, bases, dicts):
cls.instance = None
def __call__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls.instance
class NotLoggedInUserException(Exception):
'''
'''
def __init__(self, val='No users have been logged in'):
self.val = val
super(NotLoggedInUser, self).__init__()
def __str__(self):
return self.val
class LoggedInUser(object):
__metaclass__ = Singleton
user = None
def set_user(self, request):
if request.user.is_authenticated():
self.user = request.user
#property
def current_user(self):
'''
Return current user or raise Exception
'''
if self.user is None:
raise NotLoggedInUserException()
return self.user
#property
def have_user(self):
return not user is None
Create own middleware class that will be setting user for LoggedInUser instance,and insert out middleware after 'django.contrib.auth.middleware.AuthenticationMiddleware' in settings.py
from useranytimeaccess import LoggedInUser
class LoggedInUserMiddleware(object):
'''
Insert this middleware after django.contrib.auth.middleware.AuthenticationMiddleware
'''
def process_request(self, request):
'''
Returned None for continue request
'''
logged_in_user = LoggedInUser()
logged_in_user.set_user(request)
return None
In signals import LoggedInUser class and get current user
logged_in = LoggedInUser()
user = logged_in.user
In both signals, signal send three arguments,
Sender
Instance
Using
What you need is the Instant being modified...
def signal_catcher(sender, instance, **kwargs):
uname = instance.username
http://docs.djangoproject.com/en/dev/ref/signals/#pre-save