避免在django中更新模型窗体的实例化 [英] Avoiding fresh instantiation of modelform fields in 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屋!