0

I'm adding some user profile behavior to a Django app, and as usual I would like to restrict users from only being able to interact with their own data. This SO Q&A is related to the process itself:

Django--- Allowing Users to only edit their profile

Is it enough to add the authenticated user id to the create, update, and delete queries? To reduce code duplication, I was thinking that I could write a mixin that would override get_object or get_queryset and intercept the query by adding the self.request.user.pk to filter the results?

Are there other efficient methods for doing this, or mixins from Django itself?

edit This is what i was thinking:

    class OwnersDataOnlyViewMixin(object):
    def get_object(self, queryset=None):
        if self.model == get_user_model():
            # user model, so pk of model should match self.request.user.pk
            return super().get_object(self.model.objects.filter(pk=self.request.user.pk))
        else:
            # different model
            try:
                # check for the 'user' field and filter on it if found
                user_field = self.model._meta.get_field('user')
                return super().get_object(self.model.objects.filter(user=self.request.user))
            except:
                # Mixin was used with model data not associated with a user
                raise self.model.DoesNotExist      

There are probably more flexible ways of doing this, and I'm open to suggestions to improve that.

Brian
  • 642
  • 7
  • 18

1 Answers1

1

For class based views you will most likely need to override the get_querset() method as the queryset returned by it will be used for getting the data for editing and deleting, therefore return something like MyModel.objects.filter(owner=self.request.user). Note that this will result in a 404 - "Not Found" response if the user is trying to access an object that doesnt belong to him.

Upon creation of new objects you will mainly need to set request.user as the owner of the newly created object. This can be done with customizing the CreateView's form_valid() method:

class MyCreateView(LoginRequiredMixin, generic.CreateView):

    def form_valid(self, form):
        form.instance.owner = self.request.user
        return super().form_valid(form)

Depending on your model structure you can add this methods to a mixin and use them with the generic views.

Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148
  • I think what you described is just where I ended up just now, except I did `get_object` instead since `get_queryset` is not guaranteed to be called if `get_object` is overridden elsewhere. Then again, I suppose the same problem exists with my own override. For the create view, I did something similar to what you suggested as well using `form.instance`. – Brian Mar 14 '19 at 16:03
  • I've accepted your answer because that's pretty much where I ended up and seems like the right thing to do. – Brian Mar 14 '19 at 18:22