Saturday, September 24, 2016

How to make properly choices based on two models?

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.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/b2b0e010-f549-4225-93a3-ee42249a6120%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment