首页 文章

Django ModelForm上的ForeignKey字段的自由格式输入

提问于
浏览
11

我有两个与外键相关的模型:

# 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会将其显示为带有 <option><select> ;但我更喜欢这个函数作为自由格式输入, <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'}),
    }

不幸的是,我在尝试检查提交的表单上的 is_valid 时收到了 invalid literal for int() with base 10: 'test3' . 我哪里错了?是他们更容易实现这一目标的方法吗?

4 回答

  • 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')
    
  • 0

    这里有一些问题 .

    首先,您已经定义了一个字段,而不是一个小部件,因此您无法在 widgets 字典中使用它 . 您需要覆盖表单顶层的字段声明 .

    其次 get_or_create 返回两个值:检索或创建的对象,以及显示是否创建的布尔值 . 您真的只想从 to_python 方法返回第一个值 .

    我不确定其中任何一个是否会导致您的实际错误 . 您需要为我们发布实际回溯以确定 .

  • 11

    TestForm.attribution需要int值 - TestSource模型的关键 .

    也许这个版本的模型对您来说会更方便:

    class TestSource(models.Model):
        name = models.CharField(max_length=100, primary_key=True)
    
  • 0

    取自:

    How to make a modelform editable foreign key field in a django template?

    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
    

    此版本在 init 期间将s_address字段的初始数据设置为来自self的FK,这样,如果您将实例传递给表单,它将在您的字段中加载FK - 我添加了一个尝试,除了避免ObjectDoesNotExist错误,以便在有或没有数据传递给表单的情况下工作 .

    虽然,我很想知道是否有更简单的内置Django覆盖 .

相关问题