2

我正在尝试使用 m2m 中间表保存表单。实现多选字段表单时出现此错误:

Cannot assign "[Option: Option1]": "StateOption.partoption" must be a "Option" instance.

但是,如果我注释掉表单将保存但一次只能保存 1 个选项。我希望能够有一个清单并让用户选择多个选项。

我正在使用这个:Django m2m form save " through " table 作为参考,但必须承认我不太了解它,尤其是 for 循环部分。

我的问题是:如何保存表格?我明白为什么我会收到实例错误,但我不知道如何解决它。此外,下面的 for 循环可能是错误的。任何有关如何修复 for 循环的建议将不胜感激。似乎这将允许我保存多项选择。

模型.py

class Disease(models.Model):
    disease = models.CharField(max_length=300, verbose_name="disease")

class Option(models.Model):
    relevantdisease = models.ForeignKey(Disease, verbose_name="disease")
    option = models.CharField(max_length=300, verbose_name="treatment Options")

class State(models.Model):
   state = models.CharField(max_length=300, verbose_name='state')
   relevantdisease = models.ForeignKey(Disease, verbose_name="disease")
   relevantoption = models.ManyToManyField(Option, through='StateOption')

class StateOption(models.Model):
   parttstate = models.ForeignKey(State)
   partoption = models.ForeignKey(Option)
   relevantoutcome = models.ManyToManyField(Outcome, through='StateOptionOutcome')

表格.py

class StateOptionForm(forms.ModelForm):
    partoption = forms.ModelMultipleChoiceField(queryset=Option.objects.all(), required=True, widget=forms.CheckboxSelectMultiple) 
    #if I comment the line above out, then my selection will save but only 1 at at time. There are multiple options for a 'state' and I'd like to capture it all at once

    class Meta:
       model = StateOption
       exclude = ['parttstate', 'relevantoutcome']

视图.py

def diseasestateoption(request, disease_id, state_id):

    state = get_object_or_404(State, pk=state_id)
    disease = get_object_or_404(Disease, pk=disease_id)  

    if request.method == "POST":
        form = StateOptionForm(request.POST, request.FILES)

        if form.is_valid(): 
           profile = form.save(commit=False)
           profile.user = request.user
           profile.save()

           for state in request.POST.getlist('relevantoption'): #don't really understand this part and I'm probably not implementing this right
               option = StateOption.objects.create(partstate=state, partoption=profile)

       return HttpResponseRedirect(reverse('state', kwargs={'disease_id':disease_id}))

else:
    form = StateOptionForm()

模板

 <form class="option_form" action="{% url "diseasestateoption" disease.pk state.pk %}" method="post">{% csrf_token %}

    {{ disease }}
    {{ state }}
    {{option}}

我之前在这里发布过类似的问题,但没有得到答案。我是 django 和 stackoverflow 的新手,所以任何帮助将不胜感激。

编辑

我能够在没有中间表并使用 save_m2m() 的情况下实现这一点,但是,我将需要一个中间表用于我的程序的下一部分......如果我可以先解决这部分。

4

1 回答 1

1

你的错误:

Cannot assign "[Option: Option1]": "StateOption.partoption" must be a "Option" instance.

告诉您您正在尝试将某些内容保存到partoption不是Option实例的字段中。具体来说,这发生在您的视图的 for 循环中:

option = StateOption.objects.create(partstate=state, partoption=profile)

原因是它profile不是一个Option. 这是一个StateOption例子。

for 循环:

for state in request.POST.getlist('relevantoption'):

表示您在模板中指定了多个“相关选项”字段。您也可以发布您的模板代码吗?

更新:

这里发生了几件事。首先,模型。让我们将字段名称更改为 options 以更清楚地说明它们的真正含义:

class State(models.Model):
    state = models.CharField(max_length=300, verbose_name='state')
    relevantdisease = models.ForeignKey(Disease, verbose_name="disease")
    # Note: I've renamed this field to 'options' for clarity
    options = models.ManyToManyField(Option, through='StateOption')

现在,您需要一个表单来指定 State 实例的options使用复选框。我们将基于 State 模型创建一个专用表单来执行此操作:

class UpdateStateWithOptionsForm(forms.ModelForm):
    class Meta:
       model = State
       exclude = ['state', 'relevantdisease']

    def __init__(self, *args, **kwargs):

        # call the parent init
        super(UpdateStateWithOptionsForm, self).__init__(*args, **kwargs)

        # change the widget to use checkboxes
        self.fields['options']forms.ModelMultipleChoiceField(
            queryset=Option.objects.all(), 
            required=True, 
            widget=forms.CheckboxSelectMultiple)

现在我们可以编写视图代码来查找用户选择的选项 ID:

# Note: may not need disease_id if it is supposed to be the state's disease?
def diseasestateoption(request, state_id):

    state = get_object_or_404(State, pk=state_id)

    if request.method == "POST":
        form = UpdateStateWithOptionsForm(request.POST, instance=state)

        if form.is_valid(): 
           for option_id in request.POST.getlist('options'):
               state_option = StateOption.objects.create(partstate=state, partoption_id=int(option_id)

       return HttpResponseRedirect(reverse('state', kwargs={'disease_id':state.disease_id}))

    else:
        form = UpdateStateWithOptionsForm(instance=state)

    return render_to_response(
        'some_template.html',
        {
            'form': form,
            'state': state,
        },
        context_instance=RequestContext(request)
    )

好的,那它是如何工作的?查看呈现的模板 HTML 可能会有所帮助。表单的样板模板代码:

<form action="{% url 'diseasestateoption' state.pk %}" method="post">
    {% csrf_token %}

    {% if form.non_field_errors %}
        {{ form.non_field_errors }}
    {% endif %}

    {% for field in form %}
        {{ field.label }}:
        {{ field }}
        {% if field.errors %}
            {{ field.errors|striptags }}
        {% endif %}
    {% endfor %}

    <button type="submit">Save</button>
</form>

将其放入您的模板中,然后访问该页面并查看源代码。在 HTML 表单中,您会注意到一组输入的名称options,每个输入的值对应于 Option 实例的主键(来自我们在 ModelForm 中指定的查询集)。您视图中的 for 循环只是获取该列表并使用 Option pk 为 State 实例创建与该 Option 关联的 StateOption。

注意:这是未经测试的代码,其中可能存在错误!

希望这会有所帮助,祝你好运!

于 2013-08-24T21:32:34.840 回答