У меня есть ModelForm
с Django 1.11, и я перемещаю несколько полей в другую модель.Вызов make_migrations
вызывает ошибку, потому что эти поля не существуют в текущей модели.Я добавил некоторые поля в форму, но одно из полей - TranslatedField
, и поэтому в настоящее время есть 2 поля, и в будущем их может быть больше, в зависимости от количества языков.Название поля city, и в настоящее время я получаю сообщение об ошибке "Unknown field(s) (city_en, city_he) specified for SiteProfile
" (потому что я использую 2 языка - "en" и "he") - но я хочу создать все поля динамически с помощью forцикл по языкам, которые мы используем в проекте.Могу ли я переопределить (и это хороший метод программирования) метод __new__
или есть другой способ?Я предпочитаю не кодировать конкретные имена полей (city_en
и city_he
), потому что они могут измениться в будущем, в зависимости от того, сколько языков мы используем.
Вы можете увидеть мои текущие commit (не работает) на GitHub.
И текущий код этой ветки.
Хотелось бы узнать, какой метод программирования лучше всего подходит для определения динамического списка полей (все они идентичны, и будет использовано только одно из них, остальные будут удалены в методе __init__
)в ModelForm, где поля сохраняются в другой модели (есть 2 модели, но только одна форма).
Я все еще не фиксировал миграции из-за этой ошибки при запуске make_migrations.
(Я определил команду make_migrations
, которая выполняет только makemigrations
)
Форма (с моей попыткой переопределить __new__
):
class SpeedyMatchProfileBaseForm(DeleteUnneededFieldsMixin, forms.ModelForm):
user_fields = (
'diet',
'smoking_status',
'marital_status',
*(to_attribute(name='city', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
)
validators = {
'height': [speedy_match_accounts_validators.validate_height],
'diet': [speedy_match_accounts_validators.validate_diet],
'smoking_status': [speedy_match_accounts_validators.validate_smoking_status],
'marital_status': [speedy_match_accounts_validators.validate_marital_status],
**{to_attribute(name='profile_description', language_code=language_code): [speedy_match_accounts_validators.validate_profile_description] for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='city', language_code=language_code): [speedy_match_accounts_validators.validate_city] for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='children', language_code=language_code): [speedy_match_accounts_validators.validate_children] for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='more_children', language_code=language_code): [speedy_match_accounts_validators.validate_more_children] for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='match_description', language_code=language_code): [speedy_match_accounts_validators.validate_match_description] for language_code, language_name in django_settings.LANGUAGES},
'gender_to_match': [speedy_match_accounts_validators.validate_gender_to_match],
'min_age_match': [speedy_match_accounts_validators.validate_min_age_match],
'max_age_match': [speedy_match_accounts_validators.validate_max_age_match],
'diet_match': [speedy_match_accounts_validators.validate_diet_match],
'smoking_status_match': [speedy_match_accounts_validators.validate_smoking_status_match],
'marital_status_match': [speedy_match_accounts_validators.validate_marital_status_match],
}
# ~~~~ TODO: diet choices depend on the current user's gender. Also same for smoking status and marital status.
diet = forms.ChoiceField(choices=User.DIET_VALID_CHOICES, widget=forms.RadioSelect(), label=_('My diet'))
smoking_status = forms.ChoiceField(choices=User.SMOKING_STATUS_VALID_CHOICES, widget=forms.RadioSelect(), label=_('My smoking status'))
marital_status = forms.ChoiceField(choices=User.MARITAL_STATUS_VALID_CHOICES, widget=forms.RadioSelect(), label=_('My marital status'))
photo = forms.ImageField(required=False, widget=CustomPhotoWidget, label=_('Add profile picture'))
class Meta:
model = SpeedyMatchSiteProfile
fields = (
'photo',
*(to_attribute(name='profile_description', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
*(to_attribute(name='city', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
'height',
*(to_attribute(name='children', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
*(to_attribute(name='more_children', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
'diet',
'smoking_status',
'marital_status',
'gender_to_match',
*(to_attribute(name='match_description', language_code=language_code) for language_code, language_name in django_settings.LANGUAGES),
'min_age_match',
'max_age_match',
'diet_match',
'smoking_status_match',
'marital_status_match',
)
widgets = {
'smoking_status': forms.RadioSelect(),
'marital_status': forms.RadioSelect(),
**{to_attribute(name='profile_description', language_code=language_code): forms.Textarea(attrs={'rows': 3, 'cols': 25}) for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='city', language_code=language_code): forms.TextInput() for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='children', language_code=language_code): forms.TextInput() for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='more_children', language_code=language_code): forms.TextInput() for language_code, language_name in django_settings.LANGUAGES},
**{to_attribute(name='match_description', language_code=language_code): forms.Textarea(attrs={'rows': 3, 'cols': 25}) for language_code, language_name in django_settings.LANGUAGES},
'diet_match': CustomJsonWidget(choices=User.DIET_VALID_CHOICES),
'smoking_status_match': CustomJsonWidget(choices=User.SMOKING_STATUS_VALID_CHOICES),
'marital_status_match': CustomJsonWidget(choices=User.MARITAL_STATUS_VALID_CHOICES),
}
@staticmethod
def __new__(cls, *args, **kwargs):
for language_code, language_name in django_settings.LANGUAGES:
setattr(cls, to_attribute(name='city', language_code=language_code), forms.CharField(label=_('city or locality'), max_length=120))
return super().__new__(*args, **kwargs)
def __init__(self, *args, **kwargs):
self.step = kwargs.pop('step', None)
super().__init__(*args, **kwargs)
self.delete_unneeded_fields()
if ('gender_to_match' in self.fields):
self.fields['gender_to_match'] = forms.MultipleChoiceField(choices=User.GENDER_CHOICES, widget=forms.CheckboxSelectMultiple)
if ('photo' in self.fields):
self.fields['photo'].widget.attrs['user'] = self.instance.user
if ('diet' in self.fields):
update_form_field_choices(field=self.fields['diet'], choices=self.instance.user.get_diet_choices())
self.fields['diet'].initial = self.instance.user.diet
if ('smoking_status' in self.fields):
update_form_field_choices(field=self.fields['smoking_status'], choices=self.instance.user.get_smoking_status_choices())
self.fields['smoking_status'].initial = self.instance.user.smoking_status
if ('marital_status' in self.fields):
update_form_field_choices(field=self.fields['marital_status'], choices=self.instance.user.get_marital_status_choices())
self.fields['marital_status'].initial = self.instance.user.marital_status
if ('diet_match' in self.fields):
update_form_field_choices(field=self.fields['diet_match'], choices=self.instance.get_diet_match_choices())
if ('smoking_status_match' in self.fields):
update_form_field_choices(field=self.fields['smoking_status_match'], choices=self.instance.get_smoking_status_match_choices())
if ('marital_status_match' in self.fields):
update_form_field_choices(field=self.fields['marital_status_match'], choices=self.instance.get_marital_status_match_choices())
for field_name, field in self.fields.items():
if (field_name in self.validators):
field.validators.extend(self.validators[field_name])
field.required = True