Django Inline Formsets的初始数据

15 投票
3 回答
15984 浏览
提问于 2025-04-16 01:29

我做了一个表单来保存食谱。这个表单使用了一个普通表单和一个内联表单集。我有一些用户,他们手里有包含食谱的文本文件,他们希望能方便地复制粘贴数据,以便更容易地输入。我已经找到了如何在处理原始文本输入后填充表单部分的方法,但我还不知道怎么填充内联表单集。

看起来解决方案几乎已经在这里说明了: 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 个回答

2

我在使用 Django 4.1 的时候,给内联表单集传递 initial 的时候没有遇到任何问题——这里特别强调不是工厂方法。

MyInlineFormSet = inlineformset_factory(ParentModel, ChildModel, ...)
formset = MyInlineFormSet(initial=[{...}])

距离最初的问题已经过去12年了,所以代码可能已经发生了变化,或者我可能误解了这里的一些回答和评论,但我希望这个澄清能对正在寻找解决办法的人有所帮助。

2

我没法让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)
24

我首先建议你走简单的路线:先保存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列表的一个字典。就我个人的经验来看,这样显示表单集合是有效的,但我还没有尝试保存。

撰写回答