避免在django中更新模型窗体的实例化 [英] Avoiding fresh instantiation of modelform fields in django

查看:182
本文介绍了避免在django中更新模型窗体的实例化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有自定义模型框的模型。

$ b

  class UserModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self,obj):
return obj .get_full_name()

class TaskForm(forms.ModelForm):
originator = UserModelChoiceField(queryset = User.objects.all()。order_by('first_name'),widget = forms.Select (attrs = {'class':'form-control'}))

class Meta:
model = Task
fields = ['originator']

我的阅读建议以这种方式处理我的字段是覆盖模型中的任何信息(例如是否需要)因为它是从我的定义中实例化的,而不是增加模型的字段。



似乎最小化我这样改变的字段我应该与init交互(见下面)

  def __init __(self,* args,** kwargs):
super(TaskForm,self) __init __(* args,** kwargs)

sel f.fields ['originator']。queryset = User.objects.all()。order_by('first_name')
self.fields ['originator']。widget = forms.Select(attrs = {'class' :'form-control'})

我了解如何做查询和窗口部件,假设上述是正确的。我的问题是如何使用该自定义选择字段。此外,我甚至不知道这是做什么的方式,因为它似乎有点黑客。

解决方案

我记得有这个问题,没有找到一个内置或惯用的解决方案。我没有想出比这个装饰器更好的解决方案(是的,这是生产代码):

  def model_generated(model):
从模型字段生成表单字段

目的:
这个装饰器可以让您在形式字段属性如
`max_length`从模型字段复制到DRY

用法:
使用此装饰器装饰一个表单类,并将MODEL_GENERATED_FIELDS
设置为要生成的属性名称列表

限制:
- 目前,这个装饰器只支持CharFields


def decorator(cls):

在cls.MODEL_GENERATED_FIELDS中的field_name:
model_field = model._meta.get_field(field_name)

model_field_type = type(model_field)
如果model_field_type == django_models.CharField:
form_field_type = forms.CharField
attributes = {#(form_attribute,model_attribute,processor)
('label',' verbose_name',None),
('max_length',None,None),
('min_length',None,None),
('required','blank'
}
else:
#(也许有一天这个装饰器会支持更多类型的字段)
raise ValueError(Unknown type of model field:{} formform(model_field_type))

kwargs = {}
for form_attribute,model_attribute,属性中的处理器:
如果model_attribute为无:
model_attribute = form_attribute
如果处理器为无:
处理器= lambda值:值

如果hasattr(model_field,model_attribute):
kwargs [form_attribute] = processor(getattr(model_field,model_attribute))

form_field = form_field_type(** kwargs)
setattr(cls,field_name,form_field)

#注册字段,因为我们是猴子修补
#(Django的meta-class hackery来检测字段
cls.base_fields [field_name] = form_field

return cls

返回装饰器

所以例如我会有:

  class Team(models.Model):
name = models.CharField(max_length = 30,unique = True,
verbose_name =Team Name)
passphrase = models.CharField max_length = 30,
verbose_name =密码)
...

和:

  @model_generated(models.Team)
class TeamJoiningForm(forms.Form):
MODEL_GENERATED_FIELDS =('name','passphrase')
...

您可以根据自己的具体需求调整并扩展 model_generated 装饰器。对不起。


I have a modelform with a custom modelchoicefield.

class UserModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
         return obj.get_full_name()

class TaskForm(forms.ModelForm):
    originator = UserModelChoiceField(queryset=User.objects.all().order_by('first_name'),widget=forms.Select(attrs={'class':'form-control'}))

    class Meta:
        model = Task
        fields = ['originator']

My reading suggests that handling my field this way is overriding any info from the model (for example whether or not it is required) because it's instantiating it from my definition rather than augmenting the model's field.

It seems that to minimise my alteration of fields like this I should be interacting with the init instead (see below)

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

    self.fields['originator'].queryset=User.objects.all().order_by('first_name')
    self.fields['originator'].widget = forms.Select(attrs={'class':'form-control'})

I understand how to do the queryset and widget assuming that the above is correct. My question is how do I use that custom choicefield. Moreover I'm not even sure if this is the way to do it as it seems a little hacky.

解决方案

Ugh.. I remember having this problem and not finding a built-in or idiomatic solution. I didn’t come up with a better solution than this decorator (and yes, this is production code):

def model_generated(model):
    """Generate form fields from model fields

    Purpose:
        This decorator lets you have form field attributes like
        `max_length` copied over from a model field while being DRY.

    Usage:
        Decorate a form class with this decorator and set MODEL_GENERATED_FIELDS
        to a list of attribute names you would like to be generated.

    Limitations:
        - Currently, this decorator only supports CharFields.
    """

    def decorator(cls):

        for field_name in cls.MODEL_GENERATED_FIELDS:
            model_field = model._meta.get_field(field_name)

            model_field_type = type(model_field)
            if model_field_type == django_models.CharField:
                form_field_type = forms.CharField
                attributes = {  # (form_attribute, model_attribute, processor)
                    ('label', 'verbose_name', None),
                    ('max_length', None, None),
                    ('min_length', None, None),
                    ('required', 'blank', lambda value: not value),
                }
            else:
                # (Maybe one day this decorator will support more types of fields.)
                raise ValueError("Unknown type of model field: {}".format(model_field_type))

            kwargs = {}
            for form_attribute, model_attribute, processor in attributes:
                if model_attribute is None:
                    model_attribute = form_attribute
                if processor is None:
                    processor = lambda value: value

                if hasattr(model_field, model_attribute):
                    kwargs[form_attribute] = processor(getattr(model_field, model_attribute))

            form_field = form_field_type(**kwargs)
            setattr(cls, field_name, form_field)

            # Register field since we're monkey-patching
            # (Django's meta-class hackery to detect fields only runs the first time the class is declared.)
            cls.base_fields[field_name] = form_field

        return cls

    return decorator

So for example I would have:

 class Team(models.Model):
    name = models.CharField(max_length=30, unique=True,
                            verbose_name="Team Name")
    passphrase = models.CharField(max_length=30,
                                  verbose_name="Passphrase")
    ...

and:

@model_generated(models.Team)
class TeamJoiningForm(forms.Form):
    MODEL_GENERATED_FIELDS = ('name', 'passphrase')
    ...

You might adapt and extend the model_generated decorator for your own specific needs. Sorry.

这篇关于避免在django中更新模型窗体的实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆