Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何构造具有ModelChoiceField的Django表单?_Python_Django_Forms - Fatal编程技术网

Python 如何构造具有ModelChoiceField的Django表单?

Python 如何构造具有ModelChoiceField的Django表单?,python,django,forms,Python,Django,Forms,我正在尝试为我的表单编写一个测试,该表单使用自定义ModelChoiceField: from django.forms import ModelChoiceField class CycleModelChoiceField(ModelChoiceField): def label_from_instance(self, cycle): return str(cycle.begin_date) 用户在此字段中选择的任何内容都需要通过重写clean()方法传递到其他两个字

我正在尝试为我的表单编写一个测试,该表单使用自定义ModelChoiceField:

from django.forms import ModelChoiceField
class CycleModelChoiceField(ModelChoiceField):
    def label_from_instance(self, cycle):
        return str(cycle.begin_date)
用户在此字段中选择的任何内容都需要通过重写
clean()
方法传递到其他两个字段(DateField和radio ChoiceField)。这是我需要测试的复杂逻辑

以下是我在测试中尝试过的:

self.client.login(username='user', password='1234')
response = self.client.get(reverse('my_form'), follow=True)
cyc = list(CycleDate.objects.all())[0]
form_data = {'type_of_input': '0', 'cycle': cyc,'usage_type': 'E',}
form = EnergyUsageForm(data=form_data, user=response.context['user'])
但是
form.is\u valid()
返回false,并且
form.errors
显示:

{'cycle': [u'Select a valid choice. That choice is not one of the available choices.']}
我的表单结构一定有问题<代码>“循环”:cyc显然没有按预期工作。我还尝试了
'cycle':'0'
'cycle':'1'

构造这样一个表单的正确方法是什么

编辑: 我应该解释一下有哪些选择。数据库中只有一个CycleDate,只有一个选项。在shell中运行测试行之后,我键入了
form.fields['cycle'].choices.choice(cyc)
,返回
(1,'2015-05-01')
。奇怪的是
form.fields['cycle'].queryset
返回
[]
。也许问题与此有关

EDIT2: 这是我的表格,上面有一个复杂的(读作:凌乱、可怕和可耻的)清洁方法:

class EnergyUsageForm(forms.Form):
    # Override init so that we can pass the user as a parameter.
    # Then put the cycle form inside init so that it can access the current user variable
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(EnergyUsageForm, self).__init__(*args, **kwargs)

        # Get the last 12 cycle dates for the current user
        td = datetime.date.today
        cycle_dates = CycleDate.objects.filter(cycle_ref=Account.objects.get(holder__user=user).cycle,
                                               begin_date__lte=td).order_by('begin_date')
        self.fields['cycle'] = CycleModelChoiceField(queryset = cycle_dates,
                                                      required = False,
                                                      widget = forms.Select(attrs={"onChange":'changeCalendar()'}),
                                                      label = "Choose a billing cycle")


    type_of_input = forms.ChoiceField(required=False,
                                    widget=forms.Select(attrs={"onChange": "switchInput()"}),
                                    choices=INPUT,
                                    initial='0',
                                    label="Choose a way to display usage", )

    end_date = forms.DateField(widget=forms.TextInput(attrs=
                                {
                                    'class':'datepicker'
                                }), 
                                label="Choose start date",
                                help_text='Choose a beginning date for displaying usage',
                                required=True,
                                initial=datetime.date.today,)

    period = forms.ChoiceField(required=True,
                                widget=forms.RadioSelect, 
                                choices=DISPLAY_PERIOD, 
                                initial='01',
                                label="Choose period to display",)

    usage_type = forms.ChoiceField(required=True,
                                widget=forms.RadioSelect,
                                choices=USAGE_TYPE,
                                initial='E',
                                label="Choose type of usage to display",)

    def clean_end_date(self):
        data = self.cleaned_data['end_date']

        if datetime.date.today() < data:
            raise forms.ValidationError("Don't choose a future date")
        # Always return the cleaned data, whether you have changed it or
        # not.
        return data

    def clean(self):
        cleaned_data = super(EnergyUsageForm, self).clean()
        selection = cleaned_data['type_of_input']
        # Check if the user wants to use cycle_dates instead
        if selection == '0':
            # Set the end_date and period
            cleaned_data['end_date'] = cleaned_data['cycle'].begin_date #MUST BE CHANGED TO END_DATE LATER
            cleaned_data['period'] = cleaned_data['cycle'].duration
        return cleaned_data
EDIT4:
除了这些混乱之外,每当我调用
form.is\u valid()
,我都会得到
KeyError:“cycle”
。可能是由于clean()方法试图在cycle字段具有无效选择时访问cleaned_数据['cycle']。

正如DanielRoseman所指出的,答案是使用实例的id

因此,使用ModelChoiceField构造表单的正确方法如下:

my_instance = MyModelName.objects.get(whatever instance you need)
form_data = {'my_regular_choice_field': '0', 'my_model_choice_field': my_instance.id}
form = MyFormName(data=form_data)
特别是线路

cyc = list(CycleDate.objects.all())[0]
  • 首先,您得到所有CycleDates的迭代器(ok,或多或少ok…)
  • 然后你把所有的东西都读到了记忆中(为什么!)
  • 最后。。您选择第一个列表元素(当然,如果列表为空,您将得到异常…)
为什么不:

cyc = CycleDate.objects.first()
if cyc:
    # cycle is a ModelChoice - in html it stores primary key!
    form_data = {'type_of_input': '0', 'cycle': cyc.pk, 'usage_type': 'E'}
    form = EnergyUsageForm(data=form_data, user=response.context['user'])
EnergyUsageForm的初始值:

def __init__(self, *args, **kwargs):
    user = kwargs.pop('user', None)
    super(EnergyUsageForm, self).__init__(*args, **kwargs)

    # today is a function!
    td = datetime.date.today()
    account = Account.objects.get(holder__user=user)
    cycle_dates = CycleDate.objects.filter(cycle_ref=account.cycle,
                                           begin_date__lte=td)
                                   .order_by('begin_date')

    self.fields['cycle'] = CycleModelChoiceField(queryset=cycle_dates,
                                                 required=False,
                                                 widget=forms.Select(attrs={"onChange":'changeCalendar()'}),
                                                 label = "Choose a billing cycle")

无论是哪种方式,请使用
Cycle.objects.first()
而不是调用
list
来计算查询集。请显示您的表单定义,包括“复杂”的干净逻辑。另外,关于
'Cycle':cyc.id
?我看到填充该字段的查询取决于用户。您确定您的Cycle对象与您传入的用户正确相关吗?正如我提到的,您没有将Cycle对象与用户关联,因此查询不会返回任何值。
cyc = CycleDate.objects.first()
if cyc:
    # cycle is a ModelChoice - in html it stores primary key!
    form_data = {'type_of_input': '0', 'cycle': cyc.pk, 'usage_type': 'E'}
    form = EnergyUsageForm(data=form_data, user=response.context['user'])
def __init__(self, *args, **kwargs):
    user = kwargs.pop('user', None)
    super(EnergyUsageForm, self).__init__(*args, **kwargs)

    # today is a function!
    td = datetime.date.today()
    account = Account.objects.get(holder__user=user)
    cycle_dates = CycleDate.objects.filter(cycle_ref=account.cycle,
                                           begin_date__lte=td)
                                   .order_by('begin_date')

    self.fields['cycle'] = CycleModelChoiceField(queryset=cycle_dates,
                                                 required=False,
                                                 widget=forms.Select(attrs={"onChange":'changeCalendar()'}),
                                                 label = "Choose a billing cycle")