Nick
Nick

Reputation: 8309

Django Admin: Overriding Initial Value for Inlines

One of my models has a field with a default value that I'm happy with, but I want that default value to be different within the admin. Here's my attempt at achieving this:

models.py

from django.db import models


class Parent(models.Model):
    parent_field_1 = models.CharField(max_length=255)
    parent_field_2 = models.CharField(max_length=255, blank=True)

    class Meta:
        ordering = ('pk',)

    def __unicode__(self):
        return self.parent_field_1


class Child(models.Model):
    parent = models.ForeignKey(Parent)
    child_field_1 = models.CharField(max_length=255)
    child_field_2 = models.CharField(max_length=255, blank=True)
    child_field_3 = models.IntegerField(default=0)

    class Meta:
        ordering = ('pk',)
        verbose_name_plural = 'children'

    def __unicode__(self):
        return self.child_field_1

forms.py

from django import forms

from .models import Child


class ChildForm(forms.ModelForm):
    class Meta:
        model = Child
        fields = [
            'parent',
            'child_field_1',
            'child_field_2',
            'child_field_3',
        ]

    def __init__(self, *args, **kwargs):
        if 'instance' not in kwargs:
            initial = kwargs.get('initial', {})
            initial['child_field_3'] = '1'
            kwargs['initial'] = initial

        super(ChildForm, self).__init__(*args, **kwargs)

admin.py

from django.contrib import admin

from .forms import ChildForm
from .models import Parent, Child


class ChildInline(admin.StackedInline):
    model = Child
    form = ChildForm


class ParentAdmin(admin.ModelAdmin):
    inlines = [ChildInline]


class ChildAdmin(admin.ModelAdmin):
    form = ChildForm

admin.site.register(Parent, ParentAdmin)
admin.site.register(Child, ChildAdmin)

When I go to add a child, the "Child field 3" field is populated with "1" (instead of the model's default of "0"), which is what I want. When I go to edit a child, the "Child field 3" field is populated with whatever value is in the database, which is also what I want.

The problem I'm running into comes in when I go to add or edit a parent. If I try to add a parent, I am required to also fill out all of the child inline forms. Likewise, if I try to edit a parent, I am required to fill out all of the extra child inline forms. If I comment out the ChildInline class' form = ChildForm line, I get the correct behavior, but, of course, the incorrect default value ("0" instead of "1"). How can I get the correct behavior and the correct default value?

Upvotes: 4

Views: 2092

Answers (1)

Antoine Pinsard
Antoine Pinsard

Reputation: 34942

Your field is an IntegerField. You must set initial to an integer.

When the form is permitted to be empty (which is the case here), full_clean is dismissed if the form did not change. has_changed() is determined by the emptiness of changed_data.

Field changes is determined by field.has_changed(initial, data) where data is passed through field.to_python(value), which returns an integer in the case of an IntegerField. On the other hand, initial is supposed to be of the right type and is kept as provided. This leads to a comparison: has_changed = ('1' != 1), which returns True, while you expected it to return False.

Upvotes: 2

Related Questions