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 BaseModelFormSet
的 save_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
我正在将 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 BaseModelFormSet
的 save_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