python---django中form组件(2)自定制属性以及表单的各种验证,以及数据源的实时更新,以及和数据库关联使用ModelForm和元类
自定义属性以及各种验证
分析widget:
class TestForm(forms.Form):
user = fields.CharField(
required = True,
widget = widgets.TextInput()
)
追踪widgets.py
__all__ = (
'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'NumberInput',
'EmailInput', 'URLInput', 'PasswordInput', 'HiddenInput',
'MultipleHiddenInput', 'FileInput', 'ClearableFileInput', 'Textarea',
'DateInput', 'DateTimeInput', 'TimeInput', 'CheckboxInput', 'Select',
'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
'SplitHiddenDateTimeWidget', 'SelectDateWidget',
)
全部控件
class TextInput(Input):
input_type = 'text'
template_name = 'django/forms/widgets/text.html' #模板html存放路径
追踪父类:
class Input(Widget):
"""
Base class for all <input> widgets.
"""
input_type = None # Subclasses must define this.
template_name = 'django/forms/widgets/input.html' def __init__(self, attrs=None): #发现这里又参数attrs可以设置属性
if attrs is not None:
attrs = attrs.copy()
self.input_type = attrs.pop('type', self.input_type)
super(Input, self).__init__(attrs) def get_context(self, name, value, attrs):
context = super(Input, self).get_context(name, value, attrs)
context['widget']['type'] = self.input_type
return context
发现模板文件template_name = 'django/forms/widgets/input.html',实际上并不存在,是调用了父类方法
class Widget(six.with_metaclass(RenameWidgetMethods)):
def get_context(self, name, value, attrs):
context = {}
context['widget'] = {
'name': name,
'is_hidden': self.is_hidden,
'required': self.is_required,
'value': self.format_value(value),
'attrs': self.build_attrs(self.attrs, attrs),
'template_name': self.template_name,
}
return context def render(self, name, value, attrs=None, renderer=None):
"""
Returns this Widget rendered as HTML, as a Unicode string. #生成对于html代码,返回使用
"""
context = self.get_context(name, value, attrs)
return self._render(self.template_name, context, renderer) def _render(self, template_name, context, renderer=None):
if renderer is None:
renderer = get_default_renderer()
return mark_safe(renderer.render(template_name, context))
所以我们可以自定制样式,属性
widget = widgets.TextInput(attrs={"class":"c1"}),#这个属性,在前端进行设置就可以生成想要的样式,widgets代表显示的字段对象
补充:在服务端生成原生字符串,不需要前端渲染时进行转义
txt = "<input type='text' />" #默认发送到模板页面是无法显示需要进行处理 #在views中处理:
fromdjango.utils.safestring import make_safe
txt = mark_safe(txt)
#前端可以正常显示
select单选框:
sel_inp=fields.ChoiceField(
choices = [(,'a'),(,'b'),]
)
select框:
sel_inp = fields.CharField(
widget = widgets.Select(choices=[(1,'a'),(,'b'),])
)
combo多选:
radio_inp=fields.MultipleChoiceField(
choices = [(,'a'),(,'b'),] #含有multiple时可以写在外,也可以写在内,这里推荐在外
widget = widgets.SelectMultiple(attrs={'class':"c1"})
)
单选CheckBox:
chk_inp = fields.CharField(
widget = widgets.CheckboxInput()
)
多选CheckBox
mchk_inp = fields.MultipleChoiceField(
widget = widgets.CheckboxSelectMultiple(),
choices=[(, "d"), (, "e"),(,'r') ],
initial = [,]
)
radio单选:
rad_inp = fields.ChoiceField(
choices=[(,"男"),(,"女"),],
initial=,
widget =widgets.RadioSelect(),
)
字段用于保存正则表达式,Html插件用于生产HTML标签(input)
补充:为所有的字段控件设置属性(在__new__方法中获取base_fields,其中包含有字典数据{'字段名':字段对象,....})
from django.forms import ModelForm
from repository import models class CustomerForm(ModelForm):
class Meta:
model = models.CustumerInfo #将表与元类中的数据关联
fields = "__all__" def __new__(cls, *args, **kwargs):
print(cls.base_fields)
#OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)])
#这张表中的所有字段对象
for field_name,field_obj in dict(cls.base_fields).items():
field_obj.widget.attrs.update({'class':"form-control"}) #根据字段对象修改属性 return ModelForm.__new__(cls)
数据的实时更新:和数据库关联
关联方法一
数据库:
class User(models.Model):
username = models.CharField(max_length=)
email = models.CharField(max_length=)
form组件:
from app02.models import User
class InfoForm(forms.Form):
def __init__(self,*args,**kwargs): #保证了每次获取form时,都会更新数据源
super(InfoForm,self).__init__(*args,**kwargs)
self.fields['user'].widget.choices = User.objects.all().values_list('id', 'username') self.fields['email'].widget.choices = User.objects.all().values_list('id','email') email = fields.IntegerField(
widget= widgets.Select(choices=User.objects.all().values_list('id','email'))
) user = fields.IntegerField(
widget=widgets.Select(choices=User.objects.all().values_list('id', 'username'))
)
views:
from app02.forms import UserForm,InfoForm def users(req):
obj = InfoForm()
i1 = User.objects.all().values_list('id', 'username') #列表内部是元组
i2 = User.objects.all().values('id', 'username') #列表,内部是字典
print(i1,type(i1))
print(i2,type(i2))
return render(req,"users.html",{"obj":obj})
前端显示正常:
<p>{{ obj.user }}</p>
<p>{{ obj.email }}</p>
关联方法二
另一种实时更新的方法:使用ModelChoiceField
from app02.models import User
from django.forms import ModelChoiceField class InfoForm(forms.Form):
def __init__(self,*args,**kwargs):
super(InfoForm,self).__init__(*args,**kwargs)
self.fields['user'].widget.choices = User.objects.all().values_list('id', 'username') self.fields['email'].widget.choices = User.objects.all().values_list('id','email') email = fields.IntegerField(
widget= widgets.Select(choices=User.objects.all().values_list('id','email'))
) user_id = ModelChoiceField(
queryset=User.objects.all(),
to_field_name='id'
)
前端:
<p>{{ obj.user_id }}</p>
默认输出:
<option value="" selected>---------</option>
<option value="">User object</option>
<option value="">User object</option>
<option value="">User object</option>
<option value="">User object</option>
其中 User object 不是我们想要的,这依赖于models中的__str__方法
class User(models.Model):
username = models.CharField(max_length=)
email = models.CharField(max_length=) def __str__(self):
return self.username
这时候输出正常
但是不推荐这种方法,这与models关联太强,不利于解耦,推荐使用第一种
关联方法三(ModelForm和元类)
forms.py
from django.forms import ModelForm
from repository import models class CustomerForm(ModelForm):
class Meta:
model = models.CustumerInfo #将表与元类中的数据关联
fields = ['name','consultant','status','source'] #设置显示的字段
views.py中使用
form = forms.CustomerForm()
return render(request,"table_obj_change.html",locals())
前端使用:
{{ form }}

下面来查看ModelForm源码
class ModelFormMetaclass(DeclarativeFieldsMetaclass):
def __new__(mcs, name, bases, attrs):
base_formfield_callback = None
for b in bases:
if hasattr(b, 'Meta') and hasattr(b.Meta, 'formfield_callback'):
base_formfield_callback = b.Meta.formfield_callback
break formfield_callback = attrs.pop('formfield_callback', base_formfield_callback) new_class = super(ModelFormMetaclass, mcs).__new__(mcs, name, bases, attrs) if bases == (BaseModelForm,):
return new_class opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) # We check if a string was passed to `fields` or `exclude`,
# which is likely to be a mistake where the user typed ('foo') instead
# of ('foo',)
for opt in ['fields', 'exclude', 'localized_fields']:
value = getattr(opts, opt)
if isinstance(value, six.string_types) and value != ALL_FIELDS:
msg = ("%(model)s.Meta.%(opt)s cannot be a string. "
"Did you mean to type: ('%(value)s',)?" % {
'model': new_class.__name__,
'opt': opt,
'value': value,
})
raise TypeError(msg) if opts.model:
# If a model is defined, extract form fields from it.
if opts.fields is None and opts.exclude is None:
raise ImproperlyConfigured(
"Creating a ModelForm without either the 'fields' attribute "
"or the 'exclude' attribute is prohibited; form %s "
"needs updating." % name
) if opts.fields == ALL_FIELDS:
# Sentinel for fields_for_model to indicate "get the list of
# fields from the model"
opts.fields = None fields = fields_for_model(
opts.model, opts.fields, opts.exclude, opts.widgets,
formfield_callback, opts.localized_fields, opts.labels,
opts.help_texts, opts.error_messages, opts.field_classes,
# limit_choices_to will be applied during ModelForm.__init__().
apply_limit_choices_to=False,
) # make sure opts.fields doesn't specify an invalid field
none_model_fields = [k for k, v in six.iteritems(fields) if not v]
missing_fields = (set(none_model_fields) -
set(new_class.declared_fields.keys()))
if missing_fields:
message = 'Unknown field(s) (%s) specified for %s'
message = message % (', '.join(missing_fields),
opts.model.__name__)
raise FieldError(message)
# Override default model fields with any custom declared ones
# (plus, include all the other declared fields).
fields.update(new_class.declared_fields)
else:
fields = new_class.declared_fields new_class.base_fields = fields return new_class
ModelFormMetaclass类源码
opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) #opts代表ModelForm类中的元类,下面看看元类中可以设置那些数据
# We check if a string was passed to `fields` or `exclude`,
# which is likely to be a mistake where the user typed ('foo') instead
# of ('foo',)
for opt in ['fields', 'exclude', 'localized_fields']: #这是元类中可以使用的属性fields:可以操作的字段,exclude:排除的字段
value = getattr(opts, opt)
if isinstance(value, six.string_types) and value != ALL_FIELDS:
msg = ("%(model)s.Meta.%(opt)s cannot be a string. "
"Did you mean to type: ('%(value)s',)?" % {
'model': new_class.__name__,
'opt': opt,
'value': value,
})
raise TypeError(msg)
if opts.model: #model属性用来关联数据表
# If a model is defined, extract form fields from it.
if opts.fields is None and opts.exclude is None:
raise ImproperlyConfigured(
"Creating a ModelForm without either the 'fields' attribute "
"or the 'exclude' attribute is prohibited; form %s "
"needs updating." % name
)
if opts.fields == ALL_FIELDS:
# Sentinel for fields_for_model to indicate "get the list of
# fields from the model"
opts.fields = None
fields = fields_for_model(
opts.model, opts.fields, opts.exclude, opts.widgets,
formfield_callback, opts.localized_fields, opts.labels,
opts.help_texts, opts.error_messages, opts.field_classes,
# limit_choices_to will be applied during ModelForm.__init__().
apply_limit_choices_to=False,
)
# make sure opts.fields doesn't specify an invalid field
none_model_fields = [k for k, v in six.iteritems(fields) if not v]
missing_fields = (set(none_model_fields) -
set(new_class.declared_fields.keys()))
if missing_fields:
message = 'Unknown field(s) (%s) specified for %s'
message = message % (', '.join(missing_fields),
opts.model.__name__)
raise FieldError(message)
# Override default model fields with any custom declared ones
# (plus, include all the other declared fields).
fields.update(new_class.declared_fields)
else:
fields = new_class.declared_fields
new_class.base_fields = fields
return new_class
补充:
1.fields若是设置为__all__则是代表所有的字段都去显示
2.Form对象关联后可以使用instance属性去获取到关联的当前那条数据对象
python---django中form组件(2)自定制属性以及表单的各种验证,以及数据源的实时更新,以及和数据库关联使用ModelForm和元类的更多相关文章
- python Django之Form组件
python Django之Form组件 Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 小试 ...
- Django中Form组件的使用
Form介绍 HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入 ...
- django中form组件
Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否 ...
- Django中form组件的is_valid校验机制
先来归纳一下整个流程(1)首先is_valid()起手,看seld.errors中是否值,只要有值就是flase(2)接着分析errors.里面判断_errors是都为空,如果为空返回self.ful ...
- django框架中form组件的简单使用示例:注册验证
Django中form组件的三大特点: 1. 生成页面可使用的HTML标签 2. 对用户提交的数据进行初步校验 3. 保留上次输入内容 废话不多说,直接进入正题. 这是注册界面截图: 与上一篇a ...
- Python之路【第二十一篇】:Django之Form组件
Django之Form组件 Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 小试牛刀 1. ...
- 〖Python〗-- Django的Form组件
[Django的Form组件] Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 Form类的使 ...
- Python学习(三十九)—— Django之Form组件
一.构建一个表单 假设你想在你的网站上创建一个简单的表单,以获得用户的名字.你需要类似这样的模板: <form action="/your-name/" method=&qu ...
- python框架之Django(10)-Form组件
介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来.与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入 ...
随机推荐
- Redis介绍及Jedis基础操作
1.Redis简介 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes ...
- ReactJS实用技巧(2):从新人大坑——表单组件来看State
不太清楚有多少初学React的同学和博主当时一样,在看完React的生命周期.数据流之后觉得已经上手了,甩开文档啪啪啪的开始敲了起来.结果...居然被一个input标签给教做人了. 故事是这样的:首先 ...
- 12.18daily_scrum
本软件中的最后一个界面——“关于”界面的设计已经开始进行,数据传输的内容也差不多进行过半,最主要的任务依旧在测试过程中,我们组接下来还是要大力加强测试的强度和数量,注意边际数据和错误数据的测试处理: ...
- [2017BUAA软工助教]团队beta得分总表
一.累计得分 项目 α例会 α发布 α测试 α展示 α事后 合计 满分 50 10 10 150 10 230 hotcode5 50 10 9 150 9 228 弗朗明哥舞步 50 10 8 13 ...
- 20135337朱荟潼 Linux第五周学习总结——扒开系统调用的三层皮(下)
朱荟潼 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课http://mooc.study.163.com/course/USTC 1000029000 一.学习内容 (一 ...
- NBPL团队总结
我们团队钱多多记账软件项目从2017年12月号开始,历时两个周.这两个周,我们从头学起,学到了很多新的知识,对一些概念有了认知,关于团队协作,关于团队建设,关于Android开发.回顾前两周,我们一致 ...
- ThinkPHP框架知识(比较全的知识)
php框架 一.真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格 ...
- Day Nine
站立式会议 站立式会议内容总结 331 今天:学习plupload 遇到问题:无 明天:学习中文分词 442 今天:解决gradle以及项目计划页面的bug 遇到的问题:调用工具类以及配置gradle ...
- App 添加权限
配置好了
- linux执行jmeter脚本报错
今天做性能测试发现,报错为100% windows上面执行又是成功的,最后在linux的jmeter脚本中加了一个BeanShell PostProcessor prev.setDataEncodin ...