How to set initial data for Django admin model add instance form?
Asked Answered
G

4

32

How can I set an initial value of a field in the automatically generated form for adding a Django model instance, before the form is displayed? I am using Django 1.3.1.

My model is the following:

class Foo(models.Model):
  title = models.CharField(max_length=50)
  description = models.TextField()

and the current admin form is really nothing special

class FooAdmin(admin.ModelAdmin):
  ordering = ('title',)

When I use the admin page to add a new instance of Foo, I get a nice form with empty fields for title and description. What I would like is that the description field is set with a template that I obtain by calling a function.

My current best attempt at getting there is this:

def get_default_content():
  return 'this is a template for a Foo description'

class FooAdminForm(django.forms.ModelForm):

  class Meta:
      model = Foo

  def __init__(self, *args, **kwargs):
      kwargs['initial'].update({'description': get_default_content()})
      super(FooAdminForm, self).__init__(self, *args, **kwargs)

class FooAdmin(admin.ModelAdmin):
  ordering = ('title',)
  form = FooAdminForm

but if I try this I get this Django error:

AttributeError at /admin/bar/foo/add/ 
   'FooForm' object has no attribute 'get'
Request Method: GET
Request URL:    http://localhost:8000/admin/bar/foo/add/
Django Version: 1.3.1
Exception Type: AttributeError
Exception Value:    'FooForm' object has no attribute 'get'
Exception Location: /www/django-site/venv/lib/python2.6/site-packages/django/forms/widgets.py in value_from_datadict, line 178

I don't know what is wrong here, and what I should do to make it work. What I also find strange about this error (apart from the fact that I see it at all) is that there is no FooForm in my code at all?

Gilcrest answered 3/4, 2012 at 20:32 Comment(0)
C
31

You need to include self as the first argument in your __init__ method definition, but should not include it when you call the superclass' method.

def __init__(self, *args, **kwargs):
    # We can't assume that kwargs['initial'] exists! 
    if 'initial' not in kwargs:
        kwargs['initial'] = {}
    kwargs['initial'].update({'description': get_default_content()})
    super(FooAdminForm, self).__init__(*args, **kwargs)

Having said that, a model field can take a callable for its default, so you may not have to define a custom admin form at all.

class Foo(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField(default=get_default_content)
Caput answered 3/4, 2012 at 21:16 Comment(4)
Wow, that worked like a charm! Awesomely cryptic message by the way for such a silly mistake... Thanks a lot for your help!Gilcrest
By the way, I also tried your second suggestion successfully, which is even cleaner, so thanks again!Gilcrest
I found that setting kwargs['initial'] caused an error when saving the form. I had set an if clause: if 'initial' in kwargs.keys(): kwargs['initial'].update(... This worked for displaying the initial values and updating them.Beelzebub
@Beelzebub good point. I've updated my answer to set initial to an empty dict if it isn't in kwargs.Caput
E
43

Alasdair's approach is nice but outdated. Radev's approach looks quite nice and as mentioned in the comment, it strikes me that there is nothing about this in the documentation.

Apart from those, since Django 1.7 there is a function get_changeform_initial_data in ModelAdmin that sets initial form values:

def get_changeform_initial_data(self, request):
    return {'name': 'custom_initial_value'}
Echevarria answered 11/11, 2015 at 10:37 Comment(7)
for some reason in 1.10.2 this method is not called at all, though it is present in the docsMcclees
@Mcclees I just tested it in 1.10.2 and for me it worked as documented.Echevarria
It's easy to lose track of all the available hooks for ModelAdmin. Thanks for this!Junket
Django 1.11.10 and Django didn't call overrided get_changeform_initial_data method.Freedom
Facing the same issue of the method not being called I dove into the source code. The answer is quite simple, it is only called for newly created objects. Changing an existing object will not call it.Impose
Is there a similar function for inlines ?Flyer
Does this work with AutoCompleteFields?Darrel
C
31

You need to include self as the first argument in your __init__ method definition, but should not include it when you call the superclass' method.

def __init__(self, *args, **kwargs):
    # We can't assume that kwargs['initial'] exists! 
    if 'initial' not in kwargs:
        kwargs['initial'] = {}
    kwargs['initial'].update({'description': get_default_content()})
    super(FooAdminForm, self).__init__(*args, **kwargs)

Having said that, a model field can take a callable for its default, so you may not have to define a custom admin form at all.

class Foo(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField(default=get_default_content)
Caput answered 3/4, 2012 at 21:16 Comment(4)
Wow, that worked like a charm! Awesomely cryptic message by the way for such a silly mistake... Thanks a lot for your help!Gilcrest
By the way, I also tried your second suggestion successfully, which is even cleaner, so thanks again!Gilcrest
I found that setting kwargs['initial'] caused an error when saving the form. I had set an if clause: if 'initial' in kwargs.keys(): kwargs['initial'].update(... This worked for displaying the initial values and updating them.Beelzebub
@Beelzebub good point. I've updated my answer to set initial to an empty dict if it isn't in kwargs.Caput
P
15

More then 3 years later, But actually what you should do is override admin.ModelAdmin formfield_for_dbfield .. like this:

class FooAdmin(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, **kwargs):
        field =  super(FooAdmin, self).formfield_for_dbfield(db_field, **kwargs)
        if db_field.name == 'description':
            field.initial = 'My initial description'
        elif db_field.name == 'counter':
            field.initial = get_counter() + 1
        return field

Cheers;

Paranymph answered 28/6, 2015 at 1:16 Comment(3)
Very nice. It is strange but there is no such thing in the documentation: docs.djangoproject.com/en/1.8/ref/contrib/adminEchevarria
The Django admin is "way" under-documented , unfortunately.Paranymph
Alternatively you could remove the field = super... line, set kwargs['initial'] = 'My initial description' (and similar for the 'counter' field), followed by return super... That saves a line.Sere
S
4

When adding new objects, it is convenient to use get_changeform_initial_data() as suggested by Wtower.

However, when changing existing objects, that does not work (see source).

In that case, you could extend ModelAdmin.get_form() as follows (using the OP's example):

def get_form(self, request, obj=None, change=False, **kwargs):
    if obj and not obj.description:
        obj.description = get_default_content()
    return super().get_form(request, obj, change, **kwargs)
Sere answered 22/6, 2021 at 8:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.