当我想根据两个模型选择保存表单时,'form.is_valid()'失败

时间:2016-09-23 18:14:25

标签: django django-forms django-views django-validation

我想让用户选择他们的国家/地区。我有2个模型= Countries,其中有一些数字和CountriesTranslations。我正在尝试使用country(因为用户对此模型具有FK)及其translation来创建元组。在前端,我看到国家/地区的下拉列表,但是当我尝试保存表单时,我明白了 错误:Exception Value: Cannot assign "'AF'": "UserProfile.country" must be a "Countries" instance.if user_profile_form.is_valid():

发生错误
# admindivisions.models
class Countries(models.Model):
    osm_id = models.IntegerField(db_index=True, null=True)
    status = models.IntegerField()
    population = models.IntegerField(null=True)

    iso3166_1 = models.CharField(max_length=2, blank=True)
    iso3166_1_a2 = models.CharField(max_length=2, blank=True)
    iso3166_1_a3 = models.CharField(max_length=3, blank=True)

    class Meta:
        db_table = 'admindivisions_countries'
        verbose_name = 'Country'
        verbose_name_plural = 'Countries'

class CountriesTranslations(models.Model):
    common_name = models.CharField(max_length=81, blank=True, db_index=True)
    formal_name = models.CharField(max_length=100, blank=True)

    country = models.ForeignKey(Countries, on_delete=models.CASCADE, verbose_name='Details of Country')
    lang_group = models.ForeignKey(LanguagesGroups, on_delete=models.CASCADE, verbose_name='Language of Country',
                                   null=True)

    class Meta:
        db_table = 'admindivisions_countries_translations'
        verbose_name = 'Country Translation'
        verbose_name_plural = 'Countries Translations'

# profiles.forms
class UserProfileForm(forms.ModelForm):

    # PREPARE CHOICES
    country_choices = ()
    lang_group = Languages.objects.get(iso_code='en').group
    for country in Countries.objects.filter(status=1):
        eng_name = country.countriestranslations_set.filter(lang_group=lang_group).first()
        if eng_name:
            country_choices += ((country, eng_name.common_name),)
    country_choices = sorted(country_choices, key=lambda tup: tup[1])

    country = forms.ChoiceField(choices=country_choices, required=False)

    class Meta:
        model = UserProfile()
        fields = ('email', 'email_privacy',
                  'profile_url',
                  'first_name', 'last_name',
                  'country',)

# profiles.views
def profile_settings(request):
    if request.method == 'POST':
        user_profile_form = UserProfileForm(request.POST, instance=request.user)

        if user_profile_form.is_valid():
            user_profile_form.save()
            messages.success(request, _('Your profile was successfully updated!'))

            return redirect('settings')

        else:
            messages.error(request, _('Please correct the error below.'))

    else:
        user_profile_form = UserProfileForm(instance=request.user)

    return render(request, 'profiles/profiles_settings.html', {
        'user_profile_form': user_profile_form,
    })

据我了解,来自country的{​​{1}}已转换为((country, eng_name.common_name),)。保持str形式的正确方法是什么?或者如果我以错误的方式做,那么哪种方法是正确的?

EDITED : 可能的解决方案是使用country instance覆盖ModelChoiceField,如下所示:

label_from_instance

但是由于class CountriesChoiceField(forms.ModelChoiceField): def __init__(self, user_lang='en', *args, **kwargs): super(CountriesChoiceField, self).__init__(*args, **kwargs) self.user_lang = user_lang def label_from_instance(self, obj): return obj.countriestranslations_set.get(lang_group=self.user_lang) class UserProfileForm(forms.ModelForm): user_lang = user_lang_here country = CountriesChoiceField( queryset=Countries.objects.filter( status=1, iso3166_1__isnull=False, countriestranslations__lang_group=user_lang).order_by('countriestranslations__common_name'), widget=forms.Select(), user_lang=user_lang) class Meta: model = UserProfile() fields = ('email', 'email_privacy', 'profile_url', 'first_name', 'last_name', 'country',) 中的查询和页面加载速度太慢,此解决方案会产生太多查询。非常感谢任何建议。

2 个答案:

答案 0 :(得分:0)

您可能希望使用forms.ModelChoiceField代替您的下拉列表中的forms.ChoiceField。

ModelChoiceField基于QuerySet构建并保留模型实例。

答案 1 :(得分:0)

似乎有待解决。

以下版本在上方的 EDITED 中生成7 queries in 29.45ms vs 73 queries in 92.52ms。我认为如果为某些字段设置unique_together,可能会更快。

class CountriesChoiceField(forms.ModelChoiceField):
    def __init__(self, user_lang, *args, **kwargs):

        queryset = Countries.objects.filter(
            status=1, iso3166_1__isnull=False,
            countriestranslations__lang_group=user_lang).order_by('countriestranslations__common_name')

        super(CountriesChoiceField, self).__init__(queryset, *args, **kwargs)

        self.translations = OrderedDict()
        for country in queryset:
            name = country.countriestranslations_set.get(lang_group=user_lang).common_name
            self.translations[country] = name

    def label_from_instance(self, obj):
        return self.translations[obj]

class UserProfileForm(forms.ModelForm):
    user_lang = user_lang_here
    country = CountriesChoiceField(widget=forms.Select(), user_lang=user_lang)

    class Meta:
        model = UserProfile()
        fields = ('email', 'email_privacy',
                  'profile_url',
                  'first_name', 'last_name',
                  'country',)

因此,现在可以基于两(2)个具有良好速度的模型进行选择。此外,DRY原则也适用,因此如果需要以不同的形式多次使用选择 - 没问题。