验证码、文件上传和中间件

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(...)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值