Ввод в произвольной форме для поля ForeignKey в Django ModelForm - PullRequest
11 голосов
/ 09 ноября 2010

У меня есть две модели, связанные внешним ключом:

# 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> с;однако я бы предпочел, чтобы эта функция в качестве ввода произвольной формы <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 в отправленной форме.Куда я иду не так?Есть ли их и более простой способ сделать это?

Ответы [ 4 ]

11 голосов
/ 09 ноября 2010

Примерно так должно работать:

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')
1 голос
/ 09 ноября 2010

Здесь есть несколько проблем.

Во-первых, вы определили поле, а не виджет, поэтому вы не можете использовать его в словаре widgets. Вам нужно переопределить объявление поля на верхнем уровне формы.

Во-вторых get_or_create возвращает два значения: объект извлечен или создан и логическое значение, чтобы показать, был ли он создан. Вы действительно просто хотите вернуть первое из этих значений из вашего to_python метода.

Я не уверен, что один из них вызвал вашу настоящую ошибку. Вы должны опубликовать фактическую трассировку для нас, чтобы быть уверенным.

0 голосов
/ 26 января 2019

Взято из:

Как сделать поле внешнего ключа редактируемой модели в шаблоне 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 от себя, во время init , таким образом, если вы передадите экземпляр в форму, он загрузит FK в вашем char-поле - я добавил попытку, за исключением того, чтобы избежать ошибки ObjectDoesNotExist, такчто он работает с данными или без данных, передаваемых в форму.

Хотя мне бы хотелось узнать, существует ли более простая встроенная переопределение Django.

0 голосов
/ 19 ноября 2015

TestForm.attribution ожидает значение int - ключ к модели TestSource.

Возможно, эта версия модели вам будет удобнее:

class TestSource(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...