curtisp curtisp - 2 months ago 28
Python Question

Django custom decorator user_passes_test() can it obtain url parameters?

Can Django's

user_passes_test()
access view parameters?

For example I have view that receives an
id
to retrieve specific record:

def property(request, id):
property = Property.objects.get(id=int(id))


The record has a field named user_id that contains the id for user that originally created record. I want users to be able to view only their own records otherwise be redirected.

I'd like to use a custom decorator which seems simple and clean.

For custom decorator is there some variation of something like this that will work?

@user_passes_test(request.user.id = Property.objects.get(id=int(id)).id, login_url='/index/')
def property(request, id):
property = Property.objects.get(id=int(id))


I have tried creating separate
test_func
named
user_is_property_owner
to contain logic to compare current user to property record user_id

@user_passes_test(user_is_property_owner(id), login_url='/index/')
def property(request, id):
property = Property.objects.get(id=int(id))


def user_is_property_owner(property_id):
is_owner = False
try:
Property.objects.filter(id=property_id, user_id=user_id).exists()
is_owner = True
except Property.DoesNotExist:
pass


But having trouble getting current user id and the property id from the request into the
user_is_property_owner
decorator function.




EDIT to add solution I was using. I did test inside each view test was required. It is simple. i thought using a decorator might be prettier and slightly more simple.

def property(request, id):

# get object
property = Property.objects.get(id=int(id))

# test if request user is not user id on property record
if request.user.id != property.user_id:
# user is not same as property user id so redirect to index
return redirect('index')

# rest of the code if request user is user_id on property record
# eg it is ok to let user into view

Answer

Typically, (using class based views), I'll handle this in the get_queryset method so it would be something like

class PropertyDetail(DetailView):
    def get_queryset(self):
        return self.request.user.property_set.all()

and that will give a 404 if the property isn't for the current user. You might prefer to use a project like django-guardian if you end up with more permission relationships than just Property.

If you take a look at UserPassesTestMixin you'll see that it processes the test_func before calling dispatch so you'll have to call self.get_object(request) yourself if you decide to go that route.