I am using django-voting as a voting application for two of my models. Those both models have fields "author".
How can I restrict a user from voting on a model that has this particular user set as it's author without modifying django-voting app?
Django middleware is the first thing that comes to my mind, but I don't understand it's "proces_view" function. If you think middleware is the right way could you please give an example of how to do it.
Add this code anywhere in your settings.py:
from voting.managers import VoteManager
def check_user(func):
def wrapper(self, obj, user, vote):
if obj.user != user:
return func(self, obj, user, vote)
else:
return None
# or raise some exception
return wrapper
VoteManager.record_vote = check_user(VoteManager.record_vote)
I didn't run this code, maybe it's incorrect, but I hope idea is clear
Rather than a middleware hack, why not reroute requests to that particular URI through another view? Then you can performs whatever logic you like, and subsequently call the original view if appropriate.
Another idea is to use the post_save signal
like so:
from django.db.models.signals import post_save
from voting.models import Vote
def check_user(sender, instance, **kwargs):
if instance.user == instance.object.user:
instance.delete()
# do some other stuff to tell the user it didn't work
post_save.connect(check_user, sender=Vote)
The benefit of doing this vs overriding VoteManager.record_vote is that it's less tightly coupled to the voting module, and if they make changes it's less likely to break your code
edit: as in Glader's answer, you need to make sure that all the objects you're voting on have a 'user' attribute.
Related
When I login to django as a superuser, if an object of some model or the a field of the last object in that model doesn't exist or isn't set, how would I redirect to the creation of that model? And don't let me access the WebApp unless it's made?
I'm guessing there's a function to be run whenever a superuser logs in and check for an object or a field in the last object. I just don't know how to make this function and where to put it.
EDIT: Really sorry, I didn't elaborate my question further.
- I want to check for the existence of the object WHEN a superuser logs in to the webapp, not when they click on a model from the index. An object NEEDS to exist, that's why when it doesn't exist and a superuser logs in, they are to be taken to add form and not allowed to use the rest of the site until an object exists. So when someone logs in to the the Django Administration Site, I want to run a function that checks for the existence of that model and redirect or not, accordingly.
Thank you for your time reading this.
You can use a signal to run a function after the point a user logs in
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
#receiver(user_logged_in)
def superuser_login(sender, user, request, **kwargs):
if user.is_superuser:
# Do stuff here
In your admin, you can override change_view with something like this:
class MyModelAdmin(admin.ModelAdmin):
... your admin declaration
def change_view(self, request, object_id, form_url='', extra_context=None):
try:
MyModel.object.pk(pk=object_id)
return super().change_view(request, object_id, form_url=form_url, extra_context=extra_context):
except MyModel.DoesNotExist:
return redirect('admin:yourapp_yourmodel_add')
It's not sql optimized as _changeform_view will fectch your instance agin, but it's the simplest solution.
I'm building a job application form. A logged-in user is permitted to apply to the job only once (there's only one job). At the moment, a user is able to directly access the job application (FormView), by typing in its specific URL, and an error message is thrown AFTER the user submits the form. To achieve this, I'm doing a couple of things:
(1) Wrapping the job application view in login_required()
(2) Using the form_valid() method to check whether or not a user has already submitted an application to this specific job via:
def form_valid(self, form):
form = form.save(commit=False)
form.user = self.request.user
if Application.objects.filter(user=user_id):
messages.error(self.request, 'Sorry, our records show that you have already applied to this job.')
return redirect('/')
But I'd rather not permit them to reach the page at all. Instead, I want to check whether or not a user has already applied (during the request), and redirect them away from the form, if they have already applied. I have limited access to logged-in users that pass a test in the past, using something like:
def job_application_view(request):
active_user = request.user
if Application.objects.filter(user=active_user):
return HttpResponse("Some response.")
However, I can't seem to figure out how to access request via the FormView Class-Based View. I'm sure I'm missing something simple. Perhaps another method of FormView I'm missing?
You can still use decorators on class-based views, but they are slightly more difficult to apply than function-based views.
class ApplicationView(FormView):
# ...
#method_decorator(user_passes_test(job_application_view))
def dispatch(self, *args, **kwargs):
return super(ApplicationView, self).dispatch(*args, **kwargs)
Answering specific parts of your post...
I have limited access to logged-in users that pass a test in the past
With class-based views, you need to decorate the url or decorate the dispatch method with any decorators you are interested in applying.
However, I can't seem to figure out how to access request via the FormView Class-Based View. I'm sure I'm missing something simple. Perhaps another method of FormView I'm missing?
You can access the request with self.request
I want to write a Mixin (or use a third-party one) for checking if the logged user is the owner of certain object.
url(r'^api/mymodel/(?P<pk>\d)/?', CreateUpdateMyModel.as_view(), name='create_or_update')
class MyModel(models.Model):
owner = models.OneToOneField('auth.User')
class OwnerRequired(SingleObjectMixin):
# do this self.object = self.get_object() for getting the object
#
# some logic for checking if request.user == self.object.owner
# otherwise return something like Response(status=status.HTTP_403_FORBIDDEN)
Inheriting from SingleObjectMixin is important to me, since I want to be able to do something like:
class CreateUpdateMyModel(APIView, OwnerRequired):
model = MyModel
def post(self, request, *args, **kwargs):
# self.object should be available here
# so that write some code taking it into account
How OwnerRequired should look for fulfilling this?
I'm open to another alternatives, in fact, I've checked PermissionRequiredMixin from django-braces and I'd like to use it, but I'm not sure how to do it
permission_required = ?? # I can code a method for, but how can I pass the model instance and the request.user?
Is there another simple alternative?
Take a look at object level permissions. There's also a relevant example on that page in the examples sections - see the IsOwnerOrReadOnly example.
Also note that object level permissions are only run either:
You are using the GenericAPIView or a subclass of it, and calling get_object() to retrieve the instance.
You explicitly call self.check_object_permissions(request, instance within your view code.
Below is how I currently check that a model belongs to the user that's editing it. I do this in views.py for almost everything, it's a bit repetitive, I use it a lot!
# If we have an instance check that it belongs to the login.
if some_object.user != request.user:
return HttpResponseForbidden()
So, I'm wondering how others address such functionality? I have been thinking about moving this to each on save method for all objects?
Could I have examples (with code) of how Django developers do this, there must be a better wayfor checking what user can edit what, right?
I won't rush into accepting an answer, I'm really interested in authoritative responses from more experienced devs as I'm very new to Django.
Thank you.
Mixins could work if you are using class based views... You can create your own by doing something like this
class RequireOwnershipMixin(object):
def get_object(self, queryset=None):
obj = super(RequireOwnershipMixin, self).get_object(queryset)
if obj.user != self.request.user:
return None
return obj
and your view would now look something like this
class UpdateSomeObjectView(RequireOwnershipMixin, UpdateView):
...
This will override the get_object method to return None if you are not the owner of the object. You may need to do some extra checks in the get or post methods to handle when None is returned so that you can return a HttpResponseForbidden
You can also use this for any class based views that utilize the get_object method.
I'm wondering how I might accomplish a simple 'object ownership' system with django models, such that, by default, only the owner of an object may edit it.
I am attempting to allow a 'Management' group to edit all objects on behalf of the object owners, and have at this point added a custom permission:
class Meta:
permissions = (
("manage_object", "Can manage objects"),
)
To establish 'ownership' I've toyed with the idea of adding a def to the model:
def owner(self):
return self.user
But then, how might I go further? I could implement the permissions in a view and display relevant UI with a template, i.e.:
if request.user is object.owner:
# ... do stuff
elseif request.user.has_perm.can_manage: # this line is probably not right
# ... do something else
... and then present different UI elements on a template level.
So, the question is:
what faults/benefits are there with this approach?
are there recommendations?
or, any other previously implement methods?
Best thanks!
My approach would be adding a method to the model:
class YourModelWithOwnership(models.model):
...
def user_can_manage_me(self, user):
return user == self.user or user.has_perm('your_app.manage_object')
I'd then call that method whenever a permission check is required, and take some action based on the outcome. So for a view that would be
from django.shortcuts import get_object_or_404
...
def view_func(request, item_id):
item = get_object_or_404(YourModelWithOwnership, id=item_id) # or whatever is needed to get the object
if not item.user_can_manage_me(request.user):
# user not allowed to manage
...
else:
...
Later I'd probably realize that that's still quite some boilerplate code to write in every view that needs that test, so I'd implement an exception that's thrown when a user can't manage an object...
class CannotManage(Exception):
pass
...and add another method to the model:
from django.db import models
from django.shortcuts import get_object_or_404
class YourModelWithOwnership(models.model):
...
#classmethod
def get_manageable_object_or_404(cls, user, *args, **kwds):
item = get_object_or_404(cls, *args, **kwds)
if not item.user_can_manage_me(user):
raise CannotManage
return item
Then, in the view functions, this can be used:
def view_func(request, item_id):
item = YourModelWithOwnership.get_manageable_object_or_404(request.user, id=item_id)
...
This will of course raise an exception when the user isn't the owner and does not have the proper permission. That exception can be handled in the process_exception() method of a custom middleware class so that there's a single handler for all instances where a user is not allowed to mess with the object.
A while back I wrote up the usual technique for doing this in the admin. You may want to read through that to see how the implementation works.
You can look into RowLevelPermissions branch. It hasn't been included even in 1.1 beta though, I guess it still needs some development.