How to make properly choices based on two models?

17 views
Skip to first unread message

Anton Ponomarenko

unread,
Sep 24, 2016, 7:04:55 PM9/24/16
to Django users
Hello.

I want to let users to choose their countries. I have 2 models = Countries with some figures and CountriesTranslations. I am trying to make tuple with country (because user has FK to this model) and its translation. In front-end I see dropdown list of countries, but when I try to save the form, I see error: Exception Value: Cannot assign "'AF'": "UserProfile.country" must be a "Countries" instance. Error happens at the line 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,
   
})

As I understand, country from ((country, eng_name.common_name),) is converted to str. What is the right way to keep country instance in the form? or if I am doing it in the wrong way, what way is correct?

As a possible solution is to use ModelChoiceField with overriding label_from_instance as shown below:
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',)

but in this case there are too much queries because of the label_from_instance and page loads too slowly.
Would appreciate any advice how to solve this task.
Thanks.
Reply all
Reply to author
Forward
0 new messages