Django 管理表单和具有默认值的内联模型

Django admin form and inlined model with default value

我正在将 Django 应用程序从 1.4 更新到 1.8,并且遇到了 Django 管理的一个小问题。

我的模型是这样的

def new_key():
   return binascii.b2a_hex(os.urandom(20))

class ApiKey(models.Model):
  customer = models.ForeignKey(UserProfile)
  key = models.CharField(max_length=40, default=new_key)

而admin.py是

class ApiKeyInline(admin.StackedInline):
  model = ApiKey
  extra = 0

class UserProfileAdmin(admin.ModelAdmin):
  inlines = [ApiKeyInline]

admin.site.register(UserProfile, UserProfileAdmin)

使用管理页面时 api 密钥会正确填充一个随机值。但是,在保存 UserProfile 时,它​​并没有被保存,就好像什么都没有添加一样。如果我手动更改自动生成的密钥保存中的单个字母,则可以正常工作。在我看来,这是 django 检测到变化或类似问题的问题。

有什么建议吗?代码在 1.4 中有效。

经过简短调查,我找到了 class BaseModelFormSetsave_new_objects,位于 django/forms/models.py。 它具有以下检查:if not form.has_changed()

看起来很有前途,是吧?现在,我们要"enhance"这个方法。从哪儿开始?嗯...内联继承自 InlineModelAdmin,它有一个 get_formset 方法。所以...

class ApiKeyInline(admin.StackedInline):
    model = ApiKey
    extra = 0

    def get_formset(self, *args, **kwargs):
        formset = super(EmailInlineAdmin, self).get_formset(*args, **kwargs)
        # at this point, formset is a generated class called like "ApiKeyFormSet"
        # as it is a regular python objects, no one stops us from playing with it
        formset.save_new_objects = ApiKeyInline.my_own_save_new_objects_method
        return formset

    def my_own_save_new_objects_method(self, commit=True):
        # here should be something like 
        # django/forms/models.py    BaseModelFormSet.save_new_objects
        return self.new_objects

my_own_save_new_objects_method的内容你要自己编辑。原始方法再次在 dist-packages/django/forms/models.py 中,要么通过 super 调用它,要么完全自己写一些东西,无论如何跳过检查。

此外,也许这是一个糟糕且过于复杂的解决方案。我有一种感觉,应该有一个更好的。例如,不在模型中设置 default=new_key,而是为表单字段设置默认值,例如

旧线程之一中有解决方案:How to force-save an "empty"/unchanged django admin inline?

您应该将内联表单标记为始终更改。

from django.forms import ModelForm
from .models import UserProfile

class AlwaysChangedModelForm(ModelForm):
    def has_changed(self):
        """ Should returns True if data differs from initial. 
            By always returning true even unchanged inlines will get   validated and saved."""
        return True

#An inline model
class ApiKey(admin.StackedInline):
    model = ApiKey
    extra = 0
    form = AlwaysChangedModelForm