Django Inline Formsets的初始数据
我做了一个表单来保存食谱。这个表单使用了一个普通表单和一个内联表单集。我有一些用户,他们手里有包含食谱的文本文件,他们希望能方便地复制粘贴数据,以便更容易地输入。我已经找到了如何在处理原始文本输入后填充表单部分的方法,但我还不知道怎么填充内联表单集。
看起来解决方案几乎已经在这里说明了: http://code.djangoproject.com/ticket/12213,但我就是无法把这些信息拼凑起来。
我的模型:
#models.py
from django.db import models
class Ingredient(models.Model):
title = models.CharField(max_length=100, unique=True)
class Meta:
ordering = ['title']
def __unicode__(self):
return self.title
def get_absolute_url(self):
return self.id
class Recipe(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank=True)
directions = models.TextField()
class Meta:
ordering = ['title']
def __unicode__(self):
return self.id
def get_absolute_url(self):
return "/recipes/%s/" % self.id
class UnitOfMeasure(models.Model):
title = models.CharField(max_length=10, unique=True)
class Meta:
ordering = ['title']
def __unicode__(self):
return self.title
def get_absolute_url(self):
return self.id
class RecipeIngredient(models.Model):
quantity = models.DecimalField(max_digits=5, decimal_places=3)
unit_of_measure = models.ForeignKey(UnitOfMeasure)
ingredient = models.ForeignKey(Ingredient)
recipe = models.ForeignKey(Recipe)
def __unicode__(self):
return self.id
这个食谱表单是通过一个模型表单创建的:
class AddRecipeForm(ModelForm):
class Meta:
model = Recipe
extra = 0
在视图中的相关代码(解析表单输入的调用已删除):
def raw_text(request):
if request.method == 'POST':
...
form_data = {'title': title,
'description': description,
'directions': directions,
}
form = AddRecipeForm(form_data)
#the count variable represents the number of RecipeIngredients
FormSet = inlineformset_factory(Recipe, RecipeIngredient,
extra=count, can_delete=False)
formset = FormSet()
return render_to_response('recipes/form_recipe.html', {
'form': form,
'formset': formset,
})
else:
pass
return render_to_response('recipes/form_raw_text.html', {})
在上面这个空的 FormSet() 中,我可以成功地打开页面。我尝试了几种方法来给表单集提供数量、计量单位和我识别的成分,包括:
- 设置初始数据,但这对内联表单集不起作用
- 传递一个字典,但这会产生管理表单错误
- 尝试使用 init,但我在这方面有点无从下手
任何建议都非常感谢。
3 个回答
我在使用 Django 4.1 的时候,给内联表单集传递 initial
的时候没有遇到任何问题——这里特别强调不是工厂方法。
MyInlineFormSet = inlineformset_factory(ParentModel, ChildModel, ...)
formset = MyInlineFormSet(initial=[{...}])
距离最初的问题已经过去12年了,所以代码可能已经发生了变化,或者我可能误解了这里的一些回答和评论,但我希望这个澄清能对正在寻找解决办法的人有所帮助。
我没法让Aram Dulyan的代码在这个上面运行。
for subform, data in zip(formset.forms, recipe_ingredients):
subform.initial = data
看起来在django 1.8版本中,有什么东西变了,我无法对一个缓存属性进行迭代。
forms - django.utils.functional.cached_property对象在0x7efda9ef9080
我遇到了这个错误:
zip的第一个参数必须支持迭代。
不过我还是直接把字典拿来分配给我的表单集合,结果是可以的。我是从这里找到的例子:
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#understanding-the-managementform
从django.forms导入formset_factory
从我的应用中导入ArticleForm
ArticleFormSet = formset_factory(ArticleForm, can_order=True)
formset = ArticleFormSet(initial=[
{'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
{'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
])
我在模板中分配表单集合的代码
return self.render_to_response(
self.get_context_data(form=form, inputvalue_numeric_formset=my_formset(initial=formset_dict)
我首先建议你走简单的路线:先保存Recipe
(食谱)和RecipeIngredient
(食材),然后在创建FormSet
(表单集合)时使用刚刚保存的Recipe
。你可能还想在食谱中添加一个“reviewed”(已审核)的布尔字段,用来表示这些表单集合是否得到了用户的批准。
不过,如果你不想走这条路,出于某种原因,你可以这样填充你的表单集合:
我们假设你已经把文本数据解析成了食材,并且有一个像这样的字典列表:
recipe_ingredients = [
{
'ingredient': 2,
'quantity': 7,
'unit': 1
},
{
'ingredient': 3,
'quantity': 5,
'unit': 2
},
]
在“ingredient”(食材)和“unit”(单位)字段中的数字是对应食材和计量单位对象的主键值。我假设你已经想出了一种方法来将文本与数据库中的食材匹配,或者创建新的食材。
然后你可以这样做:
RecipeFormset = inlineformset_factory(
Recipe,
RecipeIngredient,
extra=len(recipe_ingredients),
can_delete=False)
formset = RecipeFormset()
for subform, data in zip(formset.forms, recipe_ingredients):
subform.initial = data
return render_to_response('recipes/form_recipe.html', {
'form': form,
'formset': formset,
})
这会将表单集合中每个表单的initial
属性设置为来自你的recipe_ingredients
列表的一个字典。就我个人的经验来看,这样显示表单集合是有效的,但我还没有尝试保存。