Django m2m字段未分配给视图中的父对象

时间:2018-02-18 18:47:21

标签: django django-views

背景

我正在尝试为我的项目实现标记系统。各种插件解决方案(taggit,tagulous)在某些方面都不合适。

我想允许用户从现有标签中选择或在Select2标记字段中创建新标签。 可以毫无问题地添加或删除现有的标记。我的困难在于动态生成和分配标签。

我的方法

Select2通过自动完成功能有助于在DOM中以不同方式呈现手动输入的标记与从数据库中选择的标记。所以在点击提交时,我有javascript收集新标签并将它们串在一起隐藏输入的值,然后从Select2字段中删除它们以避免任何验证错误(表单另外将标签名称POST为ids,这会抛出db错误)。

在视图中,我迭代了所需的新标签。对于每个条目,我创建新标记,然后将其添加到父对象的相关集。

问题

  • 虽然这成功创建了每个标记(通过管理员验证),但它不会将其添加到相关集中。
  • 在(明显没有成功)相关集合添加上没有生成错误。
  • 新生成的标签已正确实例化,可以选择Select2并在后续UpdateView上成功分配,因此我确定问题在于将标签分配给父级。
  • 通过Django shell执行的相同代码完美无缺,因此我不相信它是一个简单的语法错误。

因此,问题的轨迹似乎是在POST视图代码中将新生成的标记添加到父级,但我无法看到代码在哪里误入歧途。

感谢您的任何见解或建议!

models.py:

class Recipe_tag(models.Model):
    id = models.UUIDField(primary_key=True,default=uuid.uuid4,null=False)
    tag = models.CharField('Tag name',max_length=32,null=False,unique=True)

    def __str__(self):
        return str(self.tag)


class Recipe_base(models.Model):
    id = models.UUIDField(primary_key=True,default=uuid.uuid4,null=False)
    name = models.CharField('Recipe name',max_length=128,null=False)
    tags = models.ManyToManyField(Recipe_tag,related_name='recipes',null=True,blank=True)

    def __str__(self):
        return str(self.name)

视图的帖子部分:

def post(self, request, *args, **kwargs):
        self.object = None
        r = Recipe_base.objects.get(id=self.kwargs.get('pk'))
        form = RecipeUpdateTagsForm(request.POST,instance=r)
        form_valid = form.is_valid()
        if form_valid:
            if form.has_changed:
                f = form.save(commit=False)
                clean = form.cleaned_data
                f.addedTags = clean['addedTags']
                if f.addedTags == 'placeholder':
                    pass
                else:
                    new_tags = f.addedTags.split(',')
                    for new_tag in new_tags:
                        a = Recipe_tag(tag=new_tag)
                        a.save()
                        r.tags.add(a)
                f.save()
                form.save_m2m()
            else:
                pass
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

1 个答案:

答案 0 :(得分:0)

进一步挖掘,我在另一个网站上发现了一个帖子,其中OP遇到了同样的问题。诀窍是完全从“创建”或“更新”过程中删除m2m赋值,因为form_valid中发生的最终save()将丢弃对父相关集的任何更改。

在我的情况下,解决方案是在最终的save()发生后直接将新标签的迭代/赋值转换为form_valid:

def form_valid(self, form, **kwargs):
    self.object = form.save()
    addedTags = kwargs['addedTags']
    r = Recipe_base.objects.get(id=self.kwargs.get('pk'))
    if addedTags == 'placeholder':
        pass
    else:
        new_tags = addedTags.split(',')
        for new_tag in new_tags:
            a = Recipe_tag(tag=new_tag)
            a.save()
            # print("debug // new tag: %s, %s" % (a.tag, a.id))
            r.tags.add(a)
            # print("debug // added %s to %s" % (a.tag,r.id))
    return HttpResponseRedirect(self.get_success_url())

def get(self, request, *args, **kwargs):
    self.object = Recipe_base.objects.get(id=self.kwargs.get('pk'))
    recipe_name = self.object.name
    recipe_id = self.kwargs.get('pk')
    form = RecipeUpdateForm(instance=Recipe_base.objects.get(id=self.kwargs.get('pk')))
    return self.render_to_response(self.get_context_data(form=form,recipe_name=recipe_name,recipe_id=recipe_id))

def post(self, request, *args, **kwargs):
    self.object = None
    r = Recipe_base.objects.get(id=self.kwargs.get('pk'))
    form = RecipeUpdateForm(request.POST,instance=r)
    form_valid = form.is_valid()
    #print("debug // form_valid PASSED")
    if form_valid:
        if form.has_changed:
            #print("debug // Form changed...")
            f = form.save(commit=False)
            #print("debug // Passes save C=F")
            clean = form.cleaned_data
            #print('debug // pre-existing tags: ',clean['tags'])
            f.addedTags = clean['addedTags']
            addedTags = clean['addedTags']
            #print('debug // manual tags: ',clean['addedTags'])
            f.save()
            form.save_m2m()
            #print("debug // Form saved")
        else:
            #print("debug // Form unchanged, skipping...")
            pass
        #print("debug // Reached successful return")
        return self.form_valid(form, addedTags=addedTags,pk=r.id)
    else:
        #print("debug // Form fails validation")
        #print("debug // Reached unsuccessful return")
        return self.form_invalid(form)