views:

388

answers:

3

I have a couple special use cases for Django admin, and I'm curious about other peoples' opinions:

  1. I'd like to use a customized version the admin to allow users to edit certain objects on the site (customized to look more like the rest of the site). At this point users can only edit objects they own, but I'll eventually open this up to something more wiki-style where any user can edit any of the objects. In other words, I'd be designating all users as 'staff' and granting them permission to edit those objects.

  2. I was considering also doing this for other objects where not all users would be able to edit all objects. I'd use a custom view to make sure users only edit their own objects. The benefits are that I would have a starting point for the editing interface (as the admin creates it automatically) that I could just customize with ModelAdmin since the admin functionality is already pretty close to what I'd like.

I feel like the first suggestion would be considered acceptable, while the second might not be. After checking out a few other resources (http://stackoverflow.com/questions/498199/valid-use-case-for-django-admin and the quote from the Django Book in that question) it seems like some Django developers feel like this is the wrong idea.

My question is: why? Are there any good reasons not to use customized admin views to grant per-object permissions from a performance, stability, security, usability, etc. standpoint? It seems to me like it could save a lot of time for certain applications (and I may end up doing it anyway) but I wanted to understand the reasons for making such a distinction between the admin and everything else.

+3  A: 

You are free to do whatever you want. If you want to customize the Django admin, go for it, but you will likely not be as well supported by the mailing list and IRC once you deviate from the typical admin modifications path.

While customizing the admin might seem like the easy solution right now, more than likely it is going to be more work than just recreating the necessary forms yourself once you really try to tweak how things work. Look into the generic create/edit/delete and generic details/list views--they will expose the basic functionality you need very quickly, and are going to be easier to extend than the admin.

I believe the view that the "admin is not your app" comes from the fact that it is easier to use other mechanisms than hacking up the admin (plus, leaving the admin untouched makes forward compatibility much easier for the Django developers).

John Paulett
+1 modelforms and generic views make this stuff so ridiculously easy, it's really not worth the extra effort it takes to hack up the admin.
Carl Meyer
+2  A: 

I've previously made a django app do precisely this without modifying the actual admin code. Rather by creating a subclass of admin.ModelAdmin with several of it's methods extended with queryset filters. This will display only records that are owned by the user (in this case business is the AUTH_PROFILE_MODEL). There are various blogs on the web on how to achieve this.

You can use this technique to filter lists, form select boxes, Form Fields validating saves etc.

So Far it's survived from NFA to 1.0 to 1.1 but this method is susceptible to api changes.

In practice I've found this far quicker to generate new row level access level admin forms for new models in the app as I have added them. You just create a new model with a user fk, subclass the AdminFilterByBusiness or just

admin.site.register(NewModel,AdminFilterByBusiness)

if it doesnt need anything custom. It works and is very DRY.

You do however run the risk of not being able to leverage other published django apps. So consider this technique carefully for the project you are building.

Example Filter admin Class below inspired by http://code.djangoproject.co/wiki/NewformsHOWTO

#AdminFilterByBusiness {{{2
class AdminFilterByBusiness(admin.ModelAdmin):
    """
    Used By News Items to show only objects a business user is related to
    """
    def has_change_permission(self,request,obj=None):
        self.request = request

        if request.user.is_superuser:
            return True

        if obj == None:
            return  super(AdminFilterByBusiness,self).has_change_permission(request,obj)

        if obj.business.user == request.user:
            return True
        return False

    def has_delete_permission(self,request,obj=None):

        self.request = request

        if request.user.is_superuser:
            return True

        if obj == None:
            return  super(AdminFilterByBusiness,self).has_delete_permission(request,obj)

        if obj.business.user == request.user:
            return True
        return False

    def has_add_permission(self, request):

        self.request = request
        return super(AdminFilterByBusiness,self).has_add_permission(request)

    def queryset(self, request):
        # get the default queryset, pre-filter
        qs = super(AdminFilterByBusiness, self).queryset(request)
        #
        if not (request.user.is_superuser):
            # filter only shows blogs mapped to currently logged-in user
            try:
                qs = qs.filter(business=request.user.business_set.all()[0])
            except:
                raise ValueError('Operator has not been created. Please Contact Admins')
        return qs

    def formfield_for_dbfield(self, db_field, **kwargs):

        """ Fix drop down lists to populate as per user request """
        #regular return for superuser
        if self.request.user.is_superuser:
            return  super(AdminFilterByBusiness, self).formfield_for_dbfield(
                    db_field, **kwargs)

        if db_field.name == "business":
            return forms.ModelChoiceField(
                queryset = self.request.user.business_set.all()
               )

        #default
        return  super(AdminFilterByBusiness, self).formfield_for_dbfield(db_field, **kwargs)
michael
What do you mean when you say "You do however run the risk of not being able to leverage other published django apps."?The impression I got was that ModelAdmin effectively replaces the form (and in some cases views), and if I needed to customize any views I could always wrap or override them.
John Debs
Eg. A lot of available django 'profile' apps like pinax or its parts etc. dont use this idiom. They might cover a lot of your required functionality. It would be kludgey to mix these apps in with a row level admin.
michael
A: 

We limit the Django Admin -- unmodified -- for "back-office" access by our admins and support people. Not by users or customers. Some stylesheet changes to make the colors consistent with the rest of the site, but that's it.

For the users (our customers), we provide proper view functions to do the various transactions. Even with heavily tailored forms, there are still a few things that we need to check and control.

Django update transactions are very simple to write, and trying to customize admin seems more work that writing the transaction itself.

Our transactions are not much more complex than shown in http://docs.djangoproject.com/en/dev/topics/forms/#using-a-form-in-a-view.

Generally, our pages that have transactions almost always include workflow elements (or related content) that make them slightly more complex than the built-in admin interface. We'll have a half-dozen or so additional lines of code beyond the boilerplate.

Our use cases aren't simple add/change/delete, so we need more functionality than the default admin app provides.

S.Lott