对一个字段使用 wtform 验证时 "Not a valid choice" 是什么意思?

What is meant by "Not a valid choice" when using wtform validation for one field?

我在使用动态选项验证时收到一条无法理解的错误消息(其中一个 select 字段中的选项取决于另一个 select 字段中的选择。但是我不能 select 一个地区一旦 select 就成为一个城市。为什么不呢?必须做什么?

 for city in montaomodel.City.all().fetch(99999):  # TODO: only do this for the region
     try:
         form.area.choices.insert(long(city.key().id()),
         (str(city.key().id()), 'Select...'))
     except:
         pass

我用于插入和验证的整个代码块如下。

class AdLister(BaseRequestHandler,
               blobstore_handlers.BlobstoreUploadHandler):
    csrf_protect = False

    def post(self):
        logging.info("i post")
        ad = Ad()
        if users.get_current_user():
            ad.user = users.get_current_user()

        if self.current_user is not None:
            try:
                ad.usr = self.current_user
            except Exception, e:
                logging.info('exception %s' % str(e))
        logging.info("i post2")
        if self.request.get('type'):
            ad.type = self.request.get('type')
        if self.request.get('address'):
            ad.address = self.request.get('address')
        if self.request.get('rooms'):
            ad.number_of_rooms = int(self.request.get('rooms'))
        if self.request.get('size'):
            ad.size = float(self.request.get('size'))
        if self.request.get('regdate'):
            ad.regdate = int(self.request.get('regdate'))
        if self.request.get('mileage'):
            ad.mileage = int(self.request.get('mileage'))

        ad.category = self.request.get('category_group')

        form = AdForm(self.request.params)
        logging.info("i post23")
        if self.request.get('area'):
            for city in montaomodel.City.all().fetch(99999):  # TODO: only do this for the region
                #logging.info("i post232")
                #logging.info("region%s" , city.region.key().id())
                try:
                    form.area.choices.insert(str(long(city.key().id())),
                                     (str(city.key().id()), 'Select...'))
                except:
                    pass
        logging.info("i post3")
        if form.validate():
            title = to_unicode_or_bust(form.title.data)
            #unicode(form.title.data, 'utf-8')
            ad.title = title
            self.session['title'] = ad.title
            name = to_unicode_or_bust(form.name.data)  #, 'utf-8')
            ad.name = name
            self.session['name'] = ad.name
            ad.email = form.email.data
            self.session['email'] = ad.email
            ad.phoneview = form.phoneview.data
            self.session['phoneview'] = ad.phoneview
            try:
                if form.phonenumber.data:
                    ad.phonenumber = form.phonenumber.data
                    self.session['phonenumber'] = ad.phonenumber
            except:
                pass

            text = to_unicode_or_bust(form.text.data)  # , 'utf8')
            titletest = to_unicode_or_bust(form.title.data)



            ad.text = text
            self.session['text'] = ad.text
            ad.price = form.price.data.replace(' ', '').replace(',00',
                                                                '').replace('.00', '')
            try:
                if form.price.data:
                    ad.integer_price = form.price.data.replace(' ', ''
                    ).replace(',00', '').replace('.00', '')
            except:
                pass
            self.session['price'] = ad.price
            ad.url = self.request.host
            self.session['url'] = self.request.host
            ad.place = self.request.get('place')
            self.session['place'] = ad.place
            ad.postaladress = self.request.get('place')
            self.session['postaladress'] = ad.postaladress
            ad.put()
            self.session['ad_id'] = ad.key().id()
        else:

            self.render('createnewad.html', {
                'user': self.current_user,
                'session': self.auth.get_user_by_session(),
                'request': self.request,
                'form': form,
                'name': to_unicode_or_bust(form.name.data)  #.encode('utf-8')
            })
            return
        if self.request.get('currency'):
            ad.currency = self.request.get('currency')
            self.session['currency'] = ad.currency
        if self.request.get('cg'):
            ad.category = self.request.get('cg')
            self.session['category'] = ad.category
        if self.request.get('company_ad') == '1':
            ad.company_ad = True
            self.session['company_ad'] = 'True'
        ad.put()

        ad.url = self.request.host

        for upload in self.get_uploads():
            try:
                img = Image(reference=ad)
                img.primary_image = upload.key()
                image_url = images.get_serving_url(str(upload.key()), size=640)
                img.put()
                ad.hasimages = True
                ad.image_url = images.get_serving_url(str(upload.key()), size=640)
                ad.put()
                ad.blobs.append(upload.key())
                ad.put()
            except Exception, e:
                logging.error('There was an exception:%s' % str(e))
                pass
        ad.published = False
        if self.request.get('area'):
            city = \
                montaomodel.City.get_by_id(long(self.request.get('area'
                )))
            region = montaomodel.Region.get(city.region.key())
            ad.cities.append(city.key())
            ad.regions.append(region.key())
            ad.city = unicode(city.name)
            ad.region = unicode(region.name)


        ad.put()
        if self.current_user:
            ad.userID = str(self.current_user.auth_ids[0])
            ad.put()
            ad.usr = self.current_user.key.to_old_key()
            ad.put()

        image = ad.matched_images.get()
        image_url = None
        if image:
            if image.primary_image:
                try:
                    image_url = \
                        images.get_serving_url(str(image.primary_image.key()),
                                               size=640)
                except Exception, e:
                    image_url = '/images/' + str(image.key().id()) \
                                + '_small.jpg'
            else:
                image_url = '/images/' + str(image.key().id()) \
                            + '_small.jpg'
        imv = []
        for i in ad.matched_images:
            if i.primary_image:
                try:
                    i1 = \
                        images.get_serving_url(str(i.primary_image.key()))
                    imv.append(i1)
                except Exception, e:
                    i1 = '/images/' + str(image.key().id()) \
                         + '_small.jpg'
                    imv.append(i1)

        if ad.price:  # and doesn't contain separators
            try:
                price = \
                    i18n.I18n(self.request).format_decimal(int(ad.price))
            except Exception, e:
                price = ad.price
        else:
            price = ad.price

        self.render('preview.html', {
            'user': self.current_user,
            'session': self.auth.get_user_by_session(),
            'request': self.request,
            'ad': ad,
            'image_url': image_url,
            'imv': imv,
            'len': len(imv),
            'form': PreviewAdForm(),
            'price': price,
            })

我的表格 class 如下所示。

class AdForm(Form):
    categories = [
        ('1', _('All categories')),
        ('disabled', _('VEHICLES')),
        ('2010', _('Cars')),
        ('3', _('Motorcycles')),
        ('4', _('Accessories & Parts')),
        ('disabled', _('PROPERTIES')),
        ('7', _('Apartments')),
        ('8', _('Houses')),
        ('9', _('Commercial properties')),
        ('10', _('Land')),
        ('disabled', _('ELECTRONICS')),
        ('12', _('Mobile phones & Gadgets')),
        ('13', _('TV/Audio/Video/Cameras')),
        ('14', _('Computers')),
        ('disabled', _('HOME & PERSONAL ITEMS')),
        ('16', _('Home & Garden')),
        ('17', _('Clothes/Watches/Accessories')),
        ('18', _('For Children')),
        ('disabled', _('LEISURE/SPORTS/HOBBIES')),
        ('20', _('Sports & Outdoors')),
        ('21', _('Hobby & Collectables')),
        ('22', _('Music/Movies/Books')),
        ('23', _('Pets')),
        ('20', _('BUSINESS TO BUSINESS')),
        ('24', _('Hobby & Collectables')),
        ('25', _('Professional/Office equipment')),
        ('26', _('Business for sale')),
        ('disabled', _('JOBS & SERVICES')),
        ('28', _('Jobs')),
        ('29', _('Services')),
        ('30', _('Events & Catering')),
        ('31', _('Others')),
        ('1000', _('Sports & Outdoors')),
        ('1010', _('Hobby & Collectables')),
        ('1020', _('Hobby & Collectables')),
        ('1030', _('Music/Movies/Books')),
        ('1050', _('Pets')),
        ('1080', _('BUSINESS TO BUSINESS')),
        ('1100', _('Hobby & Collectables')),
        ('1090', _('Professional/Office equipment')),
        ('2010', _('Business for sale')),
        ('2030', _('Sports & Outdoors')),
        ('2040', _('Hobby & Collectables')),
        ('2080', _('Music/Movies/Books')),
        ('2070', _('Pets')),
        ('3000', _('BUSINESS TO BUSINESS')),
        ('3040', _('Hobby & Collectables')),
        ('3050', _('Professional/Office equipment')),
        ('3060', _('Business for sale')),
        ('4000', _('Sports & Outdoors')),
        ('4010', _('Hobby & Collectables')),
        ('4020', _('Music/Movies/Books')),
        ('4040', _('Pets')),
        ('4030', _('BUSINESS TO BUSINESS')),
        ('4090', _('Hobby & Collectables')),
        ('4060', _('Professional/Office equipment')),
        ('4070', _('Business for sale')),
        ('5030', _('Music/Movies/Books')),
        ('5020', _('Pets')),
        ('5010', _('BUSINESS TO BUSINESS')),
        ('5040', _('Hobby & Collectables')),
        ('6010', _('Professional/Office equipment')),
        ('6020', _('Business for sale')),
        ('6030', _('Music/Movies/Books')),
        ('6040', _('Pets')),
        ('7010', _('BUSINESS TO BUSINESS')),
        ('Other', _('Hobby & Collectables')),
    ]

    regions = [('', _('Choose')), ('3', _('Delhi')), ('4', _('Maharasta'
    )), ('7', _('Gujarat'))]
    cities = [('', _('«Choose city»')), ('3', _('Mumbai')), ('4',
                                                             _('Delhi'))]
    nouser = HiddenField(_('No user'))  # dummy variable to know whether user is logged in
    name = TextField(_('Name'),
                     [validators.Required(message=_('Name is required'
                     ))], widget=MontaoTextInput())
    title = TextField(_('Subject'),
                      [validators.Required(message=_('Subject is required'
                      ))], widget=MontaoTextInput())
    text = TextAreaField(_('Ad text'),
                         [validators.Required(message=_('Text is required'
                         ))], widget=MontaoTextArea())
    phonenumber = TextField(_('Phone'), [validators.Optional()])
    type = TextField(_('Type'),
                     [validators.Required(message=_('Type is required'
                     ))])
    phoneview = BooleanField(_('Display phone number on site'))
    price = TextField(_('Price'), [validators.Regexp('^[0-9]+$',
                                                     message=_(
                                                         'This is not an integer number, please see the example and try again'
                                                     )), validators.Optional()], widget=MontaoTextInput())
    email = TextField(_('Email'),
                      [validators.Required(message=_('Email is required'
                      )),
                       validators.Email(message=_('Your email is invalid'
                       ))], widget=MontaoTextInput())

    area = SelectField(_('City'), choices=cities,
                       validators=[validators.Optional()])
    category_group = SelectField(_('Category'), choices=categories,
                                 validators=[validators.Required(message=_('Category is required'
                                 ))])

    def validate_name(form, field):
        if len(field.data) > 50:
            raise ValidationError(_('Name must be less than 50 characters'
            ))

    def validate_email(form, field):
        if len(field.data) > 60:
            raise ValidationError(_('Email must be less than 60 characters'
            ))

    def validate_price(form, field):
        if len(field.data) > 8:
            raise ValidationError(_('Price must be less than 9 integers'
            ))
    def validate_area(form, field):
        if len(field.data) > 888:
            raise ValidationError(_('Dummy validator'
            ))

这里有一个代码示例(1个省有N个城市):

class ProvinceField(forms.ModelChoiceField):
    def __init__(self, query=Province.objects.all().order_by('name')):
        super(ProvinceField, self).__init__(queryset=query)

class CityField(forms.ModelChoiceField):    
    def __init__(self, query=City.objects.none()):
        super(CityField, self).__init__(queryset=query)

class CityForm(forms.Form)):
    province = ProvinceField()
    city = CityField()

    def __init__(self, *args, **kwargs):
        super(CityForm, self).__init__(*args, **kwargs)            
        if kwargs and kwargs.get('data').get('province'):
            try:
                province = Province.objects.get(pk=kwargs.get('data').get('province'))
            except ObjectDoesNotExist:
                self.fields['city'].queryset = City.objects.none()
            else:
                self.fields['city'].queryset = province.cities.all().order_by('name')
        else:
            self.fields['city'].queryset = City.objects.none()

希望这段代码对您有所帮助!