Object level permissions in Django admin

I wouldn't use object-level permission for something as simple as your requirement. You just need to have an owner ForeignKey to your Model and only allow the owner of each object to modify it (you can use the Plan_Owner -- PLEASE change it to plan_owner and Ticket_Number to ticket_number to be compatible with pep 8 and django style guide).

I have written a post that describes how to do this in django:


Actually I am describing how to use authorities that users belong to and each user can edit the objects of his authority but your requirement is covered.


For completeness, I am adding the implementation here:

Your Create and Update Class Based Views have to pass the request to your forms and also your Detail and Update CBV should allow only getting objects that belong to the user (let's suppose that your model is named UserData:

class UserDataCreateView(CreateView):

  def get_form_kwargs(self):
      kwargs = super(UserDataCreateView, self).get_form_kwargs()
      kwargs.update({'request': self.request})
      return kwargs

class UserDataDetailView(DetailView):
  def get_object(self, queryset=None):
      obj = super(UserDataDetailView, self).get_object(queryset)
      if not user_has_access(obj, self.request):
          raise Http404(u"Access Denied")
      return obj

class UserDataUpdateView(UpdateView):

  def get_form_kwargs(self):
      kwargs = super(UserDataUpdateView, self).get_form_kwargs()
      kwargs.update({'request': self.request})
      return kwargs

  def get_object(self, queryset=None):
      obj = super(UserDataUpdateView, self).get_object(queryset)
      if not user_has_access(obj, self.request):
          raise Http404(u"Access Denied")
      return obj

It checks if the request.user has permission (is the owner of the object) and also passes the request to the ModelForm. The has_access function defined above just checks if the current user is the owner of the object:

def has_access(obj, req):
    if req.user == obj.owner:
        return True
    return False

Yot ModelForm should be like this (same for create/update):

class UserDataModelForm(forms.ModelForm):
    class Meta:
      model = models.UserData
      exclude = ('owner',)

    def __init__(self, *args, **kwargs):
      self.request = kwargs.pop('request', None)
      super(ActionModelForm, self).__init__(*args, **kwargs)

    def save(self, force_insert=False, force_update=False, commit=True):
      obj = super(UserDataModelForm, self).save(commit=False)
      if obj:
          obj.owner = self.request.user
      return obj

It removes request from kwargs and sets it as an attribute and at save it sets the owner of the object to the reqest.user.

You can easily do this by overriding the get_queryset() of ModelAdmin class. So get_queryset() is the place where querying all the objects to display in the Admin Site. For example if you return Change.objects.all() in get_queryset(), will display all the objects in you Change model in Admin Site. If you return Change.objects.none() in get_queryset(), will not display any values in Change model in Admin Site.

This is what documentation mention about get_queryset().

Returns the queryset that will be used to retrieve the object that this view will display. By default, get_queryset() returns the value of the queryset attribute if it is set, otherwise it constructs a QuerySet by calling the all() method on the model attribute’s default manager.

I just override get_queryset() in your ChangeAdmin class.

class ChangeAdmin(admin.ModelAdmin):
    model = Change
    search_fields = ('RFC', 'Ticket_Number','Plan_Owner')
    list_display = ('RFC', 'Ticket_Number','Plan_Owner')

        fieldsets = [
            'Ticket Details', {
                'fields': ['RFC', 'Ticket_Number', 'Plan_Owner']

    def get_queryset(self, request):
        if request.user.is_superuser:
            queryset = Change.objects.all()
                queryset = Change.objects.filter(plan_owner_id=request.user.id)
                queryset = Change.objects.none()
        return queryset

admin.site.register(Change, ChangeAdmin)

According to this example, if the superusers logged into the admin site they will see all the objects in the Change model. If other users who are in the Change table will display their own objects (because in this example, it's filter from according to the Plan_Owner of Change model), otherwise nothing display in Admin Site.