Django扩展
一、验证码
1、简介
1. 为什么要使用验证码:
在form中常用的一个组件,目的是为了更好地保障请求的合法性,防止恶意、无效的访问,恶意注册,暴力破解等
2. 验证码实现原理:
在服务器端,生成一个随机的码"DNC1",将随机码画到一张图片中(加噪点),再图片显示在页面上。
3. 第三方包:pillow
pip install pillow - 是一个关于图片的第三方模块
2、使用步骤
从验证码的原始文件中,将captcha的包导入自己的项目的app目录下
写一个视图函数,生成验证码
def get_captcha(request):
# 1. 声明一个图片验证码对象
image = ImageCaptcha()
# 2. 生成随机码
code = random.sample(string.ascii_letters+string.digits,4)
code = ''.join(code)
# 3. 将码写入图片中
data = image.generate(code) # 返回:二进制数据
print(code)
return HttpResponse(data,'image/png')
在html中加入验证码
<form action="">
用户名:<input type="text" name="username"> <br>
密码:<input type="password" name="password"> <br>
验证码:<input type="text" name="captcha">
<!-- 加入验证码 -->
<img src="{% url 'captcha:get_captcha' %}" alt="" width="80"> <br>
<input type="submit" value="注册">
</form>
看不清,换一张
<script>
function changeCaptcha() {
var img = document.getElementsByTagName('img')[0]
img.src = "{% url 'captcha:get_captcha' %}" + "?"+new Date().getTime()
}
</script>
<a href="javascript:void(0)" onclick="changeCaptcha()">看不清,换一张</a>
验证是否正确
# def get_captcha(request):
# 在随机产生码后,将码存入session中,以便后续验证的时候使用
# request.session['code'] = code
def register_logic(request):
captcha = request.POST.get('captcha')
if captcha.lower() == request.session.get('code').lower():
# ...
return HttpResponse('验证码正确')
return HttpResponse('验证码错误')
二、文件上传
1、简介
1. 在django中要实现文件或图片的上传,直接使用FileField和ImageField字段即可。而ImageField是继承之FileField.
2. 如果要给某个用户上传一个头像:
class User(models.Model):
name = models.CharField(max_length=20)
age = models.SmallIntegerField()
picture = models.ImageField(upload_to='pics')
3. 在使用ImageField字段之前,需要安装一个第三方模块:pillow
4. 在数据库中并不会直接存储图片/文件本身,存储的是图片的路径-字符串
5. 需要在settings.py文件中,配置头像/文件的实际存储位置:
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
2、案例:为用户上传头像
配置头像的存储位置并安装pillow
# settings.py中
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
# 在terminal中
pip install pillow
定义model类
class User(models.Model):
name = models.CharField(max_length=20)
age = models.SmallIntegerField()
salary = models.DecimalField(max_digits=7, decimal_places=2)
birthday = models.DateTimeField()
head_picture = models.ImageField(upload_to='pics')
# 记得执行迁移操作
定义form表单
<!-- form表单提交文件时,必须加enctype属性 值为 multipart/form-data --><form action="{% url 'uploadapp:register_logic' %}" method="post" enctype="multipart/form-data"> {% csrf_token %} 用户名:<input type="text" name="username"> <br><br> 年龄:<input type="number" name="age"> <br><br> 薪水:<input type="number" name="salary"> <br><br> 生日:<input type="date" name="birthday"> <br><br> 头像:<input type="file" name="head_picture"> <br><br> <input type="submit" value="提交"></form>
定义view视图函数
def register_logic(request): try: username = request.POST.get('username') age = request.POST.get('age') salary = request.POST.get('salary') birthday = request.POST.get('birthday') # head_picture = request.POST.get('head_picture') #################### 接收文件时 使用request.FILES ########################### head_picture = request.FILES.get('head_picture') print(username, age, salary, birthday, head_picture) with transaction.atomic(): User.objects.create(name=username, age=age, salary=salary, birthday=birthday, head_picture=head_picture) return HttpResponse('提交成功') except Exception as e: print(e) return HttpResponse('提交失败')
修改头像的名字
import uuid# 获取原文件的扩展名 extendextend = os.path.splitext(head_picture.name)[1]# 改文件名head_picture.name = str(uuid.uuid4()) + extend
-
头像回显
- 设置静态资源查找目录
# settings.py文件中,将media目录添加为静态资源的查找路径STATICFILES_DIRS = [os.path.join(BASE_DIR,'static'),MEDIA_ROOT]
- 在模板文件中,设置img标签
<img src="/static/{{ user.head_picture }}" alt="" height="50px">或 将静态资源的访问路径改为软编码方式{% load static %}<img src="{% static user.head_picture.url %}" alt="" height="50px">
三、分页显示
1、简介
当页面中的数据过多时,需要进行分页展示,在页面上需要有相应的页号,上一页,下一页等功能,点击相应的页号时,跳转到对应的页。在django中,提供了一个类Paginator(分页器),来进行分页操作。
2、分页器Paginator
1. 第一步:初始化一个分页器对象 pagtor = Paginator(object_list=要分页的数据的'列表'-可迭代对象, per_page=每页要显示的数据个数)
# 源代码:class Paginator: def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True): self.object_list = object_list self._check_object_list_is_ordered() self.per_page = int(per_page) self.orphans = int(orphans) self.allow_empty_first_page = allow_empty_first_page
- 分页器对象的属性
声明分页器对象
pagtor = Paginator(object_list=users,per_page=3)
1. pagtor.count 获取所有页面的对象总数 - 也就是object_list参数中的元素的个数2. pagtor.num_pages 获取总页数 3. pagtor.page_range 返回页面的范围 range(1,num_pages+1)
- 分页器对象的方法
# 重要的一个方法 page = pagtor.page(number) # 调用page方法,获取某一页的页面对象,page = pagtor.page(1)
3、页面对象
# 获取页面对象 page = pagtor.page(number)
- 页面对象的属性
1. page.object_list 返回当前页面中的所有的数据对象 2. page.number 返回当前的页号-页码 3. page.paginator page.paginator.num_pages 返回当前页面所属的分页器对象
- 页面对象的方法
1. page.has_next() 是否有下一页,返回为bool类型 2. page.has_previous() 是否有上一页,返回为bool类型 3. page.has_other_pages() 是否有其它页,如果它有上一页或有下一页,则返回为True 4. page.next_page_number() 返回下一页的页号 page.number+1 如果不存在 则抛出异常 5. page.previous_page_number() 返回上一页的页号 page.number-1 如果不存在 则抛出异常 6. page.start_index() 返回当前页的起始索引7. page.end_index() 返回当前页的结束索引
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UyBoGq32-1644897742002)(Django07_实用扩展.assets/image-20210228145049860.png)]
4、分页案例
def userlist(request): users = User.objects.all() # 获取url中的参数,如果不存在返回第1页 number = int(request.GET.get('number',1)) # 声明分页器对象 pagtor = Paginator(object_list=users,per_page=3) # 判断页号是否存在 if number not in pagtor.page_range: number=1 page = pagtor.page(number) # 向后端传递的是页面对象 return render(request,'uploadapp/userlist.html',{'page':page})
<!--遍历页面对象的数据--> {% for user in page.object_list %} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.age }}</td> <td>{{ user.salary }}</td> <td>{{ user.birthday|date:'Y-m-d' }}</td> <td> <img src="{% static user.head_picture.url %}" alt="" height="50px"> </td> </tr> {% endfor %}
<!--实现上一页和下一页 及 页号--> {% if page.has_previous %} <a href="{% url 'uploadapp:userlist' %}?number={{ page.previous_page_number }}">上一页</a>{% endif %}{% for n in page.paginator.page_range %} {% if page.number == n %} <a class="b" href="{% url 'uploadapp:userlist' %}?number={{ n }}">{{ n }}</a> {% else %} <a class="a" href="{% url 'uploadapp:userlist' %}?number={{ n }}">{{ n }}</a> {% endif %}{% endfor %}{% if page.has_next %} <a href="{% url 'uploadapp:userlist' %}?number={{ page.next_page_number }}">下一页</a>{% endif %}
/*a标签样式*/ <style> .a { width: 20px; height: 20px; border: 1px solid #e1e2e3; cursor: pointer; display: inline-block; text-align: center; line-height: 20px; } .b { border: 0; width: 20px; height: 20px; cursor: pointer; display: inline-block; text-align: center; line-height: 20px; } a { text-decoration: none; }</style>
四、中间件
1、简介
1. 概念 中间件(Middleware)用于在http请求到达`视图函数之前`及`视图返回响应之后`的期间,django会根据自己的规则在合适的时机执行一些方法(中间件中的一些方法)。2. 中间件的作用 常用于抽取view中冗余的功能代码,如每个页面在访问时都需要进行`强制登录` 以前是每个视图中都行强制登录判断,如果加了中间件,就可以将判断放在中间件中。所有请求都先经过中间件,中间件中检测登录状态,有登录状态,请求正常执行(放行),否则在中间件中重定向到登录页面。
2、定义中间件
# 在任意目录(项目根目录或app目录)新建一个py文件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): pass # 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中出现异常时执行 pass # print("exception:", request, ex)
3、激活中间件
# 在settings.py文件中MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'uploadapp.mymiddleware.MyMiddleware',]
4、强制登录实例
def home(request): return render(request, 'account/home.html')
def process_request(self, request): # 将需要放行的URL存储一个列表中 pass_url = [reverse('account:login'),reverse('account:register')] # 如果请求路径 在 放行的URL的列表中 if request.path in pass_url: pass # 什么都不做 else: is_login = request.session.get('is_login') if is_login: pass else: return redirect('account:login')
五、CSRF
1、简介
CSRF(Cross-site request forgery)跨站请求伪造,是一种常见网络攻击手段。
2、基本使用
django本身提供了防范CSRF攻击的机制具体做法: 在form表单的post请求中, <form action='xx' method='post'> {% csrf_token %} xxx </form>
3、实现原理
1. 在form表单中,添加了csrf_token,在渲染页面时,django会通过CsrfViewMiddleware在服务端生成一个随机的token(令牌),放在form表单的隐藏域中及cookie中。 <input type='hidden' name='csrfmiddlewaretoken' value='82KgpReeNNQh2PPICbVXxhqo3Q6dPE195rudsNSOgoWLcKOJcafvZoCmYbdZ3S2R' /> 2. 当提交表单请求,request对象会携带cookie中的令牌 及form表单提交的隐藏域中的令牌到达服务器,然后django服务器的 CsrfViewMiddleware中间件会先验证判断两块令牌是否一致。
六、Admin管理后台
1. 前端页面-客户 2. 后台管理页面-管理员 3. 后端程序(程序员/公司的运维)之前:前端页面 + 后端程序以后:为了网站的管理和维护,我们需要给网站写一个后台管理页面 # 只要是使用django编写的项目,django框架会自动生成后台管理页面
1. 创建管理员帐户 python manage.py createsuperuser 2. 在app的admin.py中注册model类 admin.site.register(model类名) 3. 访问管理页面 http://127.0.0.1:8000/admin 即可访问 4. 修改管理页面为中文settings.py LANGUAGE_CODE = 'zh-hans' 5. 可以自己定制admin的后台管理页面的界面- 符合django的规则 或不使用admin管理页面,自己去写