Python学习之路—2018/7/2
1.Django的分页器
通过输入地址栏参数
view.py
from django.shortcuts import render
from .models import Book
from django.core.paginator import Paginator, EmptyPage
def index(request):
# 批量插入数据
book_list = []
for i in range(0, 100):
book = Book(title="book_%s" % str(i), price=i*i)
book_list.append(book)
Book.objects.bulk_create(book_list)
# 分页器
book_list = Book.objects.all()
paginator = Paginator(book_list, 10)
print("数据总数:", paginator.count) # 数据总数: 100
print("页码总数:", paginator.num_pages) # 页码总数: 10
print("页码范围:", paginator.page_range) # 页码范围: range(1, 11)
try:
current_page = int(request.GET.get("page", 1))
page = paginator.page(current_page)
except EmptyPage as e: # 当出现EmptyPage时,让当前页为第一页
page = paginator.page(1)
return render(request, "index.html", locals())
index.html
{% for book in page %}
<li>{{ book.title }}:{{ book.price }}</li>
{% endfor %}
展示效果:
添加按钮
通过给每一页的标签的href赋值对应的页码(href="?page=页码")起到切换页码的功能
如果当前页有前(后)一页,则将当前页的前(后)一页的页码传递给(上一页或者下一页的)a标签,如果没有则给a标签添加disabled属性,使其无法点击
index.html
{% for book in page %}
<li>{{ book.title }}:{{ book.price }}</li>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
// 如果当前页有前一页时
{% if page.has_previous %}
<li>
<a href="?page={{ page.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Previous" class="disabled">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{% for foo in ranges %}
{% if foo == current_page %}
<li class="active"><a href="?page={{ foo }}">{{ foo }}</a></li>
{% else %}
<li><a href="?page={{ foo }}">{{ foo }}</a></li>
{% endif %}
{% endfor %}
{% if page.has_next %}
<li>
<a href="?page={{ page.next_page_number }}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Next" class="disabled">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
当总页数过多的时候,可以自定义每次显示的页数,将页码范围定义为当前页的前n页到后n页
views.py
def index(request):
book_list = Book.objects.all()
paginator = Paginator(book_list, 5)
try:
current_page = int(request.GET.get("page", 1))
page = paginator.page(current_page)
if paginator.num_pages > 9:
ranges = range(current_page - 4, current_page + 5)
if current_page - 5 < 0:
ranges = range(1, 9)
if current_page + 5 > paginator.num_pages:
ranges = range(paginator.num_pages - 8, paginator.num_pages+1)
except EmptyPage as e:
page = paginator.page(1)
return render(request, "index.html", locals())
实现效果:
2.forms组件
当提交form表单时,有些字段需要验证,比如邮箱格式,手机号码等等,此时需要用到django中的forms组件,无需用正则表达式进行验证
检验字段功能
models.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
email = models.EmailField()
tel = models.CharField(max_length=32)
register.html
<form action="" method="post">
{% csrf_token %}
用户名<input type="text" name="user">
密码<input type="text" name="pwd">
再次输入密码<input type="text" name="r_pwd">
邮箱<input type="email" name="email">
手机号码<input type="tel" name="phone">
<input type="submit" value="提交">
</form>
views.py
from django.shortcuts import render, HttpResponse
from django import forms
class Form(forms.Form): # 校验
user = forms.CharField(label="用户名")
pwd = forms.CharField(min_length=6, label="密码")
r_pwd = forms.CharField(min_length=6, label="确认密码")
email = forms.EmailField(label="邮箱")
phone = forms.CharField(label="手机号码")
def register(request):
if request.method == "GET":
return render(request, "register.html")
elif request.method == "POST":
form = From(request.POST)
if form.is_valid(): # 判断是否检验成功
print(form.cleaned_data) # 所有校验成功的字段
else:
print(form.cleaned_data)
print(form.errors) # 检验失败的字段,以字典形式存放,键为字段名,值为错误信息
return HttpResponse("OK")
注意:
- form表单中的input标签中的name值需与校验类中的变量名保持一致
- 当检验有一个不匹配时,返回的结果(is_valid)为false
- 检验过程中只会对类中定义的变量名进行校验,多余的参数不会影响校验结果
- 可以通过errors.get("字段名")[0]获取具体的错误信息
渲染标签功能
渲染方式1
<form action="" method="post">
{% csrf_token %}
<p>
<label>用户名</label>
{{ form.user }}
</p>
<p>
<label>密码</label>
{{ form.pwd }}
</p>
<p>
<label>再次密码</label>
{{ form.r_pwd }}
</p>
<p>
<label>邮箱</label>
{{ form.email }}
</p>
<p>
<label>手机号码</label>
{{ form.phone }}
</p>
<input type="submit" value="提交">
</form>
渲染方式2
<form action="" method="post">
{% csrf_token %}
{% for foo in form %}
<p>
<label>{{ foo.label }}</label>
{{ foo }}
</p>
{% endfor %}
<input type="submit" value="提交">
</form>
渲染方式3
<form action="" method="post">
{{ form.as_p }}
<input type="submit" value="提交">
</form>
不推荐渲染方式3,因为它的格式是固定的,as_p(<p><label>...</label><input>...</input></p>
),不便于拓展。
显示错误信息
index.html
<form action="" method="post">
{% csrf_token %}
{% for foo in form %}
<p>
<label>{{ foo.label }}</label>
{{ foo }}
<span style="color: red">{{ foo.errors.0 }}</span> // 通过errors.0调用错误信息
</p>
{% endfor %}
<input type="submit" value="提交">
</form>
展示效果:
参数配置
- 自定义错误内容error_messages={}
- 定义渲染的标签类型 widgets.xxx
- 给渲染的标签加class属性widgets.xxx(attrs={})
views.py
from django import forms
from django.forms import widgets
wid_1 = widgets.TextInput(attrs={"class": "form-control"})
wid_2 = widgets.PasswordInput(attrs={"class": "form-control"})
class Form(forms.Form):
user = forms.CharField(min_length=4, label="用户名", widget=wid_1, error_messages={"required": "此字段不能为空"})
pwd = forms.CharField(min_length=6, label="密码", widget=wid_2, error_messages={"required": "此字段不能为空"})
r_pwd = forms.CharField(min_length=6, label="确认密码", widget=wid_2, error_messages={"required": "此字段不能为空"})
email = forms.EmailField(label="邮箱", widget=wid_1, error_messages={"required": "此字段不能为空", "invalid": "邮箱格式错误"})
phone = forms.CharField(label="手机号码", widget=wid_1, error_messages={"required": "此字段不能为空"})
局部钩子与全局钩子
当forms组件的检验规则不够使用时,需要用到钩子来自定义校验的内容
基本语法:
# 局部钩子
def clean_name(self):
if xxx:
return xxx
else:
raise ValidationError("xxx")
# 全局钩子
def clean(self):
if xxx:
return slef.cleaned_data
else:
raise ValidationError("xxx")
views.py
from django.shortcuts import render, HttpResponse
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from .models import UserInfo
wid_1 = widgets.TextInput(attrs={"class": "form-control"})
wid_2 = widgets.PasswordInput(attrs={"class": "form-control"})
class Form(forms.Form):
user = forms.CharField(min_length=4, label="用户名", widget=wid_1, error_messages={"required": "此字段不能为空", "min_length": "用户名长度至少四位"})
pwd = forms.CharField(min_length=6, label="密码", widget=wid_2, error_messages={"required": "此字段不能为空"})
r_pwd = forms.CharField(min_length=6, label="确认密码", widget=wid_2, error_messages={"required": "此字段不能为空"})
email = forms.EmailField(label="邮箱", widget=wid_1, error_messages={"required": "此字段不能为空", "invalid": "邮箱格式错误"})
phone = forms.CharField(label="手机号码", widget=wid_1, error_messages={"required": "此字段不能为空"})
def clean_phone(self):
val = self.cleaned_data.get("phone")
if len(val) == 11:
return val
else:
raise ValidationError("手机格式错误!")
def clean_user(self):
val = self.cleaned_data.get("user")
ret = UserInfo.objects.filter(name=val)
if not ret:
return val
else:
raise ValidationError("用户已经注册")
def clean(self):
pwd = self.cleaned_data.get("pwd")
r_pwd = self.cleaned_data.get("r_pwd")
if pwd and r_pwd:
if pwd == r_pwd:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致!")
else:
return self.cleaned_data
def register(request):
form = Form()
if request.method == "GET":
return render(request, "register.html", locals())
elif request.method == "POST":
form = Form(request.POST)
if form.is_valid():
print(form.cleaned_data)
return HttpResponse("OK")
else:
print(form.cleaned_data)
print(form.errors)
errors = form.errors.get("__all__") # 获取全局钩子捕获的错误信息
return render(request, "register.html", locals())
index.html
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
{% for foo in form %}
<div>
<label>{{ foo.label }}</label>
{{ foo }}
{% if foo.label == "确认密码" %}
<span class="pull-right" style="color: red">{{ errors.0 }}</span>
{% else %}
<span class="pull-right" style="color: red">{{ foo.errors.0 }}</span>
{% endif %}
</div>
{% endfor %}
<br>
<input type="submit" class="btn btn-default">
</form>
</div>
</div>
</div>
展示效果: