简体   繁体   中英

Django Admin Non Staff Access Data Filtering

I'm writing an application in Django (which I'm very new to) where the admin area will be exposed to 'customers' of the application, not just staff/superusers, because of the nature of the application and the way Django automatically generates forms in the admin area with such little code..

As such I need to robust and manageable way to maintain authentication and separating data, so only data created by a user is seen by that user.

At the moment I'm just using the default admin package and changing permissions for 'client users' to filter what data they can see (I only want them to see data they've created) using code like the below:

class MyModelAdmin(admin.ModelAdmin):

    def get_queryset(self, request): 
        qs = super(MyModelAdmin, self).get_queryset(request) 
        return qs.filter(user=request.user)

    def save_model(self, request, obj, form, change):
        # field not editable in admin area so handle it here...
        obj.user = request.user
        obj.save()

However as the application scales, I can see ensuring this type of data filtering becoming difficult to manage, for example if there are chains of foreign keys on certain tables( A->B B->C C->D ), and to filter the table at the end of the chain I need to do various JOIN s to get the rows which relate to the current user.

A couple of solutions I'm pondering are creating a separate admin app per user, but this feels like overkill and even more unmanageable.

Or just adding the user column to every Model where data filtering by user is required to make it easier to filter.

Any thoughts on the best approach to take?

First off, from experience, you're better off offering editing and creating functionality to your users in an actual django app, using View s. Generic views make this very easy. Once you let your users into the admin, they will get used to it and it's hard to get them to leave.

Additionally you should use contrib.auth.Group together with django-guardian to keep track of object-level permissions instead of implementing it yourself. It pays off in the long run.

If you want to make this experience on your own however, you have more than one sensible choice:

  • owner on root objects in the ForeignKey pyramid
  • owner on every model

To realize the first option, you should implement two methods on every model down the ForeignKey chain:

def get_parent(self):
    """Should return the object that should be queried for ownership information"""
    pass
def owned_by(self, user):
    """Should return a boolean indicating whether `user` owns the object, by querying `self.get_parent().owned_by(user)`"""  
    pass

However, as you stated, this incurrs many JOINS if your schema is sufficiently complex.

I would advise you to store the information about the owner in every model, everything else is a maintanence nightmare in my experience.

Instead of adding the field manually to every model manually, you should use inheritance. However django provides bad built-in support for inheritance with relations: An abstract base model cannot define a models.ForeignKey , so you're stuck with table based inheritance.

Table based inheritance brings another problem with itself: Consider these models:

from django.db import models
from app.settings import AUTH_USER_MODEL
class Base(models.Model):
    owner = models.ForeignKey(AUTH_USER_MODEL)
class ChildA(Base):
    name = models.CharField(max_length=5)
class ChildB(Base):
    location = models.CharField(max_length=5)

It is easy to find the owner of a given instance of ChildA or ChildB :

>>> obj = ChildA.objects.create(owner=Peter, name="alex")    
>>> obj.owner

Peter

However it is non trivial to find all objects owned by a particular user:

>>> Base.objects.filter(owner=Peter)
<Base-object at 0xffffff>

The default manager returns a Base object, and doesn't contain information about whether it is a ChildA or ChildB instance, which can be troublesome.

To circumvent this, I recommend a polymorphic approach with django-polymorphic or django-model-utils , which is more lightweight. They both provide means to retrieve the child classes for a given Base model in the queries. See my answer here for more information on polymorphism in django.

These also incur JOIN s, but at least the complexity is manageable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM