常規頁面中,若是想實現對錶單中用戶輸入信息的數據驗證,須要配合Ajax來實現.css
使用前咱們先來熟悉下函數參數:request,其中包含的意義:html
request.path 除去了域名和端口的訪問路徑, request.get_host 域名+端口信息 request.get_full_path() 全部路徑,包含傳遞的參數 requets.is_secure() 是否使用https進行連接
Django中的form庫:每個邦定Form實體都有一個errors屬性,它爲你提供了一個字段與錯誤消息相映射的字典表。能夠這麼定義:python
>>> f = ContactForm({'subject': 'Hello', 'message': ''}) >>> f.errors {'message': [u'This field is required.']}
實現驗證的必要條件:jquery
forms.Form
的子類;name
屬性;1)建立forms類:git
class LoginForm(forms.Form): user = forms.CharField(required=True, error_messages={'required': '用戶名不能爲空.'}) pwd = forms.CharField(required=True, min_length=6, max_length=10, error_messages={'required': '密碼不能爲空.', 'min_length': "至少6位"}) num = forms.IntegerField(error_messages={'required': '數字不能空.', 'invalid': '必須輸入數字'}) phone = forms.CharField(validators=[mobile_validate, ], )
2)views函數處理:web
def login(request): if request.method == 'POST': result = {'status': False, 'message': None} obj = LoginForm(request.POST) ret = obj.is_valid() if ret: print(obj.clean()) result['status'] = True else: from django.forms.utils import ErrorDict print(type(obj.errors), obj.errors.as_json()) # print(obj.errors) error_str = obj.errors.as_json() result['message'] = json.loads(error_str) return HttpResponse(json.dumps(result)) return render(request, 'login.html')
3)login.htm文件:ajax
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .error_msg{ color: red; } </style> </head> <body> <div> <div> <input type="text" name="user" /> </div> <div> <input type="password" name="pwd" /> </div> <input type="button" value="提交" onclick="DoSubmit();" /> </div> <script src="/static/jquery-1.12.4.js"></script> <script> function DoSubmit(){ $('.error_msg').remove(); var input_dict = {}; $('input').each(function(){ var v = $(this).val(); var n = $(this).attr('name'); input_dict[n] = v; }); console.log(input_dict); $.ajax({ url: '/login/', type: 'POST', data: input_dict, dataType:'json', success: function (result) { if(result.status){ location.href = '/index/' }else { $.each(result.message,function (k,v) { console.log(k,v[0].message); var tag = document.createElement('span'); tag.className = 'error_msg'; tag.innerText = v[0].message; $('input[name="'+k +'"]').after(tag); }) } } }) } </script> </body> </html>
2. django自帶form驗證正則表達式
Django的Form主要具備一下幾大功能:數據庫
建立Form類時,主要涉及到 【字段】 和 【插件】,字段用於對用戶請求數據的驗證,插件用於自動生成HTML;django
Field required=True, 是否容許爲空 widget=None, HTML插件 label=None, 用於生成Label標籤或顯示內容 initial=None, 初始值 help_text='', 幫助信息(在標籤旁邊顯示) error_messages=None, 錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'} show_hidden_initial=False, 是否在當前插件後面再加一個隱藏的且具備默認值的插件(可用於檢驗兩次輸入是否一直) validators=[], 自定義驗證規則 localize=False, 是否支持本地化 disabled=False, 是否能夠編輯 label_suffix=None Label內容後綴 CharField(Field) max_length=None, 最大長度 min_length=None, 最小長度 strip=True 是否移除用戶輸入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 總長度 decimal_places=None, 小數位長度 BaseTemporalField(Field) input_formats=None 時間格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 時間間隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定製正則表達式 max_length=None, 最大長度 min_length=None, 最小長度 error_message=None, 忽略,錯誤信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否容許空文件 ImageField(FileField) ... 注:須要PIL模塊,pip3 install Pillow 以上兩個字典使用時,須要注意兩點: - form表單中 enctype="multipart/form-data" - view函數中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 選項,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默認select插件 label=None, Label內容 initial=None, 初始值 help_text='', 幫助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查詢數據庫中的數據 empty_label="---------", # 默認空顯示內容 to_field_name=None, # HTML中value的值對應的字段 limit_choices_to=None # ModelForm中對queryset二次篩選 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 對選中的值進行一次轉換 empty_value= '' 空值的默認值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 對選中的每個值進行一次轉換 empty_value= '' 空值的默認值 ComboField(Field) fields=() 使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件選項,目錄下文件顯示在頁面中 path, 文件夾路徑 match=None, 正則匹配 recursive=False, 遞歸下面的文件夾 allow_files=True, 容許文件 allow_folders=False, 容許文件夾 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用 SlugField(CharField) 數字,字母,下劃線,減號(連字符) ... UUIDField(CharField) uuid類型 ...
TextInput(Input) NumberInput(TextInput) EmailInput(TextInput) URLInput(TextInput) PasswordInput(TextInput) HiddenInput(TextInput) Textarea(Widget) DateInput(DateTimeBaseInput) DateTimeInput(DateTimeBaseInput) TimeInput(DateTimeBaseInput) CheckboxInput Select NullBooleanSelect SelectMultiple RadioSelect CheckboxSelectMultiple FileInput ClearableFileInput MultipleHiddenInput SplitDateTimeWidget SplitHiddenDateTimeWidget SelectDateWidget
2.3 經常使用插件:
# 單radio,值爲字符串 # user = fields.CharField( # initial=2, # widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) # ) # 單radio,值爲字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.RadioSelect # ) # 單select,值爲字符串 # user = fields.CharField( # initial=2, # widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) # ) # 單select,值爲字符串 # user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), # initial=2, # widget=widgets.Select # ) # 多選select,值爲列表 # user = fields.MultipleChoiceField( # choices=((1,'上海'),(2,'北京'),), # initial=[1,], # widget=widgets.SelectMultiple # ) # 單checkbox # user = fields.CharField( # widget=widgets.CheckboxInput() # ) # 多選checkbox,值爲列表 # user = fields.MultipleChoiceField( # initial=[2, ], # choices=((1, '上海'), (2, '北京'),), # widget=widgets.CheckboxSelectMultiple # )
在使用選擇標籤時,須要注意choices的選項能夠從數據庫中獲取,可是因爲是靜態字段 ***獲取的值沒法實時更新***,那麼須要自定義構造方法從而達到此目的。
方式一:
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select ) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),) # 或 self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
方式二:
使用django提供的ModelChoiceField和ModelMultipleChoiceField字段來實現
from django import forms from django.forms import fields from django.forms import widgets from django.forms import models as form_model from django.core.exceptions import ValidationError from django.core.validators import RegexValidator class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')], )
import re from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.exceptions import ValidationError # 自定義驗證規則 def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手機號碼格式錯誤') class PublishForm(Form): title = fields.CharField(max_length=20, min_length=5, error_messages={'required': '標題不能爲空', 'min_length': '標題最少爲5個字符', 'max_length': '標題最多爲20個字符'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '標題5-20個字符'})) # 使用自定義驗證規則 phone = fields.CharField(validators=[mobile_validate, ], error_messages={'required': '手機不能爲空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手機號碼'})) email = fields.EmailField(required=False, error_messages={'required': u'郵箱不能爲空','invalid': u'郵箱格式錯誤'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'郵箱'}))
方法三:自定義方法
class register_fm(forms.Form): def email_form(value): email_re = re.compile(r"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$") if not email_re.match(value): raise ValidationError('郵箱格式錯誤,請從新輸入!') username = fields.CharField( required=True, min_length=2, max_length=12, error_messages={ 'required': '用戶名不能爲空', 'min_length': '用戶名最少2個字符', 'max_length': '用戶名最多爲12個字符', },) email = fields.CharField( required=True, validators=[email_form,], error_messages={ 'required': '郵箱不能爲空', },) pwd = fields.CharField( required=True, min_length=6, max_length=20, error_messages={ 'required': '密碼不能爲空', 'min_length': '密碼最少6個字符', 'max_length': '密碼最多爲20個字符', },) repwd = fields.CharField( required=True, max_length=20, error_messages={ 'required':'確認密碼不能爲空', 'max_length': '密碼最多爲20個字符', },) check_code = fields.CharField( required=True, max_length=4, error_messages={ 'required':'驗證碼不能爲空', 'max_length': '驗證碼最多爲4個字符', },) def clean_username(self): cleaned_data = self.cleaned_data c = models.UserInfo.objects.filter(username=cleaned_data['username']) if c: raise ValidationError('用戶名已存在,請從新輸入!') else: return cleaned_data def clean_repwd(self): password1 = self.cleaned_data.get('pwd') password2 = self.cleaned_data.get('repwd') if password1 and password2 and password1!=password2: raise ValidationError('兩次密碼不一致,請從新輸入!') return self.cleaned_data
執行自定義方法時,可能你們會發現一個問題;若是字段爲空(如用戶名、密碼等),點擊提交時後臺會報錯;由於celaned_data沒有想要取得數據;
這裏咱們有兩種處理方式:
1. 增長判斷:
1)若是隻判斷單條數據,咱們能夠以下:
def clean_username(self): """ Form中字段中定義的格式匹配完以後,執行此方法進行驗證 :return: """ value = self.cleaned_data['username'] if value: if "666" in value: raise ValidationError('666已經被玩爛了...', 'invalid') return value
2)若是判斷多個數據,咱們能夠考慮判斷下長度(如登陸狀況,判斷用戶名和密碼):
def clean(self): """ Form中字段中定義的格式匹配完以後,執行此方法進行驗證 :return: """ value = self.cleaned_data if len(value)>1: c=models.UserInfo.objects.filter(username=value['username'],pwd=value['password']).count() if c: return self.cleaned_data else: raise ValidationError("用戶名或密碼錯誤!")
2. 更改獲取方式
def clean_username(self): """ Form中字段中定義的格式匹配完以後,執行此方法進行驗證 :return: """ value = self.cleaned_data.get("username") if value: if "666" in value: raise ValidationError('666已經被玩爛了...', 'invalid') return value
1)建立數據庫;
class UserInfo(models.Model): """ 用戶表 """ nid = models.BigAutoField(primary_key=True) username = models.CharField(verbose_name='用戶名', max_length=32, unique=True) password = models.CharField(verbose_name='密碼', max_length=64)
2)url定義;
urlpatterns = [ url(r'^login.html$',login), ]
3)自定義驗證規則;
class login_fm(forms.Form): username = fields.CharField( max_length=12, required=True, error_messages={ 'required': '用戶名不能爲空', 'max_length': '用戶名最多爲12個字符', },) pwd = fields.CharField( max_length=20, required=True, error_messages={ 'required': '密碼不能爲空', 'max_length': '密碼最多爲20個字符', },) def clean(self): cleaned_data = self.cleaned_data if len(cleaned_data)>1: c = models.UserInfo.objects.filter(username=cleaned_data['username'],password=cleaned_data['pwd']).count() if c: return cleaned_data else: raise ValidationError('用戶名或密碼錯誤,請從新輸入!')
4)views;
from io import BytesIO from django.shortcuts import HttpResponse from django.shortcuts import render,redirect from utils.check_code import create_validate_code from repository import models import json from web.views import forms from django.core.exceptions import ValidationError class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field,ValidationError): return {'code': field.code, 'messages': field.messages} else: return json.JSONEncoder.default(self, field) def login(request): """ 登陸 :param request: :return: """ if request.method == 'GET': return render(request,'login.html') elif request.method == 'POST': ret = {'status': True, 'error': None, 'data': None} u = request.POST.get('username') obj = forms.login_fm(request.POST) if obj.is_valid(): ret['status'] = True request.session['username'] = u request.session['is_login'] = True else: ret['status'] = False ret['error'] = obj.errors.as_data() result = json.dumps(ret, cls=JsonCustomEncoder) return HttpResponse(result)
5)html模板;
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="../static/plugins/bootstrap/css/bootstrap.css"/> <link rel="stylesheet" href="../static/plugins/font-awesome/css/font-awesome.css"/> <link rel="stylesheet" href="../static/css/edmure.css"/> <link rel="stylesheet" href="../static/css/commons.css"/> <link rel="stylesheet" href="../static/css/account.css"/> </head> <body> <div class="bg"> <div class="login"> <div style="font-size: 25px; font-weight: bold;text-align: center;"> 用戶登陸 </div> <form role="form" id="login_form"> {% csrf_token %} <div class="form-group"> <label for="username">用戶名</label> <input type="email" class="form-control" name="username" id="username" placeholder="請輸入用戶名"> </div> <div class="form-group"> <label for="password">密碼</label> <input type="password" class="form-control" name="pwd" id="password" placeholder="請輸入密碼"> </div> <div class="form-group"> <span class="error_mg" id="error_msg">{{ error_msg}}</span> </div> <a id="login_yes" class="btn btn-default">登陸</a> </form> </div> </div> <script src="../static/js/jquery-1.12.4.js"></script> <script src="../static/js/jquery.cookie.js"></script> <script> $(function(){ $.ajaxSetup({ beforeSend:function (xhr,settings){ xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); } }); $('#login_yes').click(function () { $.ajax({ url:"/login.html", type:"POST", data:$('#login_form').serialize(), success:function (data) { var obj=JSON.parse(data); if (obj.status){ location.href='/backend/base-info.html'; }else{ if (obj.error.__all__){ $('#error_msg').text(obj.error.__all__[0].messages); } else{ if (obj.error.username){ $('#error_msg').text(obj.error.username[0].messages) }else{ if (obj.error.pwd){ $('#error_msg').text(obj.error.pwd[0].messages) }else{ $('#error_msg').text(obj.error) } } } } } }) }) }) </script> </body> </html> login.html
在Web應用程序中開發編寫功能時,時經常使用到獲取數據庫中的數據並將值初始化在HTML中的標籤上。
1)forms:
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField() city = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), widget=widgets.Select )
2)views:
from django.shortcuts import render, redirect from .forms import MyForm def index(request): if request.method == "GET": values = {'user': 'root', 'city': 2} obj = MyForm(values) return render(request, 'index.html', {'form': obj}) elif request.method == "POST": return redirect('http://www.google.com') else: return redirect('http://www.google.com')
3)html文件:
<form method="POST" enctype="multipart/form-data"> {% csrf_token %} <p>{{ form.user }} {{ form.user.errors }}</p> <p>{{ form.city }} {{ form.city.errors }}</p> <input type="submit"/> </form>
django爲用戶實現防止跨站請求僞造的功能,經過中間件django.middleware.csrf.CsrfViewMiddleware來完成。而對於django中設置防跨站請求僞造功能有分爲全局和局部。
全局:文件位置setting.py
,文件中間件位置:django.middleware.csrf.CsrfViewMiddleware
局部:
django.views.decorators.csrf
import csrf_exempt,csrf_protect
使用時,views.py中無需設置任何東西,常規return render(xxx)
便可,主要是在HTML頁面中.
普通form中添加在form中設置{% csrf_token %}
便可.以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>csrf</title> </head> <body> <form action="/csrf/" method="POST"> {% csrf_token %} <input type="text" name="v"/> <input type="submit" value="提交"/> </form> </body> </html>