Как я получаю, чтобы мой Address ModelForm не вызывал исключение, когда поле оставлено пустым? - PullRequest
2 голосов
/ 21 февраля 2011

Вот форма:

class AddressForm(ModelForm):
    postal_code = CharField()
    city = CharField()
    province = CharField()
    country = CharField()

    def clean_postal_code(self):
        postal_code = self.cleaned_data['postal_code'].strip().upper()
        if not re.match(r'^[A-Z][0-9][A-Z]\s?[0-9][A-Z][0-9]$', postal_code):
            raise ValidationError('Invalid postal code.')
        return ' '.join((postal_code[:3], postal_code[-3:]))

    def clean(self):
        data = self.cleaned_data

        if 'country' in data:
            data['country'] = Country.objects.get_or_create(name=data['country'])[0]
            if 'province' in data:
                data['province'] = Province.objects.get_or_create(name=data['province'], country=data['country'])[0]
                if 'city' in data:
                    data['city'] = City.objects.get_or_create(name=data['city'], province=data['province'])[0]
                    if 'postal_code' in data:
                        data['postal_code'] = PostalCode.objects.get_or_create(code=data['postal_code'], city=data['city'])[0]

        return data

    class Meta:
        model = Address

Все нормализовано в отдельные таблицы, но я не хочу использовать поля <select>, потому что могут быть сотни тысяч записей. Поэтому вместо этого я использую CharFields, а затем использую автозаполнение.

Проблема в том, что если что-то вроде поля country оставить пустым, то весь этот фрагмент кода внутри clean() никогда не будет запущен, а затем поля никогда не преобразуются в соответствующие им типы. Таким образом, я получаю исключение, подобное этому:

Невозможно назначить «u'A1B 2C3»: «Address.postal_code» должен быть экземпляром «PostalCode».

Я надеялся, что, поскольку процесс clean() технически не прошел (страна является обязательным полем), он не проверит это. Теперь я не знаю, как этого избежать.

Предложения

1 Ответ

1 голос
/ 21 февраля 2011

Вау!Это удивительно сложная проблема.Я, должно быть, ударил 10 дорожных блоков.

если вам не нужна уникальная совместная проверка (чего вы в любом случае не делаете в форме модели): я думаю, что самое простое решение - исключить поля модели, переопределить save и вручную применить очищенные данные к self.instance.Позвольте регулярной проверке формы сделать уборку.

skip_model_validation_fields =  \
    ('country','province','city','postal_code')

def save(self, *args, **kwargs):
    for field in self.skip_model_validation_fields:
        setattr(self.instance, field, self.cleaned_data.get(field))

    return super(MyForm, self).save(*args, **kwargs)

class Meta:
    model = Address
    exclude = self.skip_model_validation_fields

Если вам нужен unique_together, вы можете вручную запустить self.unique_together(), но в этот момент вам также потребуется зафиксировать ошибку, повторно отобразить форму и т. Д. *

Так что, возможно,новое предложение состоит в том, чтобы использовать формы вместо хакерского обращения с ModelForm.Похоже, помощь больше не является помощью!


Проблема в том, что даже если вы поднимаете forms.ValidationError внутри clean(), создается экземпляр, и поля из cleaned_data устанавливаются на этот экземпляр независимо от того,,

Перемещение валидации в поле будет работать, но будет проблемой, потому что ваша валидация имеет иерархию, и вам нужно будет определить иерархию в каждом чистом методе полей.

Индекс почты зависит от города, город зависит от провинции ... и т. Д., Иначе вы рискуете столкнуться с той же ошибкой

Вы можете удалить элементы непосредственно из cleaned_data в вашей функции clean(), но это тожебыть королевской болью по той же причине, что и выше.

Сегодня я узнал тонну о ModelForms ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...