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

时间:2020-12-23 19:34:55

My question is: is there a way to create custom model form that will use a specified field from a related model rather than the related model's id?

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

To clarify, if I have the following two models:

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

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)

Now if I create an UpdateView on ModelTwo using a ModelForm then the parent field will be pre-filled with the corresponding id from ModelOne. However I want it to display the name attribute of ModelOne and then on form submission parse the unique name (of ModelOne) to the corresponding instance of ModelOne. The reason I want to do it this way, is that I believe it is far more intuitive from a users perspective to deal with the name of ModelOne (when updating a ModelTwo instance) rather than its "id".

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

Any suggestions of how I can do this?

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

2 个解决方案

#1


1  

Firstly, try defining the unicode method on ModelOne. It might not apply to the solution, but it's worth having - it will drive the text values in a form Select widget...

首先,尝试在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

If that's not sufficient, something like this might work (it is adapted from a form I have that updates the user's name attached to a profile)...

如果这还不够,那么这样的东西可能会起作用(它改编自我所拥有的表格,更新附加到个人资料的用户姓名)......

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)

This is untested, and you'll likely need to adapt this to validate that the name exists and make sure the original parent field doesn't appear on the form. With any luck, the first answer covers what I think your question is.

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

#2


0  

Using Rog's answer as a starting point and delving through some of Django's internals I eventually came to a working solution. Given my level of Django knowledge, I imagine there is a better way of doing this; so if you have another method please add it.

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

So based on the above two models, I created the following form class:

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

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://*.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)

#1


1  

Firstly, try defining the unicode method on ModelOne. It might not apply to the solution, but it's worth having - it will drive the text values in a form Select widget...

首先,尝试在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

If that's not sufficient, something like this might work (it is adapted from a form I have that updates the user's name attached to a profile)...

如果这还不够,那么这样的东西可能会起作用(它改编自我所拥有的表格,更新附加到个人资料的用户姓名)......

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)

This is untested, and you'll likely need to adapt this to validate that the name exists and make sure the original parent field doesn't appear on the form. With any luck, the first answer covers what I think your question is.

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

#2


0  

Using Rog's answer as a starting point and delving through some of Django's internals I eventually came to a working solution. Given my level of Django knowledge, I imagine there is a better way of doing this; so if you have another method please add it.

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

So based on the above two models, I created the following form class:

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

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://*.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)