Django:ManyToManyField,如果对象尚不存在,则添加对象

ben*_*nto 6 forms django many-to-many models

我正在尝试使用 Django 中的 ModelForms 将内容添加到我的数据库,其中包括 ManyToManyField。这是我的模型的相关部分:

class Category(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()

    def __unicode__(self):
        return self.name

class Recipe(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
    user = models.ForeignKey(User)
    image = models.ImageField(upload_to='images/recipethumbs/', null = True, blank=True)
    ingredients = models.ManyToManyField(Ingredient, through="IngredientMap")
    categories = models.ManyToManyField(Category)
    citation = models.CharField(max_length=200)

    def __unicode__(self):
Run Code Online (Sandbox Code Playgroud)

默认情况下,ManyToManyFields 由多项选择表示。这允许用户从类别对象中预先存在的字段中进行选择。我想更改此设置,以便用户可以选择当前存在的类别,或者添加新的类别(如果不存在)。看起来,这可以通过将小部件更改为 TextInput 来完成。

class RecipeForm(ModelForm):
    class Meta:
        model = Recipe
        widgets = {
            'categories': TextInput(attrs={'size': 40,}),
            'ingredients': Textarea,
        }
Run Code Online (Sandbox Code Playgroud)

但是,如果用户输入类别表中当前不存在的类别,则 Django 会抱怨我需要“输入值列表”。如果新类别尚不存在,如何获取添加新类别的表单?

正如 Ilvar 建议的那样,我在自定义 .save() 中的解决方案:

categories = re.findall(r'\w+[\w\s]+', self.cleaned_data.get('categories')) #returns an array

        if commit:
            m.save()

        # You can only assign m2m if the Recipe object has been saved.
        for category in categories:
            try:
                category_in_db = Category.objects.get(name=category)
            except:
                category_in_db = None
            if category_in_db:
                m.categories.add(category_in_db)
            else:
                m.categories.create(name=category)

        return m
Run Code Online (Sandbox Code Playgroud)

ilv*_*var 1

您需要exclude从表单中创建 m2m 字段,并CharField手动添加表单类本身。然后重写以在和__init__中设置此字段。您可以对中的文本做任何您想做的事情。我想,在您的情况下,它将用逗号分隔,为每个值创建一个类别,并在保存父表单之后将类别列表分配给对象中的字段,然后再返回结果。也许还添加一些自动完成功能以避免客户端输入错误。initialsavesavecleaned_data