i'm new in Django. I'm trying to "override" a permission_required for inheriting view. Example:
class ValetView(PermissionRequieredMixin, View):
permission_required = ('lav.add_valets')
this works fine. If I go to this view, it's works dependient on /admin setted permissions. But now, I need to separate permission in ADD and EDIT, so I created this view inheriting from base:
class ValetsEditView(ValetsView)
ValetsView.permission_required = ('lav.edit_valets')
When I try to access any of two, it said: permission denied but I allow the user only edit feature, NOT add. How can I grant permission only for edit??
I hope it is understood. Thanks!
As Willem said the problem lies with ValetsView.permission_required. This shoudl be simply permission_required. Full code is:
class ValetView(PermissionRequieredMixin, View):
permission_required = 'lav.add_valets'
class ValetsEditView(ValetsView)
permission_required = 'lav.edit_valets'
That should now work for you. I've also removed the parenthesis around the permissions, as they were serving no purpose.
Something you didn't ask for
The following would also have been valid, as Django supports specifying a sequence of tuples. Note the trailing , below, which is required to specify a tuple of a single value in Python:
class ValetView(PermissionRequieredMixin, View):
permission_required = ('lav.add_valets',)
class ValetsEditView(ValetsView)
permission_required = ('lav.edit_valets',)
In your code you had ('lav.edit_valets') (without a trailing ,), which python does not interpret as a tuple, rather it just removes the parenthesis and sees it as the string 'lav.edit_valets'. Just one of those little quirks.
Your original code ValetsView.permission_required = ('lav.edit_valets') is modifying the parent view's permission_required. Change it to the following:
class ValetsEditView(ValetsView)
permission_required = ('lav.edit_valets')
Related
When I place the PermissionRequiredMixin as the most left paramenter, my requests get forwarded to the login URL even though the request is coming from an already authenticated user.
class ExampleViewSet(PermissionRequiredMixin, viewsets.ModelViewSet):
permission_required = ('example.example_view',)
When I place the PermissionRequiredMixin after the ModelViewSet the authenticated user is detected, however, the permission_required is ignored, and every user without the permission is allowed as well. And this answer suggested, that this is caused by the placement of the parameter, which leads to the first problem.
class ExampleViewSet(viewsets.ModelViewSet, PermissionRequiredMixin):
permission_required = ('example.example_view',)
How do I solve this problem?
It is not a problem. Order of inheritance classes is important. View base class have to set at last position. Mixins by theirs positions can override some function of the django view. The ordering of overriding function by the child class is in part defined by this ordering. First parent in the order will be called at first.
In your case, if you put a breakpoint in your PermissionRequiredMixin, you will see that python does not pass in it when you call your page
You can read some links as:
https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
http://python-history.blogspot.com/2010/06/method-resolution-order.html
How does Python's super() work with multiple inheritance?
I am subclassing the DecimalField class from Django RestFramework and need to access context. Formatting for decimal fields includes settings that are user-selectable, so I need to access the user object (which, I assume, should be inside context) inside the to_representation() method.
Via debug, I've looked at all properties of self inside my CustomDecimalField class, but, of course, I can't see class methods, so I don't know if there's an appropriate "get" method to get context. I've tried self.context (as a property), self.get_context(), and self.getcontext(), but none worked.
I found this announcement re: require_context:
https://www.django-rest-framework.org/community/3.11-announcement/
...but it seems to be valid only for validation and default value methods.
This seems like such a simple thing; hard to believe it is so difficult.
You have to manually set the user inside the context. So, in your view:
serializer = YourSerializer(your_data, context={'user': request.user})
And you will then be able to access it in your to_representation() method:
def to_representation(self, instance):
user = self.context.get("user")
Please see the documentation for more information.
I am trying to implement a web app in django where I have a class based view that inherits from PermissionRequiredMixin and generics.View.
Here is an example model:
class SomeModel(models.Model):
# Here I will put some model attributes
class Meta:
permissions = (
('some_permission', 'Can do some job'),
)
Until now I am not confused yet. However, when I am trying to write the view, I am confused. I read some Stack Overflow questions and also the django documentation, but I did not understand well because they are writing something like this:
class MyView(PermissionRequiredMixin, View)
permission_required = ('polls.can_open', 'polls.can_edit')
I tried to figure out what to write in my case, and I ended up in writing this:
class SomeView(PermissionRequiredMixin, View):
permission_denied_message = 'You don\'t have permission'
permission_required = 'app_name.some_permission'
What made me in doubt is that PyCharm did not auto-complete when I wrote the last line which is: permission_required = 'app_name.some_permission'. Also, I am not sure whether django will know which permission that I am talking about here.
Did I understand how to do the job? or I am wrong? Should I write it like this: permission_required = 'app_name.SomeModel.some_permission'?
I'm trying to manage my REST API like that :
http://xxx/users/userid[0-9]+/projects/projectid[0-9]+/tasks/taskid[0-9]+/
So I can access the JSON easily in my website. But, the thing is, I defined my view classes using the REST framework generic views. For example, here is my UserDetail view :
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
But of course I don't want all my users to be displayed, I just want my user with the ID userid to be displayed. I don't really know how to do it, I tried
queryset = User.objects.filter(id=userid)
but of course userid is not defined... Any help please ?
Edit : just to clarify, here is the url that leads to this view :
url(r'^users/(?P<pku>[0-9]+)/$', views.UserDetail.as_view(
), name='user-detail'),
First of all, If you want to use class based views, you should read some more about them. They are thoroughly explained on Django's docs and you can read about the specific generics of the framework you're using on the REST framework docs too. I'm not saying that you haven't read those, just that you seem to be missing some basic concepts that are explained there.
Now, to the problem at hand, if you look at the doc's of the generic view you're extending, you can see that it represents the endpoints for a single instance, meaning that it won't act on all your model's instances (as you seem to assume).
Also, you can see that this view is built on top of a series of other classes, the more critical one being GenericAPIView. On there you can see two things:
The queryset class field in this context is meant for filtering the posible instances you can manipulate, not obtaining the specific instance described on your url.
The lookup_field is the field that defines which attribute from your model will be used for getting the actual instance. You should define this field to whatever field you're going to use on your url to identify your object (generally it's pk). It's also important to note that the url should include a keyword argument corresponding to this value.
Internally, the view will take care of calling the get_object method, which usese the lookup_field value to find the specific model, and then feed that object to the serializer and return the result back to the client.
DISCLAIMER: I've never used Django REST framework, I put this answer togheter by reading the relevants docs and based on my experience.
I guess you need this:
Resolve function (django 1.4)
Then in your view class method you can do:
temp1, args, kwargs = resolve(self.request.path)
I want to overwrite the custom objects model manager to only return objects a specific user created. Admin users should still return all objects using the objects model manager.
Now I have found an approach that could work. They propose to create your own middleware looking like this:
#### myproject/middleware/threadlocals.py
try:
from threading import local
except ImportError:
# Python 2.3 compatibility
from django.utils._threading_local import local
_thread_locals = local()
def get_current_user():
return getattr(_thread_locals, 'user', None)
class ThreadLocals(object):
"""Middleware that gets various objects from the
request object and saves them in thread local storage."""
def process_request(self, request):
_thread_locals.user = getattr(request, 'user', None)
#### end
And in the Custom manager you could call the get_current_user() method to return only objects a specific user created.
class UserContactManager(models.Manager):
def get_query_set(self):
return super(UserContactManager, self).get_query_set().filter(creator=get_current_user())
Is this a good approach to this use-case? Will this work? Or is this like "using a sledgehammer to crack a nut" ? ;-)
Just using:
Contact.objects.filter(created_by= user)
in each view doesn`t look very neat to me.
EDIT Do not use this middleware approach !!!
use the approach stated by Jack M. below
After a while of testing this approach behaved pretty strange and with this approach you mix up a global-state with a current request.
Use the approach presented below. It is really easy and no need to hack around with the middleware.
create a custom manager in your model with a function that expects the current user or any other user as an input.
#in your models.py
class HourRecordManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(created_by=user)
class HourRecord(models.Model):
#Managers
objects = HourRecordManager()
#in vour view you can call the manager like this and get returned only the objects from the currently logged-in user.
hr_set = HourRecord.objects.for_user(request.user)
See also this discussion about the middelware approach.
One way to handle this would be to create a new method instead of redefining get_query_set. Something along the lines of:
class UserContactManager(models.Manager):
def for_user(self, user):
return super(UserContactManager, self).get_query_set().filter(creator=user)
class UserContact(models.Model):
[...]
objects = UserContactManager()
This allows your view to look like this:
contacts = Contact.objects.for_user(request.user)
This should help keep your view simple, and because you would be using Django's built in features, it isn't likely to break in the future.
It seems necessary to use the middleware to store the user information.
However, I'd rather not modify the default ModelManager objects, but hook it upto a different manager, that I will use in the code, say in your case user_objects instead of objects.
Since you will use this only within views that are #login_required you dont need all the complex error handling in the Middleware.
Just my 2ยข.
Or even simpler and use foreign key to retrieve queryset.
If you have model like that
class HourRecord(models.Model):
created_by = ForeignKey(get_user_model(), related_name='hour_records')
You can query HourRecords in a view by user with simply:
request.user.hour_records.all()