2016-09-23 2 views
0

사용자가 국가를 선택할 수있게하고 싶습니다. 나는 2 개의 모델 = Countries과 약간의 수치와 CountriesTranslations을 가지고있다. country (사용자가이 모델에 FK를 가지고 있기 때문에)과 튜플을 만들려고 노력 중입니다. translation. 프런트 엔드에서 나는 국가의 드롭 다운 목록을 볼 수 있지만 내가 양식을 저장하려고 할 때, 나는 오류를 참조하십시오 Exception Value: Cannot assign "'AF'": "UserProfile.country" must be a "Countries" instance. 오류 라인 if user_profile_form.is_valid():두 모델을 기반으로 선택 항목으로 양식을 저장하려고하면 '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, eng_name.common_name),)에서 country가 변환됩니다에서 발생 str. 양식에 country 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',) 

하지만이 솔루션은 너무 느리게 너무 많은 label_from_instance에 있기 때문에 쿼리의 쿼리와 페이지가로드를 생성합니다 아래 그림과 같이 가능한 솔루션으로는 최우선 label_from_instanceModelChoiceField을 사용하는 것입니다. 어떤 조언을 부탁드립니다.

답변

0

해결 될 것으로 보입니다.

아래 버전은 위 EDITED7 queries in 29.45ms73 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 원칙이 적용되므로 다른 형태로 여러 번 선택 사항을 사용할 필요가 있다면 아무런 문제가 없습니다.

0

드롭 다운 목록의 forms.ChoiceField 대신 forms.ModelChoiceField을 사용하고 싶을 것입니다.

ModelChoiceField는 QuerySet을 기반으로 빌드되며 모델 인스턴스를 보존합니다.

+0

문제는 두 모델의 데이터를 결합해야한다는 것입니다. 국가 모델에서 'country' 인스턴스를 가져와'CountriesTranslations '모델에서이 모델의'번역 '을 가져와야합니다 (이 것은 사용자에게 표시됨). 만약 내가 올바르게 이해한다면,'ModelChoiceField'는 내 나라의 번역 된 이름을 제공 할 수 없으며'country' 인스턴스 자체 만 제공 할 수 있습니다. – TitanFighter

+0

선택적 인자'to_field_name'은 표시 이름을 사용자 정의 할 수있는 것처럼 보입니다. 그 기능이 얼마나 유연한 지 확신 할 수는 없지만 설정을 시도해 볼만한 가치가 있다고 생각합니다. 그리고 HTML 템플릿 언어의 구문에있는 한 동적으로 계산 된 값을 입력 할 수 있다고 생각합니다. –

+0

방금 ​​시도했습니다. 냄새 맡지 마. 'to_field_name'은, 내가 이해할 수있는 것처럼, 단지'html 옵션 값'을 변경합니다 - https://docs.djangoproject.com/en/1.10/ref/forms/fields/#django.forms.ModelChoiceField.to_field_name – TitanFighter

관련 문제