Tuesday, August 31, 2010

Django Admin: Making fields read-only for editing

Sometimes, when you are developing a Django website, you want to use the Django admin interface to create and edit your custom, homemade models. How to do this, is well documented on this page on the Django website. However, sometimes you want the edit page to differ from the create page. For example, you might want users to be able to fill in a slug field for a model, but because you are afraid of broken links, you don't want them to be able to edit that slug.

Of course you can override the model's save method to raise an exception when somebody tries to edit the slug, but that is both ugly and confusing for the user. What you actually want is a standard text field when you create an object, but a read-only field if you edit an object. From the previously mentioned page, we know that Django supports something called 'readonly_fields' and that the ModelAdmin model has a method called 'get_readonly_fields'. How can we use this information to make a field read-only in edit mode? Simply override the get_readonly_fields method.

A silly example:
Let's say we have a model called Message, for which we create an admin model called MessageAdmin, following the guide from the Django website. In the admin interface, we want to show the fields 'author', 'title', 'slug' and 'text'. As you might have guessed, we want the slug to be read-only in edit mode. The MessageAdmin class will then look like this:
class MessageAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {
            'fields': ('author', 'title', 'slug', 'text',)
        }),
    )
    
    def get_readonly_fields(self, request, obj = None):
        if obj: #In edit mode
            return ('slug',) + self.readonly_fields
        return self.readonly_fields

The interesting part is the overridden get_readonly_fields method. From the Django website we learn that, if there is no obj, we are in create mode. Therefor, if obj exists, we are in edit mode. In this case, if we are in edit mode, we add 'slug' to the other read-only fields we may or may not have already defined and return all the read-only fields. If we are not in edit mode, we return only the fields we may or may not have defined as read-only.

That's all there is to it. Nothing really exciting, but it can be really helpful to know.

7 comments:

  1. Worked perfectly, Thanks!!

    ReplyDelete
  2. Thanks!
    That's exactly what I want.

    ReplyDelete
  3. Saved me from having to hack the code to find this exact method myself. Thanks!

    ReplyDelete
  4. Thanks, very useful!

    ReplyDelete
  5. But you can't add new items to the list, the 'obj' refers to the parent

    ReplyDelete