实例代码:
前端界面:article_list.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
head>
<body>
{% for article in articles %}
<p>{{ article.title }}-{{ article.content }}-{{ article.create_time }}p>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
{% if page_obj.has_previous %}
<li>
<a href="{% url 'list' %}?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
{% endif %}
{% if left_has_more == True %}
<li><a href="{% url 'list' %}?page=1">1a>li>
<li><a href="#">...a>li>
{% endif %}
{% for page in left_range %}
<li><a href="{% url 'list' %}?page={{ page }}">{{ page }}a>li>
{% endfor %}
<li><a href="#">{{ page_obj.number }}a>li>
{% if right_has_more == True %}
<li><a href="#">...a>li>
<li><a href="{% url 'list' %}?page={{ paginator.num_pages }}">{{ paginator.num_pages }}a>li>
{% endif %}
{% if page_obj.has_next %}
<li>
<a href="{% url 'list' %}?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
{% endif %}
ul>
nav>
body>
html>
视图文件:views.py
from django.shortcuts import render
from django.views.generic import ListView
from .models import Article
from django.http import HttpResponse
class ArticleListView(ListView):
model = Article
template_name = 'article_list.html'
paginate_by = 10
context_object_name = 'articles'
ordering = 'create_time'
page_kwarg = 'page'
'''
# def get_context_data(self, **kwargs):
# get_context_data的源码中包含有属性
# context = {
# 'paginator': paginator,
# 'page_obj': page,
# 'is_paginated': is_paginated,
# 'object_list': queryset
# }
# context = super(ArticleListView, self).get_context_data(**kwargs)
# count:总共有多少条数据。
# num_pages:总共有多少页。
# page_range:页面的区间。比如有三页,那么就range(1,4)。
# paginator = context.get('paginator')
# print(paginator.count) # 89
# print(paginator.num_pages) # 9
# print(paginator.page_range) # range(1, 10)
# return context
# has_next:是否还有下一页。
# has_previous:是否还有上一页。
# next_page_number:下一页的页码。
# previous_page_number:上一页的页码。
# number:当前页。
# start_index:当前这一页的第一条数据的索引值。
# end_index:当前这一页的最后一条数据的索引值。
# page = context.get('page_obj')
# print(page.has_next) # >
# print(page.has_previous) # >
# print(page.next_page_number) # >
# print(page.previous_page_number) # >
# print(page.number) # 5
# print(page.start_index) # >
# print(page.end_index) # >
# def get_queryset(self):
# return Article.objects.filter(id__lte=89)
'''
# 自己编写get_context_data源码,修改其属性
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
paginator = context.get('paginator')
page_obj = context.get('page_obj')
paginator_data = self.get_page(paginator, page_obj)
context.update(paginator_data)
return context
# 编写get_page方法, page_offset=2页码左右的偏移量
def get_page(self, paginator, page_obj, page_offset=2):
current_page = page_obj.number
# 左右页码
# left_range = range(current_page-page_offset, current_page) # range(9,11)---->9,10
# right_range = range(current_page+1, current_page+page_offset+1) # range(12,14)--->12,13
# 定义变量解决左右页码最前最后页码显示重复的问题
left_has_more = False
right_has_more = False
# 解决页码显示为0或者负值的问题
if current_page <= page_offset + 2:
left_range = range(1, current_page)
else:
left_range = range(current_page - page_offset, current_page) # range(9,11)---->9,10
# 当前页码 1....13 14 15 16 17....20
left_has_more = True
# 解决页码显示末尾的问题
if current_page >= paginator.num_pages - page_offset - 1: # 总页码-偏移量-1
right_range = range(current_page+1, paginator.num_pages+1) # range取不到最右边的值,所以+1
else:
right_range = range(current_page+1, current_page+page_offset+1) # range(12,14)--->12,13
right_has_more = True
# 返回数据
return {
"left_range": left_range,
"right_range": right_range,
"left_has_more": left_has_more,
"right_has_more": right_has_more,
}
def index(request):
for i in range(1,101):
article = Article(title='标题%s'% i, content='内容:%s' % i)
article.save()
return HttpResponse("index")
在一些网站开发中。经常会需要捕获一些错误,然后将这些错误返回比较优美的界面,或者是将这个错误的请求做一些日志保存。
404:服务器没有指定的url。
403:没有权限访问相关的数据。
405:请求的method错误。
400:bad request,请求的参数错误。
500:服务器内部错误,一般是代码出bug了。
502:一般部署的时候见得比较多,一般是nginx启动了,然后uwsgi有问题
在碰到比如404,500错误的时候,想要返回自己定义的模板(html文件名称只能是404.html或者500.html,否则定位不了)。那么可以直接在templates文件夹下创建相应错误代码的html模板文件。那么以后在发生相应错误后,会将指定的模板返回回去。
修改配置文件 settings.py
DEBUG = False
ALLOWED_HOSTS = ["127.0.0.1"]
对于404和500这种自动抛出的错误。我们可以直接在templates文件夹下新建相应错误代码的模板文件。而对于其他的错误,我们可以专门定义一个app,用来处理这些错误。
视图文件views.py
from django.http import HttpResponse
from django.shortcuts import render
def view_405(request):
return render(request,"errors/405.html",status=405)
绑定路由:urls.py
from django.urls import path
from . import views
urlpatterns = [
path("405",views.view_405,name="405")
]
单纯从前端的html来说,表单是用来提交数据给服务器的,不管后台的服务器用的是Django还是PHP语言还是其他语言。只要把input标签放在form标签中,然后再添加一个提交按钮,那么以后点击提交按钮,就可以将input标签中对应的值提交给服务器了。
Django中的表单丰富了传统的HTML语言中的表单。在Django中的表单,主要做以下两件事
在讲解Django表单的具体每部分的细节之前。我们首先先来看下整体的使用流程。
首先我们在后台服务器定义一个表单类,继承自django.forms.Form
# -*- encoding: utf-8 -*-
"""
@File : forms.py
@Time : 2020/7/17 16:27
@Author : chen
表单文件:forms.py
"""
from django import forms # 直接导入form
class MessageForm(forms.Form):
# 渲染到前端页面
title = forms.CharField(max_length=3, label='标题', min_length=2, error_messages={"min_length": '标题字符段不符合要求!'})
content = forms.CharField(widget=forms.Textarea, label='内容', error_messages={"required": 'content字段必须填写!'})
email = forms.EmailField(label='邮箱')
reply = forms.BooleanField(required=False, label='回复')
然后在视图中,根据是GET还是POST请求来做相应的操作。如果是GET请求,那么返回一个空的表单,如果是POST请求,那么将提交上来的数据进行校验。
from django.shortcuts import render, redirect
from django.views import View
from .forms import MessageForm
from django.http import HttpResponse
class IndexView(View):
def get(self, request):
form = MessageForm()
return render(request, "index.html", {"form": form})
def post(self, request):
form = MessageForm(request.POST)
if form.is_valid():
title = form.cleaned_data.get('title')
content = form.cleaned_data.get('content')
email = form.cleaned_data.get('email')
reply = form.cleaned_data.get('reply')
return HttpResponse("success")
else:
print(form.errors) # 报错信息
print(type(form.errors)) #
print(form.errors.get_json_data()) # {'title': [{'message': '标题字符段不符合要求!', 'code': 'min_length'}]}
return HttpResponse("fail")
在使用GET请求的时候,我们传了一个form给模板,那么以后模板就可以使用form来生成一个表单的html代码。在使用POST请求的时候,我们根据前端上传上来的数据,构建一个新的表单,这个表单是用来验证数据是否合法的,如果数据都验证通过了,那么我们可以通过cleaned_data来获取相应的数据。在模板中渲染表单的HTML。
index.html 前端界面
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>首页h1>
<form action="" method="post">
<table>
{{ form.as_table }}
<tr>
<td>td>
<td><input type="submit" value="提交">td>
tr>
table>
form>
body>
html>
我们在最外面给了一个form标签,然后在里面使用了table标签来进行美化,在使用form对象渲染的时候,使用的是table的方式,当然还可以使用ul的方式(as_ul
),也可以使用p标签的方式(as_p
),并且在后面我们还加上了一个提交按钮。这样就可以生成一个表单了。
使用Field可以是对数据验证的第一步。你期望这个提交上来的数据是什么类型,那么就使用什么类型的Field。
用来接收文本。
参数:
max_length:这个字段值的最大长度。
min_length:这个字段值的最小长度。
required:这个字段是否是必须的。默认是必须的。
error_messages:在某个条件验证失败的时候,给出错误信息。
用来接收邮件,会自动验证邮件是否合法。
错误信息的key:required、invalid。
用来接收浮点类型,并且如果验证通过后,会将这个字段的值转换为浮点类型。
参数:
max_value:最大的值。
min_value:最小的值。
错误信息的key:required、invalid、max_value、min_value。
用来接收整形,并且验证通过后,会将这个字段的值转换为整形。
参数:
max_value:最大的值。
min_value:最小的值。
错误信息的key:required、invalid、max_value、min_value。
用来接收url格式的字符串。
错误信息的key:required、invalid。
在验证某个字段的时候,可以传递一个validators参数用来指定验证器,进一步对数据进行过滤。验证器有很多,但是很多验证器我们其实已经通过这个Field或者一些参数就可以指定了。比如EmailValidator
,我们可以通过EmailField来指定,比如MaxValueValidator
,我们可以通过max_value
参数来指定。
MaxValueValidator:验证最大值。
MinValueValidator:验证最小值。
MinLengthValidator:验证最小长度。
MaxLengthValidator:验证最大长度。
EmailValidator:验证是否是邮箱格式。
URLValidator:验证是否是URL格式。
RegexValidator:如果还需要更加复杂的验证,那么我们可以通过正则表达式的验证器:RegexValidator。比如现在要验证手机号码是否合格,那么我们可以通过以下代码实现:
class MyForm(forms.Form):
telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='请输入正确格式的手机号码!')])
实例如下:
from django import forms # 直接导入form
from django.core import validators
class MessageForm(forms.Form):
# 正则表达式编写验证器
tel = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}", message="手机格式不正确")])
有时候对一个字段验证,不是一个长度,一个正则表达式能够写清楚的,还需要一些其他复杂的逻辑,那么我们可以对某个字段,进行自定义的验证。比如在注册的表单验证中,我们想要验证手机号码是否已经被注册过了,那么这时候就需要在数据库中进行判断才知道。对某个字段进行自定义的验证方式是,定义一个方法,这个方法的名字定义规则是:clean_fieldname。如果验证失败,那么就抛出一个验证错误。比如要验证用户表中手机号码之前是否在数据库中存在,那么可以通过以下代码实现。
# -*- encoding: utf-8 -*-
"""
@File : forms.py
@Time : 2020/7/17 16:27
@Author : chen
表单文件:forms.py
"""
from django import forms # 直接导入form
from django.core import validators
from .models import User
class MessageForm(forms.Form):
# 渲染到前端页面
title = forms.CharField(max_length=3, label='标题', min_length=2, error_messages={"min_length": '标题字符段不符合要求!'})
content = forms.CharField(widget=forms.Textarea, label='内容', error_messages={"required": 'content字段必须填写!'})
email = forms.EmailField(label='邮箱')
reply = forms.BooleanField(required=False, label='回复')
# 正则表达式编写验证器
tel = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}", message="手机格式不正确")])
pwd1 = forms.CharField(max_length=11)
pwd2 = forms.CharField(max_length=11)
# 手机号验证唯一性,针对某个字段进行自定义验证的命名必须是: clean_字段名
def clean_tel(self):
tel = self.cleaned_data.get('tel')
result = User.objects.filter(tel=tel).exists()
if result:
raise forms.ValidationError("号码已经存在")
return tel # 验证某个字段,就必须返回该字段
# 验证两次密码是否一致
def clean(self): # 当之前的所有字段都验证之后,才会执行这个clean函数
# 先调用父类中的clean方法,源码中返回的就是cleaned_data数据
cleaned_data = super().clean()
pwd1 = cleaned_data.get("pwd1")
pwd2 = cleaned_data.get("pwd2")
if pwd1 != pwd2:
raise forms.ValidationError("密码不一致")
return cleaned_data # 当验证多个字段时候,必须返回cleaned_data
对某个字段进行验证,如果验证数据的时候,需要针对多个字段进行验证,那么可以重写clean方法。比如要在注册的时候,要判断提交的两个密码是否相等。
注意:
如果验证失败了,那么有一些错误信息是我们需要传给前端的。这时候我们可以通过以下属性来获取:
form.errors
:这个属性获取的错误信息是一个包含了html标签的错误信息。form.errors.get_json_data()
:这个方法获取到的是一个字典类型的错误信息。将某个字段的名字作为key,错误信息作为值的一个字典。form.as_json()
:这个方法是将form.get_json_data()返回的字典dump成json格式的字符串,方便进行传输。{'username': [{'message': 'Enter a valid URL.', 'code': 'invalid'}, {'message': 'Ensure this value has at most 4 characters (it has 22).', 'code': 'max_length'}]}
那么如果我只想把错误信息放在一个列表中,而不要再放在一个字典中。这时候我们可以定义一个方法,把这个数据重新整理一份。
重新编写form.errors.get_json_data()
的输出错误信息;通过print(forms.get_errors)
输出错误信息;这样就可以把某个字段所有的错误信息直接放在这个列表中。
class MyForm(forms.Form):
username = forms.URLField(max_length=4)
def get_errors(self):
errors = self.errors.get_json_data()
new_errors = {}
for key,message_dicts in errors.items():
messages = []
for message in message_dicts:
messages.append(message['message'])
new_errors[key] = messages
return new_errors