显示基于另一个表数据的表单元素

时间:2011-07-07 09:03:26

标签: python django django-models django-forms

我有以下型号:

class Product(models.Model):
    active = models.BooleanField(default=True)
    name = models.CharField(max_length=40, unique=True)
    acronym = models.CharField(max_length=3, unique=True)
    bool1_name = models.CharField(max_length=60, blank=True, null=True)
    bool1_default = models.BooleanField(default=False)
    int1_name = models.CharField(max_length=60, blank=True, null=True)
    int1_default = models.IntegerField(blank=True, null=True)
    float1_name = models.CharField(max_length=60, blank=True, null=True)
    float1_default = models.FloatField(blank=True, null=True)
    date1_name = models.CharField(max_length=60, blank=True, null=True)

class ProductData(models.Model):
    created = models.DateTimeField(default=datetime.now)
    created_by = models.ForeignKey(User)
    item = models.ManyToManyField(Item)
    bool1_val = models.BooleanField(default=False)
    int1_val = models.IntegerField(blank=True, null=True)
    float1_val = models.FloatField(blank=True, null=True)
    date1_val = models.DateField(blank=True, null=True)

class Item(models.Model):
    product = models.ForeignKey(Product)
    business = models.ForeignKey(Business)

我已将以下数据输入数据库:

# pseudo code

Product(1,'Toothpaste','TP','Is the toothpaste white?',1,,,'Weight',1,)
Product(1,'Milk','MLK',,,,,'Litres',2,'Best Before')

我希望能够根据ProductData中定义的变量为Product构建表单(创建基于表单的值 - 来自另一个的值-table )。我想要这样的东西:

class ProductDataForm(ModelForm):

    def __init__(self,p,*args,**kwargs):
        super(ProductDataForm, self).__init__(*args, **kwargs)
        # if the values isn't set in the product
        if p.bool1_name is None:
            # hide the input
            self.fields['bool1_val'].widget = forms.CharField(required=False)
        else:
            # else make sure the name the field name is the product name
            self.fields['bool1_val'].widget = forms.BooleanField(label=p.bool1_name)

...

但是我在将Product的实例传递给ProductDataForm时遇到了问题。其他人说我可以使用BaseModelFormSet,但是关于此的文献是粗略的,我不确定如何应用它。

修改

如果我创建一个包含我不想在ProductDataForm的 init 中显示的所有字段的数组,我该如何将这些字段传递给Meta类的排除。像这样:

class ProductDataForm(ModelForm):

    def __init__(self,p,*args,**kwargs):
        super(ProductDataForm, self).__init__(*args, **kwargs)
        tempExclude = []
        if not p.bool1_name:
            tempExclude.append('bool1_val')
        else:
            self.fields['bool1_val'].label = p.bool1_name

        self.Meta.exclude = tempExclude

    class Meta:
        model = ProductData
        exclude = []

修改

我现在正试图将我要排除的字段存储在setting.py文件中,如下所示:

# settings.py
SUBITEM_EXCLUDE_FIELDS = ['dave']

# views.py
def new_product_data_view(request,product='0'):
    try:
        i_fpKEY = int(product)
    except ValueError:
        raise Http404()
    if not i_fpKEY:
        t_fp = Product.objects.filter(active=1).order_by('id')[0]
    else:
        t_fp = Product.objects.get(id=i_fpKEY)
    FieldsToExcludeFromProductDataForm(t_fp)
    print "views.py > PRODUCTDATA_EXCLUDE_FIELDS = "+str(PRODUCTDATA_EXCLUDE_FIELDS)
    siForm = ProductDataForm(t_fp, request.POST, auto_id='si_%s')
    return render_to_response(...)

# middleware.py
def FieldsToExcludeFromProductDataForm(tempFP):
    excludedFields = ['created','created_by','item']
    if not tempFP.bool1_name:
        excludedFields.append('bool1_val')
    if not tempFP.int1_name:
        excludedFields.append('int1_val')
    ...
    for val in excludedFields:
        PRODUCTDATA_EXCLUDE_FIELDS.append(val)
    print "middleware.py > PRODUCTDATA_EXCLUDE_FIELDS = "+str(PRODUCTDATA_EXCLUDE_FIELDS)

# forms.py
class ProductDataForm(ModelForm):

    # Only renames the fields based on whether the product has a name
    # for the field. The exclusion list is made in middleware
    def __init__(self,fp,*args,**kwargs):
        super(ProductDataForm, self).__init__(*args, **kwargs)
        if fp.bool1_name:
            self.fields['bool1_val'].label = fp.bool1_name
        if fp.int1_name:
            self.fields['int1_val'].label = fp.int1_name

    class Meta:

        def __init__(self,*args,**kwargs):
            super(Meta, self).__init__(*args, **kwargs)
            print 'Meta > __init__ > PRODUCTDATA_EXCLUDE_FIELDS = '+str(PRODUCTDATA_EXCLUDE_FIELDS)

        model = ProductData
        print 'Meta > PRODUCTDATA_EXCLUDE_FIELDS = '+str(PRODUCTDATA_EXCLUDE_FIELDS)
        #exclude = PRODUCTDATA_EXCLUDE_FIELDS

但是终端显示Meta类很早就被处理了,因此无法获得新修改的PRODUCTDATA_EXCLUDE_FIELDS

Meta > PRODUCTDATA_EXCLUDE_FIELDS = ['dave']
[11/Jul/2011 15:51:31] "GET /page/profile/1/ HTTP/1.1" 200 11410
middleware.py > PRODUCTDATA_EXCLUDE_FIELDS = ['dave', 'created', 'created_by', 'item', 'bool1_val', 'int1_val']
views.py > PRODUCTDATA_EXCLUDE_FIELDS = ['dave', 'created', 'created_by', 'item', 'bool1_val', 'int1_val']
[11/Jul/2011 15:51:32] "GET /item/new/ HTTP/1.1" 200 5028
[11/Jul/2011 15:51:32] "GET /client/1/ HTTP/1.1" 200 5445
[11/Jul/2011 15:51:32] "GET /client/view/1/ HTTP/1.1" 200 3082

2 个答案:

答案 0 :(得分:4)

为什么要使用exclude和metaclasses - 只需删除要排除的字段:

class ProductDataForm(ModelForm):
     __init__():
         ...
          for field_name in PRODUCTDATA_EXCLUDE_FIELDS:
              del self.fields[field_name]
         ...

也许这不太对,但它很简单,而且很有效。

答案 1 :(得分:2)

你的想法很明显,但后来你试图以非常迂回的方式实现它。 Python允许您使用type()函数的三个参数形式在运行时动态创建类。 使用此功能的常用方法是使用factory函数,Django以django.forms.models.modelform_factory的形式提供。 此功能未记录,但似乎是in progress。它来自与modelformset_factoryinlineformset_factory相同的函数系列,这些函数已记录在案,所以我说它可以安全使用。

签名是

modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None)

modelexcludefields等同于您通常在表单的内部Meta类中声明的内容(这就是它的实现方式)。

现在,要在您的示例中使用此功能,您需要稍微改变一下方法。首先导入工厂功能。

from django.forms.models import modelformset_factory

然后使用您的视图功能或类来:

  1. 获取产品实例。
  2. 根据产品实例生成排除字段列表(我将其称为excludedFields)。
  3. 创建表单类:

    formClass = modelform_factory(ProductData, excludes=excludedFields)

  4. 像往常一样初始化您的表单,但使用formClass而不是预定义的表单(ProductDataForm

    form = formClass()form = formClass(request.POST)等......