多对多关系的 Select2 小部件

Posted

技术标签:

【中文标题】多对多关系的 Select2 小部件【英文标题】:Select2 widget for ManyToMany relationship 【发布时间】:2017-09-02 14:04:39 【问题描述】:

我正在使用django-select2 小部件来获得更好的ManyToMany 小部件。视图看起来更好,我可以搜索和选择许多不同的用户。但是,当我保存模型时,允许的用户列表是空白的。我直接从视图和单元测试中尝试了这一点。我无法理解我做错了什么。

有什么想法吗?

我强烈怀疑有两个问题:一个是保存表单创建的模型,另一个是我如何设置data 字典值。


我的模特:

class ProjectCode(models.Model):
    allowed_users = models.ManyToManyField(User, blank=True)

我的表格:

class ProjectCodeForm(forms.ModelForm):
    class Meta:
        model = ProjectCode
        fields = '__all__'
        widgets = 'allowed_users': Select2MultipleWidget(), 

我的观点:

def create_code(request):
    context = 
    if request.method == 'POST':
        form = ProjectCodeForm(request.POST)
        if form.is_valid():
            new = form.save(commit=False)
            new.save()
    form = ProjectCodeForm()
    context['form'] = form
    return render(request, 'order_book/create_code.html', context)

我的测试:

def test_code_create_post_pass(self):
    init = len(ProjectCode.objects.all())
    data = 'allowed_users': [self.jdoe.pk, ], 
    response = self.client.post('/order_book/code/create/', data)
    self.assertEqual(response.status_code, 200)
    self.assertContains(response, "Create a project code")
    self.assertEqual(init + 1, len(ProjectCode.objects.all()))
    self.assertEqual("jdoe", ProjectCode.objects.all()[0].display_allowed_users())

此测试失败:

======================================================================
FAIL: test_code_create_post_pass (order_book.tests.AuthorisedViewTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/path/tests.py", line 385, in test_code_create_post_pass
    "jdoe", ProjectCode.objects.all()[0].display_allowed_users())
AssertionError: u'jdoe' != u''
- jdoe
+

【问题讨论】:

【参考方案1】:

发个问题,继续玩pdb,突然冒出一个想法……我把视图修改为:

def create_code(request):
    context = 
    if request.method == 'POST':
        form = ProjectCodeForm(request.POST)
        if form.is_valid():
            new = form.save(commit=False)
            # I am doing "clever" things with the new object here,
            # like setting a creator and created date/time…
            new.save()
            for usr in form.cleaned_data['allowed_users']:  # ← This is NOT correct
                new.allowed_users.add(usr)  # ← Neither is this…
    form = ProjectCodeForm()
    context['form'] = form
    return render(request, 'order_book/create_code.html', context)

我收到一条错误消息,提示 allowed_users 无法设置为 new 还没有主键。这导致我在保存对象后尝试设置数据。

请注意,测试data 是正确的,现在测试通过了。

或者,您可以read the freaking documentation 并使用正确的方法save_m2m()

def create_code(request):
    context = 
    if request.method == 'POST':
        form = ProjectCodeForm(request.POST)
        if form.is_valid():
            new = form.save(commit=False)
            # I am doing "clever" things with the new object here,
            # like setting a creator and created date/time…
            new.save()
            form.save_m2m()  # ← This is CORRECT!
    form = ProjectCodeForm()
    context['form'] = form
    return render(request, 'order_book/create_code.html', context)

【讨论】:

以上是关于多对多关系的 Select2 小部件的主要内容,如果未能解决你的问题,请参考以下文章

在 Django Admin 中过滤多对多框

如何在 WinForms 中绑定多对多关系?

Hibernate映射多对多双向关联关系(小案例)

Yii2 Select2 - 更新时选择的值 - 联结表(多对多)

多对多表的设计小案例

Hibernate学习———— 双向多对多映射关系