4

I'm totally new in Python and Django :) and i need some help.

What i want to do:

I have a model Page and i need to add a custom field "message" when someone try to update one object.

Why? Because i'm building a revision system. This field, it's just an explanation about the change. So this field is not linked to the Page (but to another model PageRevision)

After some research i managed to add this field to my form in the admin.py file, like this:

class PageAdminForm(forms.ModelForm):
    # custom field not backed by database
    message = forms.CharField(required=False)

    class Meta:
        model = Page

it's working, my field is now displayed...But i don't want this field everywhere. Just when someone try to update a Page object. i have found this answer different-fields-for-add-and-change-pages-in-admin but it's not working for me because it's a custom field (i think).

The rest of my code in admin.py:

class PageAdmin(admin.ModelAdmin):
    form = PageAdminForm
    fields = ["title", "weight", "description", "message"]
    list_display = ["title", "weight", "description"]
    list_filter = ["updated_at"]

    def get_form(self, request, obj=None, **kwargs):
        if obj is None:
            # not working ?
            kwargs['exclude'] = ['message']
        # else:
        #     kwargs['exclude'] = ['message']
        return super(PageAdmin, self).get_form(request, obj, **kwargs)

    def save_model(self, request, obj, form, change):
        if not obj.id:
            obj.author = request.user
        obj.modified_by = request.user
        wiki_page = obj.save()

        # save page in revision table
        revision = PageRevision(change=change, obj=wiki_page,
                            request=request)
        # retrieve value in the custom field
        revision.message = form.cleaned_data['message']
        revision.save()

def get_form doesn't exclude my custom message field because i think it doesn't know is existence. If i put another field like title, it's works. So how to exclude the custom field from add view ?

Thanks :)

Community
  • 1
  • 1
Epok
  • 661
  • 1
  • 8
  • 16

2 Answers2

6

You're right, it won't work this way, because 'message' is not a field found on the Page model and the ModelAdmin class will ignore the exclusion. You can achieve this in many ways, but I think the best way to do it is this:

class PageAdmin(admin.ModelAmin):
    change_form = PageAdminForm

    ...

    def get_form(self, request, obj=None, **kwargs):
       if obj is not None:
          kwargs['form'] = self.change_form

       return super(UserAdmin, self).get_form(request, obj, **defaults)

Basicaly here django will use an auto-generated ModelForm when adding a Page and your custom form when editing the Page. Django itself uses a similar technique to display different forms when adding and changing a User:

https://github.com/django/django/blob/stable/1.6.x/django/contrib/auth/admin.py (the interesting part is in line 68)

ppetrid
  • 3,745
  • 27
  • 29
  • Thanks! +1 for the link very interesting. Now i have a new error 'NoneType' object is not iterable only when i'm on the change view. i'll investigate this tonight. – Epok Dec 06 '13 at 10:26
  • Ok it was another method which was causing the error "get_fieldsets"...I had to remove line fields=[...] from PageAdmin and add editable=false on my model to display only the field i wanted. (because message field was causing an error in add form because it wasn't find anymore) – Epok Dec 06 '13 at 10:41
2

I just stumbled upon this same question and, since it comes in the first search results I would like to add this other solution for people that do not want to use a custom form.

You can override the get_fields method of your admin class to remove a custom field from the Add page.

def get_fields(self, request, obj=None):
    fields = list(super().get_fields(request, obj=obj))
    if obj is None:
        fields.remove("message")
    return fields