python自定义表单_python---django中form组件(2)自定制属性以及表单的各种验证,以及数据源的实时更新,以及和数据库关联使用ModelForm和元类...

自定义属性以及各种验证

分析widget:

classTestForm(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',

)

全部控件

classTextInput(Input):

input_type= 'text'template_name= 'django/forms/widgets/text.html' #模板html存放路径

追踪父类:

classInput(Widget):""" Base class for all widgets.""" input_type = None # Subclasses must define this.

template_name= 'django/forms/widgets/input.html'def __init__(self, attrs=None): #发现这里又参数attrs可以设置属性if attrs isnot 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_typereturn context

发现模板文件template_name = 'django/forms/widgets/input.html',实际上并不存在,是调用了父类方法

classWidget(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,

}returncontext

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)returnself._render(self.template_name, context, renderer)

def _render(self, template_name, context, renderer=None):if renderer isNone:

renderer=get_default_renderer()return mark_safe(renderer.render(template_name, context))

所以我们可以自定制样式,属性

widget = widgets.TextInput(attrs={"class":"c1"}),#这个属性,在前端进行设置就可以生成想要的样式,widgets代表显示的字段对象

补充:在服务端生成原生字符串,不需要前端渲染时进行转义

txt = ""#默认发送到模板页面是无法显示需要进行处理

#在views中处理:

fromdjango.utils.safestring import make_safe

txt=mark_safe(txt)

#前端可以正常显示

select单选框:

sel_inp=fields.ChoiceField(

choices= [(1,'a'),(2,'b'),])

select框:

sel_inp =fields.CharField(

widget= widgets.Select(choices=[(1,'a'),(2,'b'),])

)

combo多选:

radio_inp=fields.MultipleChoiceField(

choices= [(1,'a'),(2,'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=[(1, "d"), (2, "e"),(3,'r') ],

initial= [2,3]

)

radio单选:

rad_inp =fields.ChoiceField(

choices=[(1,"男"),(2,"女"),],

initial=2,

widget=widgets.RadioSelect(),

)

字段用于保存正则表达式,Html插件用于生产HTML标签(input)

补充:为所有的字段控件设置属性(在__new__方法中获取base_fields,其中包含有字典数据{'字段名':字段对象,....})

fromdjango.forms import ModelFormfromrepository import modelsclassCustomerForm(ModelForm):classMeta:

model=models.CustumerInfo #将表与元类中的数据关联

fields= "__all__"def __new__(cls,*args, **kwargs):

print(cls.base_fields)

#OrderedDict([('name', ), ('contact_type', ), ('contact', ), ('source', ), ('referral_from', ), ('consult_courses', ), ('consult_content', ), ('status', ), ('consultant', )])

#这张表中的所有字段对象for field_name,field_obj indict(cls.base_fields).items():

field_obj.widget.attrs.update({'class':"form-control"}) #根据字段对象修改属性return ModelForm.__new__(cls)

数据的实时更新:和数据库关联

关联方法一

数据库:

classUser(models.Model):

username= models.CharField(max_length=32)

email= models.CharField(max_length=32)

form组件:

fromapp02.models import UserclassInfoForm(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:

fromapp02.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})

前端显示正常:

{{ obj.user }}

{{ obj.email }}

关联方法二

另一种实时更新的方法:使用ModelChoiceField

fromapp02.models import Userfrom django.forms import ModelChoiceFieldclassInfoForm(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'

)

前端:

{{ obj.user_id }}

默认输出:

---------

User object

User object

User object

User object

其中 User object 不是我们想要的,这依赖于models中的__str__方法

classUser(models.Model):

username= models.CharField(max_length=32)

email= models.CharField(max_length=32)

def __str__(self):return self.username

这时候输出正常

但是不推荐这种方法,这与models关联太强,不利于解耦,推荐使用第一种

关联方法三(ModelForm和元类)

forms.py

fromdjango.forms import ModelFormfromrepository import modelsclassCustomerForm(ModelForm):classMeta:

model=models.CustumerInfo   #将表与元类中的数据关联

fields= ['name','consultant','status','source']  #设置显示的字段

views.py中使用

form =forms.CustomerForm()return render(request,"table_obj_change.html",locals())

前端使用:

{{ form }}

下面来查看ModelForm源码

classModelFormMetaclass(DeclarativeFieldsMetaclass):

def __new__(mcs, name, bases, attrs):

base_formfield_callback=Nonefor b inbases:if hasattr(b, 'Meta') and hasattr(b.Meta, 'formfield_callback'):

base_formfield_callback=b.Meta.formfield_callbackbreakformfield_callback= attrs.pop('formfield_callback', base_formfield_callback)

new_class=super(ModelFormMetaclass, mcs).__new__(mcs, name, bases, attrs)if bases ==(BaseModelForm,):returnnew_class

opts= new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))

# We checkif a stringwas passed to `fields` or `exclude`,

# whichis 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)ifopts.model:

# If a modelis defined, extract form fields fromit.if opts.fields is None and opts.exclude isNone:

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:

# Sentinelfor 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) ifnot v]

missing_fields= (set(none_model_fields) -

set(new_class.declared_fields.keys()))ifmissing_fields:

message= 'Unknown field(s) (%s) specified for %s'message= message % (','.join(missing_fields),

opts.model.__name__)

raise FieldError(message)

# Overridedefaultmodel 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=fieldsreturn new_class

ModelFormMetaclass类源码

opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))  #opts代表ModelForm类中的元类,下面看看元类中可以设置那些数据

# We checkif a stringwas passed to `fields` or `exclude`,

# whichis 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)ifopts.model:  #model属性用来关联数据表

# If a modelis defined, extract form fields fromit.if opts.fields is None and opts.exclude isNone:

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:

# Sentinelfor 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) ifnot v]

missing_fields= (set(none_model_fields) -

set(new_class.declared_fields.keys()))ifmissing_fields:

message= 'Unknown field(s) (%s) specified for %s'message= message % (','.join(missing_fields),

opts.model.__name__)

raise FieldError(message)

# Overridedefaultmodel 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=fieldsreturn new_class

补充:

1.fields若是设置为__all__则是代表所有的字段都去显示

2.Form对象关联后可以使用instance属性去获取到关联的当前那条数据对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值