django基础之FBV与CBV,ajax序列化补充,Form表单
目录:
- FBV与CBV
- ajax序列化补充
- Form表单(一)
一、FBV与CBV
1、什么是FBV、CBV?
django书写view时,支持两种格式写法,FBV(function bases view即函数编程),CBV(class bases view即类编程)。
其中函数编程是本blog一直使用的编程方式,不再讨论。以下主要讨论CBV方式。但这两种方式没有优劣,书写可选择任意一种使用
2、CBV写法应用之登陆验证
1 from django.shortcuts import render,HttpResponse, redirect 2 from django.views import View 3 from django.views.decorators.csrf import csrf_exempt,csrf_protect 4 from django.utils.decorators import method_decorator 5 6 def auth(func): 7 def inner(request,*args,**kwargs): 8 if request.session.get('username'): 9 obj = func(request,*args,**kwargs) 10 return obj 11 else: 12 return redirect('/login.html') 13 return inner 14 15 16 # @method_decorator(auth,name='get') 17 class IndexView(View): 18 19 @method_decorator(auth) 20 def dispatch(self, request, *args, **kwargs): 21 if request.session.get('username'): 22 response = super(IndexView,self).dispatch(request, *args, **kwargs) 23 return response 24 else: 25 return redirect('/login.html') 26 27 @method_decorator(auth) 28 def get(self,request,*args,**kwargs): 29 return HttpResponse(request.session['username']) 30 31 @method_decorator(csrf_exempt) # 无效 32 def post(self,request,*args,**kwargs): 33 return HttpResponse(request.session['username'])
3、CSRF之csrf_exempt的bug处理
1 class IndexView(View): 2 3 @method_decorator(csrf_exempt) 4 def dispatch(self, request, *args, **kwargs): 5 return super(LoginView,self).dispatch(request, *args, **kwargs) 6 7 8 def get(self,request,*args,**kwargs): 9 return HttpResponse(request.session['username']) 10 11 12 def post(self,request,*args,**kwargs): 13 return HttpResponse(request.session['username'])
4、多实例调用
1 from django.shortcuts import render,HttpResponse, redirect 2 from django.views import View 3 from django.views.decorators.csrf import csrf_exempt,csrf_protect 4 from django.utils.decorators import method_decorator 5 from app01 import models 6 7 class AuthView(object): 8 def dispatch(self, request, *args, **kwargs): 9 if request.session.get('user_info'): 10 response = super(AuthView,self).dispatch(request, *args, **kwargs) 11 return response 12 else: 13 return redirect('/login.html') 14 15 class UsersView(AuthView,View): 16 17 def get(self,request,*args,**kwargs): 18 user_list = models.UserInfo.objects.all() 19 return render(request,'users.html',{'user_list':user_list})
二、ajax序列化补充
1、后端代码书写的两种方式
方式一
1 user_list = models.UserInfo.objects.all() 2 data = serializers.serialize("json", user_list) 3 [ 4 {"model": "app01.userinfo", "pk": 1, "fields": {"username": "\u5174\u666e", "password": "123123"}}, 5 {"model": "app01.userinfo", "pk": 2, "fields": {"username": "\u94f6\u79cb\u826f", "password": "666"}} 6 ]
方式二
1 user_list = models.UserInfo.objects.values('id','username') 2 user_list = list(user_list) 3 data = json.dumps(user_list) 4 [ 5 {"username": "\u5174\u666e", "id": 1}, 6 {"username": "\u94f6\u79cb\u826f", "id": 2} 7 ]
2、对json.dumps进行定制
1 import json 2 from datetime import date 3 from datetime import datetime 4 5 class JsonCustomEncoder(json.JSONEncoder): 6 def default(self, field): 7 if isinstance(field, datetime): 8 return field.strftime('%Y-%m-%d %H:%M:%S') 9 elif isinstance(field, date): 10 return field.strftime('%Y-%m-%d') 11 else: 12 return json.JSONEncoder.default(self, field) 13 14 15 user_list = [ 16 {'id':1,'name':'alex','ctime': datetime.now()}, 17 {'id':2,'name':'eric','ctime': datetime.now()} 18 ] 19 20 data = json.dumps(user_list,cls=JsonCustomEncoder) 21 print(data)
三、Form表单(一)
1、 创建From类(本质就是正则表达式的集合)
1 from django.forms import Form 2 from django.forms import fields 3 from django.forms import widgets 4 5 class UserForm(Form): 6 username = fields.CharField( 7 required=True, # 不为空 8 error_messages={'required':'用户名不能为空'}, #报错信息 9 widget=widgets.TextInput(attrs={'class':'form-control'}) #css样式 10 ) 11 password = fields.CharField( 12 required=True, 13 error_messages={'required': '邮箱不能为空','invalid':'邮箱格式错误'}, 14 widget = widgets.TextInput(attrs={'class': 'form-control'}) 15 ) 16 # fields.EmailField() 17 # fields.GenericIPAddressField(protocol='ipv4') 18 19 ut_id = fields.ChoiceField( 20 choices=[], 21 widget=widgets.Select(attrs={'class':'form-control'}) 22 ) 23 24 role_id = fields.MultipleChoiceField( 25 choices=[], 26 widget=widgets.SelectMultiple(attrs={'class':'form-control'}) 27 ) 28 29 def __init__(self,*args,**kwargs): 30 super(UserForm,self).__init__(*args,**kwargs) #继承父类__init__ 31 # self.fields已经有所有拷贝的字段 32 self.fields['ut_id'].choices = models.UserType.objects.values_list('id','title') #每刷新到数据库取值 33 self.fields['role_id'].choices = models.Role.objects.values_list('id','caption')
2、生成HTML标签
view中
form = MyForm()
html中
{{form.xx}}
1 class AddUserView(AuthView,View): 2 def get(self,request,*args,**kwargs): 3 form = UserForm() 4 return render(request,'add_user.html',{'form':form}) 5 6 def post(self,request,*args,**kwargs): 7 form = UserForm(data=request.POST) 8 # 将用户提交的数据和UserForm中定义规则进行匹配: 9 if form.is_valid(): 10 # 把所有正确数据获取到 11 # {'username': 'xxxxx', 'password': 'xxxxx', 'ut_id': '1'} 12 # print(form.cleaned_data) 13 # {'username': 'xxxxx', 'password': 'xxxxx', 'ut_id': '1',role_id:} 14 role_id_list = form.cleaned_data.pop('role_id') # [1,2] 15 obj = models.UserInfo.objects.create(**form.cleaned_data) 16 obj.rl.add(*role_id_list) 17 return redirect('/users.html') 18 else: 19 print(form.errors) 20 return render(request, 'add_user.html', {'form': form})
3、带默认值的HTML标签
view中
form = MyForm(initial={'xx': xxx})
html中
{{form.xx}}
1 class EditUserView(AuthView,View): 2 def get(self,request,pk): 3 obj = models.UserInfo.objects.filter(id=pk).first() 4 role_id_list = obj.rl.values_list('id') 5 v = list(zip(*role_id_list))[0] if role_id_list else [] 6 form = UserForm(initial={'username': obj.username, 'password': obj.password, 'ut_id': obj.ut_id,'role_id':v}) 7 return render(request,'edit_user.html',{'form':form}) 8 9 def post(self,request,pk): 10 form = UserForm(data=request.POST) 11 if form.is_valid(): 12 # # {'username': 'xxxxx', 'password': 'xxxxx', 'ut_id': '1',role_id:} 13 role_id = form.cleaned_data.pop('role_id') 14 # 用户表更新 15 query = models.UserInfo.objects.filter(id=pk) 16 query.update(**form.cleaned_data) 17 obj = query.first() 18 obj.rl.set(role_id) 19 20 return redirect('/users.html') 21 else: 22 print(form.errors) 23 return render(request, 'edit_user.html', {'form': form})
4、提交数据
view中
form = MyForm(data=request.POST)
if form.is_valid(): # 表单验证
print(form.cleaned_data) #取表单数据
else:
print(form.errors) #表单错误信息
5、Form类之内置字段
1 Field 2 required=True, 是否允许为空 3 widget=None, HTML插件 4 label=None, 用于生成Label标签或显示内容 5 initial=None, 初始值 6 help_text='', 帮助信息(在标签旁边显示) 7 error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} 8 show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) 9 validators=[], 自定义验证规则 10 localize=False, 是否支持本地化 11 disabled=False, 是否可以编辑 12 label_suffix=None Label内容后缀
6、Form类之内置字段(二)
1 CharField(Field) 2 max_length=None, 最大长度 3 min_length=None, 最小长度 4 strip=True 是否移除用户输入空白 5 6 IntegerField(Field) 7 max_value=None, 最大值 8 min_value=None, 最小值 9 10 FloatField(IntegerField) 11 ... 12 13 DecimalField(IntegerField) 14 max_value=None, 最大值 15 min_value=None, 最小值 16 max_digits=None, 总长度 17 decimal_places=None, 小数位长度 18 19 BaseTemporalField(Field) 20 input_formats=None 时间格式化 21 22 DateField(BaseTemporalField) 格式:2015-09-01 23 TimeField(BaseTemporalField) 格式:11:12 24 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 25 26 DurationField(Field) 时间间隔:%d %H:%M:%S.%f 27 ... 28 29 RegexField(CharField) 30 regex, 自定制正则表达式 31 max_length=None, 最大长度 32 min_length=None, 最小长度 33 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} 34 35 EmailField(CharField) 36 ... 37 38 FileField(Field) 39 allow_empty_file=False 是否允许空文件 40 41 ImageField(FileField) 42 ... 43 注:需要PIL模块,pip3 install Pillow 44 以上两个字典使用时,需要注意两点: 45 - form表单中 enctype="multipart/form-data" 46 - view函数中 obj = MyForm(request.POST, request.FILES) 47 48 URLField(Field) 49 ... 50 51 52 BooleanField(Field) 53 ... 54 55 NullBooleanField(BooleanField) 56 ... 57 58 ChoiceField(Field) 59 ... 60 choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) 61 required=True, 是否必填 62 widget=None, 插件,默认select插件 63 label=None, Label内容 64 initial=None, 初始值 65 help_text='', 帮助提示 66 67 68 ModelChoiceField(ChoiceField) 69 ... django.forms.models.ModelChoiceField 70 queryset, # 查询数据库中的数据 71 empty_label="---------", # 默认空显示内容 72 to_field_name=None, # HTML中value的值对应的字段 73 limit_choices_to=None # ModelForm中对queryset二次筛选 74 75 ModelMultipleChoiceField(ModelChoiceField) 76 ... django.forms.models.ModelMultipleChoiceField 77 78 79 80 TypedChoiceField(ChoiceField) 81 coerce = lambda val: val 对选中的值进行一次转换 82 empty_value= '' 空值的默认值 83 84 MultipleChoiceField(ChoiceField) 85 ... 86 87 TypedMultipleChoiceField(MultipleChoiceField) 88 coerce = lambda val: val 对选中的每一个值进行一次转换 89 empty_value= '' 空值的默认值 90 91 ComboField(Field) 92 fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 93 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) 94 95 MultiValueField(Field) 96 PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 97 98 SplitDateTimeField(MultiValueField) 99 input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] 100 input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] 101 102 FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 103 path, 文件夹路径 104 match=None, 正则匹配 105 recursive=False, 递归下面的文件夹 106 allow_files=True, 允许文件 107 allow_folders=False, 允许文件夹 108 required=True, 109 widget=None, 110 label=None, 111 initial=None, 112 help_text='' 113 114 GenericIPAddressField 115 protocol='both', both,ipv4,ipv6支持的IP格式 116 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 117 118 SlugField(CharField) 数字,字母,下划线,减号(连字符) 119 ... 120 121 UUIDField(CharField) uuid类型
7、Django内置插件:
1 TextInput(Input) 2 NumberInput(TextInput) 3 EmailInput(TextInput) 4 URLInput(TextInput) 5 PasswordInput(TextInput) 6 HiddenInput(TextInput) 7 Textarea(Widget) 8 DateInput(DateTimeBaseInput) 9 DateTimeInput(DateTimeBaseInput) 10 TimeInput(DateTimeBaseInput) 11 CheckboxInput 12 Select 13 NullBooleanSelect 14 SelectMultiple 15 RadioSelect 16 CheckboxSelectMultiple 17 FileInput 18 ClearableFileInput 19 MultipleHiddenInput 20 SplitDateTimeWidget 21 SplitHiddenDateTimeWidget 22 SelectDateWidget
常用选择插件:
1 <strong># 单radio,值为字符串</strong> 2 # user = fields.CharField( 3 # initial=2, 4 # widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) 5 # ) 6 7 <strong># 单radio,值为字符串</strong> 8 # user = fields.ChoiceField( 9 # choices=((1, '上海'), (2, '北京'),), 10 # initial=2, 11 # widget=widgets.RadioSelect 12 # ) 13 14 <strong># 单select,值为字符串</strong> 15 # user = fields.CharField( 16 # initial=2, 17 # widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) 18 # ) 19 20 <strong># 单select,值为字符串</strong> 21 # user = fields.ChoiceField( 22 # choices=((1, '上海'), (2, '北京'),), 23 # initial=2, 24 # widget=widgets.Select 25 # ) 26 27 <strong># 多选select,值为列表</strong> 28 # user = fields.MultipleChoiceField( 29 # choices=((1,'上海'),(2,'北京'),), 30 # initial=[1,], 31 # widget=widgets.SelectMultiple 32 # ) 33 34 <strong># 单checkbox</strong> 35 # user = fields.CharField( 36 # widget=widgets.CheckboxInput() 37 # ) 38 39 <strong># 多选checkbox,值为列表</strong> 40 # user = fields.MultipleChoiceField( 41 # initial=[2, ], 42 # choices=((1, '上海'), (2, '北京'),), 43 # widget=widgets.CheckboxSelectMultiple 44 # )
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一:
1 from django.forms import Form 2 from django.forms import widgets 3 from django.forms import fields 4 from django.core.validators import RegexValidator 5 6 class MyForm(Form): 7 8 user = fields.ChoiceField( 9 # choices=((1, '上海'), (2, '北京'),), 10 initial=2, 11 widget=widgets.Select 12 ) 13 14 def __init__(self, *args, **kwargs): 15 super(MyForm,self).__init__(*args, **kwargs) 16 # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),) 17 # 或 18 self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
方式二:
使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
1 from django import forms 2 from django.forms import fields 3 from django.forms import widgets 4 from django.forms import models as form_model 5 from django.core.exceptions import ValidationError 6 from django.core.validators import RegexValidator 7 8 class FInfo(forms.Form): 9 authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) 10 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
8、自定义规则
方式一:
1 from django.forms import Form 2 from django.forms import widgets 3 from django.forms import fields 4 from django.core.validators import RegexValidator 5 6 class MyForm(Form): 7 user = fields.CharField( 8 validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], 9 )
方式二:
1 import re 2 from django.forms import Form 3 from django.forms import widgets 4 from django.forms import fields 5 from django.core.exceptions import ValidationError 6 7 8 # 自定义验证规则 9 def mobile_validate(value): 10 mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') 11 if not mobile_re.match(value): 12 raise ValidationError('手机号码格式错误') 13 14 15 class PublishForm(Form): 16 17 18 title = fields.CharField(max_length=20, 19 min_length=5, 20 error_messages={'required': '标题不能为空', 21 'min_length': '标题最少为5个字符', 22 'max_length': '标题最多为20个字符'}, 23 widget=widgets.TextInput(attrs={'class': "form-control", 24 'placeholder': '标题5-20个字符'})) 25 26 27 # 使用自定义验证规则 28 phone = fields.CharField(validators=[mobile_validate, ], 29 error_messages={'required': '手机不能为空'}, 30 widget=widgets.TextInput(attrs={'class': "form-control", 31 'placeholder': u'手机号码'})) 32 33 email = fields.EmailField(required=False, 34 error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, 35 widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
方式三:
1 from django import forms 2 from django.forms import fields 3 from django.forms import widgets 4 from django.core.exceptions import ValidationError 5 from django.core.validators import RegexValidator 6 7 class FInfo(forms.Form): 8 username = fields.CharField(max_length=5, 9 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], ) 10 email = fields.EmailField() 11 12 def clean_username(self): 13 """ 14 Form中字段中定义的格式匹配完之后,执行此方法进行验证 15 :return: 16 """ 17 value = self.cleaned_data['username'] 18 if "666" in value: 19 raise ValidationError('666已经被玩烂了...', 'invalid') 20 return value
方式四:
1 from django.forms import Form 2 from django.forms import widgets 3 from django.forms import fields 4 5 from django.core.validators import RegexValidator 6 7 8 ############## 自定义字段 ############## 9 class PhoneField(fields.MultiValueField): 10 def __init__(self, *args, **kwargs): 11 # Define one message for all fields. 12 error_messages = { 13 'incomplete': 'Enter a country calling code and a phone number.', 14 } 15 # Or define a different message for each field. 16 f = ( 17 fields.CharField( 18 error_messages={'incomplete': 'Enter a country calling code.'}, 19 validators=[ 20 RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'), 21 ], 22 ), 23 fields.CharField( 24 error_messages={'incomplete': 'Enter a phone number.'}, 25 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')], 26 ), 27 fields.CharField( 28 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')], 29 required=False, 30 ), 31 ) 32 super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args, 33 **kwargs) 34 35 def compress(self, data_list): 36 """ 37 当用户验证都通过后,该值返回给用户 38 :param data_list: 39 :return: 40 """ 41 return data_list 42 43 ############## 自定义插件 ############## 44 class SplitPhoneWidget(widgets.MultiWidget): 45 def __init__(self): 46 ws = ( 47 widgets.TextInput(), 48 widgets.TextInput(), 49 widgets.TextInput(), 50 ) 51 super(SplitPhoneWidget, self).__init__(ws) 52 53 def decompress(self, value): 54 """ 55 处理初始值,当初始值initial不是列表时,调用该方法 56 :param value: 57 :return: 58 """ 59 if value: 60 return value.split(',') 61 return [None, None, None]
9、初始化数据
在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。
1、Form
1 from django.forms import Form 2 from django.forms import widgets 3 from django.forms import fields 4 from django.core.validators import RegexValidator 5 6 7 class MyForm(Form): 8 user = fields.CharField() 9 10 city = fields.ChoiceField( 11 choices=((1, '上海'), (2, '北京'),), 12 widget=widgets.Select 13 )
2、Views
1 from django.shortcuts import render, redirect 2 from .forms import MyForm 3 4 5 def index(request): 6 if request.method == "GET": 7 values = {'user': 'root', 'city': 2} 8 obj = MyForm(values) 9 10 return render(request, 'index.html', {'form': obj}) 11 elif request.method == "POST": 12 return redirect('http://www.google.com') 13 else: 14 return redirect('http://www.google.com')
3、HTML
1 <form method="POST" enctype="multipart/form-data"> 2 {% csrf_token %} 3 <p>{{ form.user }} {{ form.user.errors }}</p> 4 <p>{{ form.city }} {{ form.city.errors }}</p> 5 6 <input type="submit"/> 7 </form>