Python学习之路—2018/7/3

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">&laquo;</span>
                </a>
            </li>
        {% else %}
            <li class="disabled">
                <a href="#" aria-label="Previous" class="disabled">
                    <span aria-hidden="true">&laquo;</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">&raquo;</span>
                </a>
            </li>
        {% else %}
            <li class="disabled">
                <a href="#" aria-label="Next" class="disabled">
                    <span aria-hidden="true">&raquo;</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>

展示效果:

猜你喜欢

转载自www.cnblogs.com/ExBurner/p/9261156.html