动态更新ModelForm的元类

时间:2021-05-22 19:17:34

I am hoping to dynamically update a ModelForm's inline Meta class from my view. Although this code seems to update the exclude list in the Meta class, the output from as_p(), as_ul(), etc does not reflect the updated Meta exclude.

我希望从我的视图中动态更新一个ModelForm的内联元类。虽然这段代码似乎更新了元类中的排除列表,但是as_p()、as_ul()等的输出并不反映更新后的元排除。

I assume then that the html is generated when the ModelForm is created not when the as_*() is called. Is there a way to force the update of the HTML?

我假设html是在创建ModelForm时生成的,而不是在调用as_*()时生成的。是否有办法强制更新HTML?

Is this even the best way to do it? I just assumed this should work.

这是最好的方法吗?我只是认为这个应该有效。

Thoughts?

想法吗?

from django.forms import ModelForm

from testprogram.online_bookings.models import Passenger

class PassengerInfoForm(ModelForm):

    def set_form_excludes(self, exclude_list):
        self.Meta.exclude = excludes_list

    class Meta:
        model = Passenger
        exclude = []

4 个解决方案

#1


55  

The Meta class is used to dynamically construct the form definition - so by the time you've created the ModelForm instance, the fields not in the exclude have already been added as the new object's attributes.

元类用于动态地构造表单定义——因此,当您创建了ModelForm实例时,不包含在exclude中的字段已经被添加为新对象的属性。

The normal way to do it would be to just have multiple class definitions for each possible exclude list. But if you want the form itself to be dynamic, you'll have to create a class definition on the fly. Something like:

通常的做法是为每个可能的排除列表提供多个类定义。但是如果希望表单本身是动态的,就必须动态地创建类定义。喜欢的东西:

def get_form(exclude_list):
    class MyForm(ModelForm):
        class Meta:
            model = Passenger
            exclude = exclude_list
    return MyForm

form_class = get_form(('field1', 'field2'))
form = form_class()

UPDATE: I just revisited this post and thought I'd post a little more idiomatic way to handle a dynamic class:

更新:我刚刚重新浏览了这篇文章,我想我应该用一些更习惯的方式来处理动态类:

def PassengerForm(exclude_list, *args, **kwargs):
    class MyPassengerForm(ModelForm):
        class Meta:
            model = Passenger
            exclude = exclude_list

        def __init__(self):
            super(MyPassengerForm, self).__init__(*args, **kwargs)

    return MyPassengerForm()

form = PassengerForm(('field1', 'field2'))

#2


13  

Another way:

另一种方法:

class PassengerInfoForm(ModelForm):
    def __init__(self, *args, **kwargs):
        exclude_list=kwargs.pop('exclude_list', '')

        super(PassengerInfoForm, self).__init__(*args, **kwargs)

        for field in exclude_list:
            del self.fields[field]

    class Meta:
        model = Passenger

form = PassengerInfoForm(exclude_list=['field1', 'field2'])

#3


3  

Similar approach, somewhat different goal (generic ModelForm for arbitrary models):

相似的方法,不同的目标(任意模型的通用模型形式):

from django.contrib.admin.widgets import AdminDateWidget
from django.forms import ModelForm
from django.db import models

def ModelFormFactory(some_model, *args, **kwargs):
    """
    Create a ModelForm for some_model
    """
    widdict = {}
    # set some widgets for special fields
    for field in some_model._meta.local_fields:
        if type(field) is models.DateField:
            widdict[field.name] = AdminDateWidget()

    class MyModelForm(ModelForm): # I use my personal BaseModelForm as parent
        class Meta:
            model = some_model
            widgets = widdict

    return MyModelForm(*args, **kwargs)

#4


2  

Use modelform_factory (doc):

使用modelform_factory(doc):

from django.forms.models import modelform_factory

from testprogram.online_bookings.models import Passenger

exclude = ('field1', 'field2')
CustomForm = modelform_factory(model=Passenger, exclude=exclude)  # generates ModelForm dynamically
custom_form = CustomForm(data=request.POST, ...)  # form instance

#1


55  

The Meta class is used to dynamically construct the form definition - so by the time you've created the ModelForm instance, the fields not in the exclude have already been added as the new object's attributes.

元类用于动态地构造表单定义——因此,当您创建了ModelForm实例时,不包含在exclude中的字段已经被添加为新对象的属性。

The normal way to do it would be to just have multiple class definitions for each possible exclude list. But if you want the form itself to be dynamic, you'll have to create a class definition on the fly. Something like:

通常的做法是为每个可能的排除列表提供多个类定义。但是如果希望表单本身是动态的,就必须动态地创建类定义。喜欢的东西:

def get_form(exclude_list):
    class MyForm(ModelForm):
        class Meta:
            model = Passenger
            exclude = exclude_list
    return MyForm

form_class = get_form(('field1', 'field2'))
form = form_class()

UPDATE: I just revisited this post and thought I'd post a little more idiomatic way to handle a dynamic class:

更新:我刚刚重新浏览了这篇文章,我想我应该用一些更习惯的方式来处理动态类:

def PassengerForm(exclude_list, *args, **kwargs):
    class MyPassengerForm(ModelForm):
        class Meta:
            model = Passenger
            exclude = exclude_list

        def __init__(self):
            super(MyPassengerForm, self).__init__(*args, **kwargs)

    return MyPassengerForm()

form = PassengerForm(('field1', 'field2'))

#2


13  

Another way:

另一种方法:

class PassengerInfoForm(ModelForm):
    def __init__(self, *args, **kwargs):
        exclude_list=kwargs.pop('exclude_list', '')

        super(PassengerInfoForm, self).__init__(*args, **kwargs)

        for field in exclude_list:
            del self.fields[field]

    class Meta:
        model = Passenger

form = PassengerInfoForm(exclude_list=['field1', 'field2'])

#3


3  

Similar approach, somewhat different goal (generic ModelForm for arbitrary models):

相似的方法,不同的目标(任意模型的通用模型形式):

from django.contrib.admin.widgets import AdminDateWidget
from django.forms import ModelForm
from django.db import models

def ModelFormFactory(some_model, *args, **kwargs):
    """
    Create a ModelForm for some_model
    """
    widdict = {}
    # set some widgets for special fields
    for field in some_model._meta.local_fields:
        if type(field) is models.DateField:
            widdict[field.name] = AdminDateWidget()

    class MyModelForm(ModelForm): # I use my personal BaseModelForm as parent
        class Meta:
            model = some_model
            widgets = widdict

    return MyModelForm(*args, **kwargs)

#4


2  

Use modelform_factory (doc):

使用modelform_factory(doc):

from django.forms.models import modelform_factory

from testprogram.online_bookings.models import Passenger

exclude = ('field1', 'field2')
CustomForm = modelform_factory(model=Passenger, exclude=exclude)  # generates ModelForm dynamically
custom_form = CustomForm(data=request.POST, ...)  # form instance