Django自定义更新模型表单 - 显示相关模型的选定字段而不是外键ID。

时间:2012-06-20 00:44:51

标签: django django-models django-forms django-views

我的问题是:有没有办法创建自定义模型表单,它将使用相关模型中的指定字段而不是相关模型的ID?

澄清一下,如果我有以下两个模型:

class ModelOne(models.Model):
  id = models.AutoField(primary_key = True)
  name = models.CharField(unique = True, blank = False, null = False)

class ModelTwo(models.Model):
  id = models.AutoField(primary_key = True)
  parent = models.ForeignKey(ModelOne, blank = False, null = False)
  attribute_1 = models.CharField(blank = False, null = False)
  attribute_2 = models.IntegerField(blank = False, null = False)

现在,如果我使用ModelForm在ModelTwo上创建UpdateView,那么父字段将预先填充ModelOne中的相应ID。但是我希望它显示ModelOne的name属性,然后在表单提交上将唯一名称(ModelOne)解析为相应的ModelOne实例。我想这样做的原因是,我认为从用户的角度来看,处理ModelOne的名称(更新ModelTwo实例时)而不是它的&#34; id&#34;会更直观。< / p>

关于我如何做到这一点的任何建议?

2 个答案:

答案 0 :(得分:1)

首先,尝试在ModelOne上定义 unicode 方法。它可能不适用于解决方案,但它值得拥有 - 它将以表单选择小部件来驱动文本值...

def __unicode__(self):
    '''Warning: be careful not to use related objects here,
    could cause unwanted DB hits when debugging/logging
    '''
    return self.name

如果这还不够,那么这样的事情可能有用(它改编自我所拥有的表格,更新了附在个人资料中的用户名)......

class M2Form(forms.ModelForm):
    m1_name = forms.CharField()

    class Meta:
        model = ModelTwo

    def save(self, *args, **kw):
        # Update your name field here, something like
        if self.cleaned_data.get('m1_name'):
            self.instance.parent = ModelOne.objects.get(name=self.cleaned_data.get('m1_name'))
            return super(M2Form, self).save(*args, **kw)

这是未经测试的,您可能需要对其进行调整以验证名称是否存在,并确保原始父字段未显示在表单上。运气好的话,第一个答案涵盖了我认为你的问题。

答案 1 :(得分:0)

使用Rog的答案作为起点并深入研究Django的一些内部结构,我最终找到了一个有效的解决方案。鉴于我的Django知识水平,我想有一个更好的方法来做到这一点;所以,如果您有其他方法,请添加它。

基于以上两个模型,我创建了以下表单类:

class CustomForm(forms.ModelForm):
    parent = models.CharField(label='Name')

    class Meta:
       model = ModelTwo
       exclude = ['parent']

    def __init__(self,*args,**kwargs):
       # The line of code below is the one that I was looking for. It pre-populates 
       # the "parent" field of the form with the "name" attribute of the related 
       # ModelOne instance.
       kwargs['initial']['parent'] = kwargs['instance'].parent.name
       super(CustomForm,self).__init__(*args,**kwargs)
       # The next line is for convenience and orders the form fields in our desired 
       # order. I got this tip from: 
       # http://stackoverflow.com/questions/913589/django-forms-inheritance-and-order-of-form-fields
       self.fields.keyOrder = ['parent','attribute_1','attribute_2']

    def save(self, *args, **kwargs):
       if self.cleaned_data.get('parent'):
       # This section of code is important because we need to convert back from the 
       # unique 'name' attribute of ModelOne to the corresponding instance so that
       # ModelTwo can be saved. Thanks goes to Rog for this section of code.
           self.instance.parent = ModelOne.objects.get(name=self.cleaned_data.get('parent'))
           return super(CustomForm, self).save(*args, **kwargs)