0

I have a class-based view that is inherited by other views:

class EditProfileAttribute(View):
    template_name = 'mytemplate.html'
    ThisModel = models.Model
    def get(request, model_id):
        instance = self.ThisModel.objects.get(id=model_id)
        if instance.user != request.user:
            return HttpResponseForbidden("Forbidden.")
        # add form here
        return render(request, self.template_name, {'model_id':model_id})

    def post(request, model_id):
        instance = self.ThisModel.objects.get(id=model_id)
         if instance.user != request.user:
             return HttpResponseForbidden("Forbidden.")
         # do some editing here, save forms, etc.
         return HttpResponse("Edited")

class EditAddressView(EditProfileAttributeView):
        ThisModel = Address

class EditLinkView(EditProfileAttributeView):
        ThisModel = Link

Now in my models.py file:

class Address(models.Model):
    user = models.ForeignKey(User)
    address = models.TextField(max_length=100)

class Link(models.Model):
    from_user = models.ForeignKey(User, related_name='from_link')
    to_user = models.ForeignKey(User, related_name='to_link')

The class EditAddressView works because the User field is explicitly called user, but the class EditLinkView does not because it requires instance.from_user instead of instance.user. (Let's just say I can't rename from_user).

What I'd like to do is to transform the instance.user != request.user part into a decorator where the syntax user is not required, but I can't figure out how to reference self.ThisModel.

At the same time, this allows me not to repeat the same method for both get and post.

Is what I want even possible?

1 Answer 1

3

You can use another class attribute to store the name of the user field, and then use get_attr to fetch the value in the method.

If you want to avoid duplicating code in get and post, then factor the common code into a helper method, or override dispatch instead.

class EditProfileAttribute(View):
    ThisModel = models.Model
    user_field_name = 'user'

    def dispatch(self, request, *args, **kwargs):
         self.instance = self.ThisModel.objects.get(id=kwargs['model_id'])
         if getattr(self.instance, self.user_field_name) != request.user:
             return HttpResponseForbidden("Forbidden.")
         # calling super() will call get() or post() as appropriate
         return super(EditProfileAttribute, self).dispatch(request, *args, **kwargs)
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks @Alasdair! That solves my first problem -- but I have another one. What if I have a POST method that requires the same instance check? Is there really no way around having to copy the same line in both the get() and post() methods?
Also, what if I have a DeleteView that also requires a similar check? It would help much if I don't have to copy the same lines over and over again...
You can override the dispatch method to avoid duplicating code in get() and post(). The code above should work as a mixin, I can't see why your DeleteView can't inherit it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.