Django公司管理员py:save_model()不是由调用的模型.保存()在ModelForm的save方法中

2024-10-01 09:39:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我的博客文章模型有多对多标签字段:

tags = models.ManyToManyField(PostTag)

但是编辑它很不舒服,我把我的模型修改成这样:

^{pr2}$

然后我修改了我的admin.py

class BlogAdminForm (forms.ModelForm):
    tagging = forms.CharField(required=False, label="Tags", max_length=200,
                        widget=forms.TextInput(attrs={'class':'vTextField'}))

    class Meta:
        model = BlogPost

    def __init__(self, *args, **kwargs):
        super(BlogAdminForm, self).__init__(*args, **kwargs)

        if kwargs.has_key('instance'):
            instance = kwargs['instance']
            self.initial['tagging'] = instance.tagging

    def save(self, commit=True):
        model = super(BlogAdminForm, self).save(commit=False)
        model.tagging = self.cleaned_data["tagging"]

        if commit:
            model.save()

        return model

这很好,但只适用于编辑对象。我试图创建新对象时出错。为什么?因为多对多关系可以与尚未在数据库中且没有主键的对象一起使用(“BlogPost”实例需要有主键值才能使用多对多关系)。我试图通过以下方式编辑save方法来解决这个问题:

def save(self, commit=True):
    model = super(BlogAdminForm, self).save(commit=False)
    try:
        model.tagging = self.cleaned_data["tagging"]
    except ValueError:
        model.save()
        model.tagging = self.cleaned_data["tagging"]

    if commit:
        model.save()

这解决了最初的问题。但是现在model.save()没有调用管理模型的save_model方法:

class BlogAdmin (admin.ModelAdmin):
    # ... 
    form = BlogAdminForm

    def save_model(self, request, obj, form, change):
        obj.author = request.user
        obj.save()

结果我得到了一个新的错误:null value in column "author_id" violates not-null constraint.我做错了什么?我可以手动调用这个方法吗?在


Tags: instance模型selffalse编辑modelsavedef
1条回答
网友
1楼 · 发布于 2024-10-01 09:39:59

在保存实例之后,必须保存标记,这意味着要在save_model函数中保存。这与您的标记操作代码无关:如果您查看documentation for the ^{} method,它会说:

Another side effect of using commit=False is seen when your model has a many-to-many relation with another model. If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn't possible to save many-to-many data for an instance until the instance exists in the database.

To work around this problem, every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass. After you've manually saved the instance produced by the form, you can invoke save_m2m() to save the many-to-many form data.

有几种方法可以解决你的问题。您可以编写一个widget,在标记ID和逗号分隔的标记名列表之间来回转换,然后在save_model方法中调用form.save_m2m()。但是这种方法的缺点是,在解码来自小部件的值时,您必须创建新的标记,即使表单没有保存(可能是因为表单中其他地方的验证错误)。在

因此,我认为在这种情况下,更好的方法是将您自己的save_tags方法添加到表单中:

class BlogAdminForm(forms.ModelForm):
    tagging = forms.CharField(required=False, label="Tags", max_length=200,
                              widget=forms.TextInput(attrs={'class':'vTextField'}))

    class Meta:
        model = Post

    def __init__(self, *args, **kwargs):
        super(BlogAdminForm, self).__init__(*args, **kwargs)
        if 'instance' in kwargs:
            tags = (t.name for t in kwargs['instance'].tags.all())
            self.initial['tagging'] = ', '.join(tags)

    def save_tags(self, obj):
        obj.tags = (Tag.objects.get_or_create(name = tag.strip())[0]
                    for tag in self.cleaned_data['tagging'].split(','))

class BlogPostAdmin(admin.ModelAdmin):
    form = BlogAdminForm

    def save_model(self, request, obj, form, change):
        obj.author = request.user
        obj.save()
        form.save_tags(obj)

请注意,我将标记操作代码移到了表单中:我认为它属于这里,而不是模型中,因为它都是关于用户输入的。我还做了一些风格上的改进:

  • 'instance' in kwargskwargs.has_key('instance')简单。

  • 生成器表达式(t.name for t in kwargs['instance'].tags.all())比在for循环中构建列表简单。

  • ^{} method是一个方便的快捷方式,可以避免try: ... except ObjectDoesNotExist: ...

  • 您可以直接分配给ManyToMany字段,而不是先调用clear,然后再调用add(而且,当标记不变时,它更有效)。

相关问题 更多 >