Вход в свободную форму для поля ForeignKey в Django ModelForm
У меня есть две модели, связанные с внешним ключом:
# models.py
class TestSource(models.Model):
name = models.CharField(max_length=100)
class TestModel(models.Model):
name = models.CharField(max_length=100)
attribution = models.ForeignKey(TestSource, null=True)
По умолчанию django ModelForm представляет это как <select>
с <option>
s; однако я бы предпочел, чтобы эта функция как вход свободной формы, <input type="text"/>
и за кулисами, создала или создала необходимый объект TestSource, а затем связала его с объектом TestModel.
Я попытался определить пользовательский ModelForm и Field, чтобы выполнить это:
# forms.py
class TestField(forms.TextInput):
def to_python(self, value):
return TestSource.objects.get_or_create(name=value)
class TestForm(ModelForm):
class Meta:
model=TestModel
widgets = {
'attribution' : TestField(attrs={'maxlength':'100'}),
}
К сожалению, я получаю: invalid literal for int() with base 10: 'test3'
при попытке проверить is_valid
на представленной форме. Где я иду не так? Их и более простой способ сделать это?
Ответы
Ответ 1
Что-то вроде этого должно работать:
class TestForm(ModelForm):
attribution = forms.CharField(max_length=100)
def save(self, commit=True):
attribution_name = self.cleaned_data['attribution']
attribution = TestSource.objects.get_or_create(name=attribution_name)[0] # returns (instance, <created?-boolean>)
self.instance.attribution = attribution
return super(TestForm, self).save(commit)
class Meta:
model=TestModel
exclude = ('attribution')
Ответ 2
Здесь есть несколько проблем.
Во-первых, вы определили поле, а не виджет, поэтому вы не можете использовать его в словаре widgets
. Вам необходимо переопределить объявление поля на верхнем уровне формы.
Во-вторых, get_or_create
возвращает два значения: извлеченный или созданный объект и логическое значение, чтобы показать, было ли оно создано. Вы действительно хотите вернуть первое из этих значений из вашего метода to_python
.
Я не уверен, что любой из них вызвал вашу фактическую ошибку. Вы должны опубликовать фактическую трассировку для нас, чтобы быть уверенным.
Ответ 3
TestForm.attribution ожидает ключ int value для модели TestSource.
Возможно, эта версия модели будет более удобной для вас:
class TestSource(models.Model):
name = models.CharField(max_length=100, primary_key=True)
Ответ 4
Взято из:
Как сделать редактируемое поле внешнего ключа модели формы в шаблоне Django?
class CompanyForm(forms.ModelForm):
s_address = forms.CharField(label='Address', max_length=500, required=False)
def __init__(self, *args, **kwargs):
super(CompanyForm, self).__init__(*args, **kwargs)
try:
self.fields['s_address'].initial = self.instance.address.address1
except ObjectDoesNotExist:
self.fields['s_address'].initial = 'looks like no instance was passed in'
def save(self, commit=True):
model = super(CompanyForm, self).save(commit=False)
saddr = self.cleaned_data['s_address']
if saddr:
if model.address:
model.address.address1 = saddr
model.address.save()
else:
model.address = Address.objects.create(address1=saddr)
# or you can try to look for appropriate address in Address table first
# try:
# model.address = Address.objects.get(address1=saddr)
# except Address.DoesNotExist:
# model.address = Address.objects.create(address1=saddr)
if commit:
model.save()
return model
class Meta:
exclude = ('address',) # exclude form own address field
Эта версия устанавливает начальные данные поля s_address как FK от self, во время init - я добавил попытку, за исключением исключения ошибки ObjectDoesNotExist, чтобы она работала с данными или без передачи данных в форму.
Хотя мне бы хотелось узнать, есть ли в Django более простой встроенный метод переопределения.