1、验证码
1、作用
为了更好的保障请求的合法性,防止无效访问,恶意访问,暴力破解等攻击。
包
pip install pillow
2、验证码的使用
导入第三方库
文件直接复制粘贴到自己项目的app中就可以
生成验证码
from django.shortcuts import render,HttpResponse
import random,string
from .captcha.image import ImageCaptcha #导入当前目录的验证码包
# Create your views here.
def captch(request):
image=ImageCaptcha() #生成验证码对象
code=random.sample(string.ascii_letters+string.digits,5) #随机产生五位数的验证码
code=''.join(code) #把列表拼接成字符串
print(code)
request.session['code']=code #存入页面的session中,为了以后的验证,注意session的作用。
data = image.generate(code) #生成验证码图片
return HttpResponse(data,'image/png') #返回给要验证码的界面
在HTML使用验证码
<input type="text" name="code">
<img src="{% url 'ems:captch' %}" id="image_code" width="80px" height="30px" align="center">
<a href="javascript:void(0)" onclick="change()">换一张</a>
<script>
function change() {
var url = "{% url 'capt:captch' %}?"+new Date().getTime() #根据时间戳更换验证码
$('#image_code').attr('src',url) //刷新验证码
}
</script>
验证验证码
def registlogic(request):
code = request.session.get('code')
if code.lower() == request.POST.get('code').lower():
return HttpResponse("成功")
else:
return HttpResponse("失败")
2、文件上传
1、简介
Django的模型类(django.db.models.Model)提供了两个FileField和ImageField用于上传文件和图片。而ImageField继承自FileField。使用Django的ImageField需要提前安装pillow模块。
2、使用用例,为用户上传头像
1、设置图片的保存路径
#settings.py
MEDIA_ROOT = os.path.join(BASE_DIR,"media") # 项目目录下的media目录 需要在项目目录下创建media目录
2、定义Model
class User(models.Model):
name = models.CharField(max_length=20)
#文件将存于 MEDIA_ROOT目录下的pics目录下
pic = models.ImageField(upload_to="pics")
生成移植文件并执行
3、定义form表单
<form action="{% url 'ems:uplogic_logic' %}" method="post" enctype="multipart/form-data"> #上传文件
{% csrf_token %}
用户名:<input type="text" name="name"><br>
头像:<input type="file" name="source">
<input type="submit" value="提交">
</form>
4、定义view函数
import uuid,os
def uplogic_logic(request):
try:
name = request.POST.get('name')
file = request.FILES.get('source')
file.name =str(uuid.uuid4())+os.path.splitext(file.name)[1] # 生成唯一文件名,防止上传文件相同的用户名
user = User.objects.create(name=name, pic=file)
return HttpResponse("上传成功")
except:
return HttpResponse("上传失败")
注意,此时数据库中存储的路径是相对于MEDIA_ROOT的路径
所以可以将MEDIA_ROOT设置为静态资源根目录,可便于后续的头像回显
5、回显图片
1、设置今天资源根目录
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
STATIC_URL = '/static/'
STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), MEDIA_ROOT ]
2、view函数
def query(request):
users = User.objects.all()
return render(request,'uploadfile_demo/detail.html',{'users':users})
3、模板中使用
{% load static %}
<img src="{% static user.headpic.url %}" width="50px">
users是view传入参数,headpic是列名,url是表示当前图片的路径
三、分页显示
Django自身提供了一些类来实现管理分页,数据被分在不同的页面中,并带有上一页下一页的标签,这个类叫做Paginator
2、Paginator分页器
pagtor = Paginator(User.objects.all(),per_page=3)
User.objects.all() 要分页的对象
per_age 每页显示的个数
属性
Paginator.count:每一页面的条数
Pagnator.num_pages:页面总数。
pagiator.page_range:页面范围,从1开始,例如[1,2,3,4]。
page对象
Paginator.page(number):根据参数number返回一个Page对象,表示第number页。
page = Paginator(User.objects.all(),per_page=3).page(1) # 获取第一页
也就是获取第一页所有对象的集合
page方法
Page.has_next() 如果有下一页,则返回True。
Page.has_previous() 如果有上一页,返回 True。
Page.has_other_pages() 如果有上一页或下一页,返回True。
Page.next_page_number() 返回下一页的页码。如果下一页不存在,抛出InvlidPage异常。
Page.previous_page_number() 返回上一页的页码。如果上一页不存在,抛出InvalidPage异常。
Page.start_index() 返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始。比如,将五个对象的列表分为每页两个对象,第二页的start_index()会返回3。
Page.end_index() 返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。 比如,将五个对象的列表分为每页两个对象,第二页的end_index() 会返回 4。
属性
Page.object_list当前页上所有对象的列表。
Page.number当前页的序号,从1开始。
Page.paginator相关的Paginator对象。
三、分页
# http://localhost:8000/page/index/?num=2
def index(request):
number = request.GET.get('num',1)
pagtor = Paginator(User.objects.all(),per_page=4)
page = pagtor.page(number) # 某一页的page对象
return render(request,'page_demo/index.html',{'page':page})
显示上一页下一页
{% if page.has_previous %}
<a href="{% url 'ems:show' %}?number={{ page.previous_page_number }}">上一页</a>
{% endif %}
{% for number in page.paginator.page_range %}
<a href="{% url 'ems:show' %}?number={{ number }}">{{ number }}</a>
{% endfor %}
{% if page.has_next %}
<a href="{% url 'ems:show' %}?number={{ page.next_page_number }}">下一页</a>
{% endif %}
四、中间件
作用
常用作view中冗余功能的抽取,如每个页面(或某些页面)在访问前强制登录。
减少代码冗余,提高代码的复用性。但是注意,中间件会把所有的都拦截
定义中间件
class MyMiddleware(MiddlewareMixin): # 自定义的中间件
def __init__(self,get_response):#初始化
super().__init__(get_response)
print("init1")
#view处理请求前执行
def process_request(self,request): #某一个view
print("request:",request)
#在process_request之后View之前执行
def process_view(self,request, view_func, view_args, view_kwargs):
print("view:",request,view_func,view_args,view_kwargs)
#view执行之后,响应之前执行
def process_response(self,request,response):
print("response:",request,response)
return response #必须返回response
#如果View中抛出了异常
def process_exception(self,request,ex):#View中出现异常时执行
print("exception:",request,ex)
激活中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
....
'emsapp.MyMiddleWate.MyMiddleware',# 注册自定义中间件,尽量放在最后注册
app名,文件名,函数名
]
强制登录实例
from django.shortcuts import redirect
from django.utils.deprecation import MiddlewareMixin
class MyMiddleware(MiddlewareMixin): # 自定义的中间件
def __init__(self, get_response): # 初始化
super().__init__(get_response)
print("init1")
# view处理请求前执行
def process_request(self, request): # 某一个view
print("request:", request)
# 在process_request之后View之前执行
def process_view(self, request, view_func, view_args, view_kwargs):
# print("view:", request, view_func, view_args, view_kwargs)
if 'login' in request.path or 'regist' in request.path or 'captch' in request.path:
pass
else:
res = request.session.get('login')
if res:
return redirect(request,'ems:show')
else:
return redirect(request,'ems:login')
# view执行之后,响应之前执行
def process_response(self, request, response):
print("response:", request, response)
return response # 必须返回response
# 如果View中抛出了异常
def process_exception(self, request, ex): # View中出现异常时执行
print("exception:", request, ex)
五、CSRF
作用
CSRF(Cross-site request forgery)跨站请求伪造,是一种常见的网络攻击手段。
使用
在表单的POST后加{% csrf_token %}
<form action="" method="post">
{% csrf_token %}
....
</form>
原理
1. 在发送请求时,通过CsrfViewMiddleware 在服务器端生成一个随机的token(令牌),在render渲染模板时存放于form单的隐藏域和cookie中
2. 当再次form发送请求时,会携带隐藏域令牌和cookie中的令牌,此时CsrfViewMiddleware 会先于View执行,去判断两块令牌是否一致,验明正身。
六、Admin管理后台
1、简介
Admin站点是Django有别于其它Web框架最重要的一点
admin通过读取你的模型数据,快速构造出一个可以对实际数据进行管理的Web站点
常用于开发测试,简单管理等场合,适用于部门内部为工作方便的场合,但不建议在生产环境中使用。
2、admin使用步骤
2.1 安装django.contrib.admin
确保,在settins.py中的INSTALLED_APPS 中安装了admin,它会扫描每个app的admin.py,进而支持后台管理。
INSTALLED_APPS = [
'django.contrib.admin', # 安装admin
'django.contrib.auth', # 下面四个为admin的依赖模块
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
...
]
2.2 注册Model
在App的admin.py中注册需要管理的Model类
admin.site.register(User)
2.3 创建管理员账户
python manage.py createsuperuser
2.4 启动服务
python manage.py runserver
2.5 访问后台管理页面
http://localhost:8000/admin
在URLconf中默认已经配置好admin的访问路径
urlpatterns = [
path('admin/', admin.site.urls),
...
]
3、定制admin
3.1 User(Models)管理页面
[外链图片转存失败(img-cpXUZJ3z-1568627975119)(.\Django-notes-pic\admin界面.png)]
修改后台列表显示User Object,改为更为直观的信息:
class User(models.Model):
name = models.CharField(max_length=30)
imagepath = models.ImageField(upload_to='Mr_lee')
def __str__(self): # 重写__str__方法
return self.name
[外链图片转存失败(img-A4CAhaB3-1568627975120)(.\Django-notes-pic\修改admin后台User显示.png)]
3.2 操作选项位置
- 定制前:
[外链图片转存失败(img-Vyc2786Y-1568627975121)(.\Django-notes-pic\操作选项位置.png)]
**补充:**注册admin的另一种方式 装饰器
@admin.register(User) # 等价于 admin.site.register(User)
class UserAdmin(admin.ModelAdmin):
#定制操作选项位置
actions_on_top = True
actions_on_bottom = True
- 定制后:
[外链图片转存失败(img-J2MLROWJ-1568627975121)(C:\Users\ADMINI~1\AppData\Local\Temp\1531814252335.png)]
3.3 定制列名
- 定制前:
[外链图片转存失败(img-wfarZbs9-1568627975122)(C:\Users\ADMINI~1\AppData\Local\Temp\1531814668417.png)]
- 定制后:
[外链图片转存失败(img-KXBmHm7A-1568627975123)(C:\Users\ADMINI~1\AppData\Local\Temp\1531814709514.png)]
- 代码:
class UserAdmin(admin.ModelAdmin):
list_display = ['name','imagepath'] # 定制显示的列名
- 定制列名的别名(可以是中文)
class User(models.Model):
name = models.CharField(max_length=30)
imagepath = models.ImageField(upload_to='Mr_lee')
def other_name(self):
return self.name
other_name.short_description = '姓名' # 改列名
other_name.admin_order_field = 'name' # 保证改列名后依然可以排序
def other_path(self):
return self.imagepath
other_path.short_description = '图片路径'
other_name.admin_order_field = 'imagepath'
# -- admin.py -- #
class UserAdmin(admin.ModelAdmin):
list_display = ['other_name','other_path'] # 定制显示的列名的别名 注意使用的是other_name
[外链图片转存失败(img-Wsi0kANL-1568627975124)(.\Django-notes-pic\列名别名.png)]
3.4 定制Model名
- 定制前:
[外链图片转存失败(img-MLRe7DlU-1568627975125)(.\Django-notes-pic\定制Model名)]
- 定制后:
[外链图片转存失败(img-KnbYuncK-1568627975126)(.\Django-notes-pic\model名)]
- 代码:
class User(models.Model):
...
class Meta:
verbose_name = "用户" # 修改Model名
verbose_name_plural = "用户" # 复数Model名 默认为users
3.5 定制过滤查询栏
- 定制前:
[外链图片转存失败(img-NjemSxPd-1568627975126)(.\Django-notes-pic\定制查询栏前)]
- 定制后:
[外链图片转存失败(img-9u9cdOhN-1568627975127)(.\Django-notes-pic\查询栏)]
- 代码:
class UserAdmin(admin.ModelAdmin):
list_display = ['other_name','other_path']
list_filter = ['name','imagepath'] # 定制过滤查询栏
3.6 定制分页
- 定制前:
[外链图片转存失败(img-fEgSy1tf-1568627975128)(.\Django-notes-pic\定制分页前)]
- 定制后:
[外链图片转存失败(img-7KPskagj-1568627975129)(E:\Python Web\Python-Web-Lee\05 Django\Django-notes-pic\分页)]
3.7 定制搜索框
- 定制前:
[外链图片转存失败(img-7KitTvRQ-1568627975130)(.\Django-notes-pic\定制搜索框前)]
- 定制后:
[外链图片转存失败(img-VGxI8MyI-1568627975130)(.\Django-notes-pic\搜索框)]
- 代码:
class UserAdmin(admin.ModelAdmin):
search_fields = ['name'] #可以以哪列作为条件搜索,会增加搜索框
3.8 定制可操作的列
- 定制前:
[外链图片转存失败(img-e52CUJ7v-1568627975131)(.\Django-notes-pic\定制可操作的列前)]
- 定制后:
[外链图片转存失败(img-9AGIm8bW-1568627975132)(.\Django-notes-pic\可操作的列)]
- 代码:
class UserAdmin(admin.ModelAdmin):
fields = [('name', 'imagepath')] #增加和修改页显示的列,且name和imagepath列在一行显示
3.9 定制分组显示
- 定制前:
[外链图片转存失败(img-8BZtMiJt-1568627975132)(.\Django-notes-pic\定制分组前)]
- 定制后:
[外链图片转存失败(img-1d4WJqG0-1568627975133)(.\Django-notes-pic\分组显示)]
- 代码:
class UserAdmin(admin.ModelAdmin):
...
fieldsets = (
('基本信息',{'fields':['name']}),
('头像',{'fields':['imagepath']})
)
3.10 关联显示
class Order(models.Model):
title = models.CharField(max_length=30)
price = models.FloatField()
user = models.ForeignKey(to=User,on_delete=models.CASCADE)
class OrderInline(StackedInline):
model = Order
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
inlines = [
OrderInline
]
七、Form(了解)
可以快速定制前端的form表单,并提供参数接收,数据验证。功能看似很全面
但实则使用价值不大,因为有众多前端框架,可以更专业的定制前端逻辑,而django如此定制有些越俎代庖,而且会影响前端框架的建设,无法和前端人员或前端技术对接(如:easyUI,bootstrap等)
所以,此章节请自学,了解即可!
class UserForm(forms.ModelForm):
class Meta:
model = User2
#fields=["name","age","gender"]
exclude=["salary2"]
def aa(request):
print("goto a template")
render(request,"xx.html",{"form":UserForm()})
xx.html
<form action = "xxx" method="post">
{% csrf_token %}
<table>
{{ form }}
<tr>
<td colspan="2" align="center"><input type="submit" value="Submit" /></td>
</tr>
</table>
</form>
def test3(request):
form = UserForm(request.POST)
if form.is_valid():
print("表单数据合法")
#{'name': 'zzz', 'age': 18, 'gender': True, 'birth': datetime.date(2018, 11, 12),...}
data = form.cleaned_data
print(data)
return render(...)