美文网首页
第3章 Django视图

第3章 Django视图

作者: 无罪的坏人 | 来源:发表于2018-08-16 22:32 被阅读0次

    内容来源于《Web接口开发与自动化测试——基于Python语言》虫师编著,如有涉及版权问题,归虫师本人所有。请大家支持虫师的著作:http://www.broadview.com.cn/book/4811
    源码下载:https://github.com/defnngj/guest

    目标:用Django开发一个发布会签到系统。

    3.1 来写个登录功能

    打开.../sign/templates/index.html文件,开发一个登录表单。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Django Page</title>
    </head>
    <body>
        <h1>发布会管理</h1>
        <form>
            <input name="username" type="text" placeholder="username" ><br>
            <input name="password" type="password" placeholder="password"><br>
            <button id="btn" type="submit">登录</button>
        </form>
    
    </body>
    </html>
    

    启动Django服务,访问:http://127.0.0.1:8000/index/,如下图所示。

    登录功能.png

    3.1.1 GET和POST请求

    当客户端通过HTTP协议向服务器提交请求时,最常用的方法就是GET和POST。

    • GTE:从指定的资源请求数据。
    • POST:向指定的资源提交要被处理的数据。

    1. GET请求

    先来看看GET方法是如何传参的,给form表单添加属性 method="get"。

    ......
        <form method="get">
            <input name="username" type="text" placeholder="username" ><br>
            <input name="password" type="password" placeholder="password"><br>
            <button id="btn" type="submit">登录</button>
        </form>
    ......
    

    保存在index.html文件,刷新登录页面,输入账号密码admin/admin123,单击“登录”按钮。
    查看浏览器URL地址栏:
    http://127.0.0.1:8000/index/?username=admin&password=admin123
    GET方法会将用户提交的数据添加到URL地址中,路径后面跟问号“?”。后面跟的是用户名admin和密码admin123,用“&”分隔开。

    2. POST请求

    再次将form表单中的属性改为method="post",刷新页面,输入账号密码,发现报错了。


    CSRF verification failed. Request aborted.png

    这是跨站请求伪造(CSRF)漏洞。Django针对CSRF的保护措施是在生成的每个表单中放置一个自动生成的令牌,通过令牌判断POST请求是否来自同一个网站。
    接下来网form表单中添加CSRF令牌,添加{% csrf_token %}

    .......
        <form method="post">
            <input name="username" type="text" placeholder="username" ><br>
            <input name="password" type="password" placeholder="password"><br>
            <button id="btn" type="submit">登录</button>
            {% csrf_token %}
        </form>
    ......
    

    刷新后发现登录页面的报错消失了。
    借助工具查看POST请求,发现请求参数中多了一个csrfmiddlewaretoken参数。


    查看POST请求.png

    如果想忽略该检查,可以在settings.py文件中注释掉csrf。

    ......
    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',
    ]
    ......
    

    3.1.2 处理登录请求

    通过form表单的action属性来指定提交的路径。打开index.html,添加内容:

    ......
        <form method="post" action="/login_action/">
    ......
    

    当我们写好账号密码时,点击登录按钮,由http://127.0.0.1:8000/login_action/路径来提交登录请求。所以,在usrls.py中添加login_action/的路由。

    ......
    urlpatterns = [
    ......
        re_path(r'login_action/$', views.login_action),
    ]
    

    登录请求由views.py视图文件中的login_action函数来处理,打开sign/views.py,创建login_action视图函数。

    ......
    def login_action(request):
        if request.method == 'POST':
            username = request.POST.get('username', '')
            password = request.POST.get('password', '')
            if username == 'admin' and password == 'admin123':
                return HttpResponse('login success!')
            else:
                return render(request, 'index.html', {'error': 'username or password error!'})
    

    通过login_action函数来处理登录请求。
    但是发现登录页面病没有显示错误提示,打开index.html修改:

    ......
    <h1>发布会管理</h1>
        <form method="post" action="/login_action/">
            <input name="username" type="text" placeholder="username" ><br>
            <input name="password" type="password" placeholder="password"><br>
            {{error}}<br>
            <button id="btn" type="submit">登录</button>
            {% csrf_token %}
        </form>
    ......
    

    可以看出错误提示放在哪个地方了。


    登录失败.png
    登录成功.png

    3.1.3 登录成功页

    接下来需要把“login success”字符串换成HTML页面。所以,先创建.../templates/event_manage.html页面。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Event Manage Page</title>
    </head>
    <body>
        <h1>Login Success!</h1>
    </body>
    </html>
    

    打开views.py文件,修改内容如下:

    ......
    from django.http import HttpResponse, HttpResponseRedirect
    ......
    # 登录动作
    def login_action(request):
        if request.method == 'POST':
            username = request.POST.get('username', '')
            password = request.POST.get('password', '')
            if username == 'admin' and password == 'admin123':
                return HttpResponseRedirect('/event_manage/')
            else:
                return render(request, 'index.html', {'error': 'username or password error!'})
    
    # 发布会管理
    def event_manage(request):
        return render(request, "event_manage.html")
    

    此处用了一个新的类HttpResponseRedirect,要先导入,它可以对路径进行重定向,从而将登录成功之后的请求指向/event_manage/目录,即:http://127.0.0.1:8000/event_manage/
    创建event_manage函数,用于返回发布会管理页面event_manage.html。
    最后不要忘记在urls.py中添加event_manage/的路由。

    ......
    urlpatterns = [
    ......
        re_path(r'^event_manage/$', views.event_manage),
    ]
    

    登录成功了!!!


    登录成功跳转到event_manage.html.png

    3.2 Cookie和Session

    • Cookie机制:Cookie分发通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。
    • Session机制:session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

    3.2.1 Cookie的使用

    修改views.py文件。

    ......
    # 登录动作
    def login_action(request):
        if request.method == 'POST':
            username = request.POST.get('username', '')
            password = request.POST.get('password', '')
            if username == 'admin' and password == 'admin123':
                response = HttpResponseRedirect('/event_manage/')
                response.set_cookie('user', username, 3600)  # 添加浏览器cookie
                return response
            else:
                return render(request, 'index.html', {'error': 'username or password error!'})
    
    # 发布会管理
    def event_manage(request):
        username = request.COOKIES.get('user', '')  # 读取浏览器cookie
        return render(request, "event_manage.html", {"user": username})
    

    当用户登录成功后,在跳转到event_manage视图函数的过程中,通过set_cookie()方法向浏览器中添加Cookie信息。
    set_cookie传了三个参数:第一个参数"user"用于表示写入浏览器的Cookie名,第二个参数是用户输入的用户名,第三个参数3600表示设置Cookie信息在浏览器中的保持时间,单位是秒。
    在event_manager视图函数中,通过request.COOKIES来读取Cookie名为“user”的值,并且通过render将它和event_manage.html页面一起返回。
    接下来修改event_manage.html。

    ......
    <body>
        <div style="float: right;">
            <a>嘿!{{user}} 欢迎</a><hr/>
        </div>
        <h1>Login Success!</h1>
    </body>
    ......
    

    重新登录,界面如下图:


    登录页面显示Cookie里的用户名.png
    Cookie信息.png

    3.2.2 Session的使用

    在Django中使用Session和Cookie类似,但是Session更安全。
    修改views.py文件。

    ......
    # 登录动作
    def login_action(request):
        if request.method == 'POST':
            username = request.POST.get('username', '')
            password = request.POST.get('password', '')
            if username == 'admin' and password == 'admin123':
                response = HttpResponseRedirect('/event_manage/')
                # response.set_cookie('user', username, 3600)  # 添加浏览器cookie
                request.session['user'] = username  # 将Session信息记录到浏览器
                return response
            else:
                return render(request, 'index.html', {'error': 'username or password error!'})
    
    # 发布会管理
    def event_manage(request):
        # username = request.COOKIES.get('user', '')  # 读取浏览器cookie
        username = request.session.get('user', '')  # 读取浏览器session
        return render(request, "event_manage.html", {"user": username})
    

    还需要创建django_session表。

    \guest > python3 manage.py migrate
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, sessions
    Running migrations:
      Applying contenttypes.0001_initial... OK
      Applying auth.0001_initial... OK
      Applying admin.0001_initial... OK
      Applying admin.0002_logentry_remove_auto_add... OK
      Applying admin.0003_logentry_add_action_flag_choices... OK
      Applying contenttypes.0002_remove_content_type_name... OK
      Applying auth.0002_alter_permission_name_max_length... OK
      Applying auth.0003_alter_user_email_max_length... OK
      Applying auth.0004_alter_user_username_opts... OK
      Applying auth.0005_alter_user_last_login_null... OK
      Applying auth.0006_require_contenttypes_0002... OK
      Applying auth.0007_alter_validators_add_error_messages... OK
      Applying auth.0008_alter_user_username_max_length... OK
      Applying auth.0009_alter_user_last_name_max_length... OK
      Applying sessions.0001_initial... OK
    

    因为Django已经默认设置SQLite3,可以通过查看settings.py文件中查看SQLite3数据库的配置。

    # Database
    # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    

    此时在guest项目根目录下已经生成了一个db.sqlite3的数据库文件。来看看验证Session功能是否生效,重新登录。


    查看Sessionid.png

    3.3 Django认证系统

    本节会使用Django的认证系统来实现真正的用户信息验证。之前的验证都是写死的,只是简单判断用户名和密码是不是admin/admin123。

    3.3.1 登录Admin后台

    先创建登录Admin后台的管理员账号。

    (venv) liujindeMacBook-Pro:guest liujin$ python3 manage.py createsuperuser
    Username (leave blank to use 'liujin'): admin
    Email address: admin@email.com
    Password: 
    Password (again): 
    Superuser created successfully.
    

    创建的超级管理员账号密码是:admin/admin123456。
    Admin的后台地址是:http://127.0.0.1:8000/admin/
    通过创建的超管账号登录,并用新创建的用户再次登录后台(要给权限才能登录)。

    Admin管理后台登录.png
    Admin管理后台.png

    3.3.2 引用Django认证登录

    Django已经给我们封装好了用户认证和登录的相关办法,只需要拿来用即可。并且,同样适用auth_user表中的数据进行验证。
    修改views.py文件。

    # 登录动作
    def login_action(request):
        if request.method == 'POST':
            username = request.POST.get('username', '')
            password = request.POST.get('password', '')
            user = auth.authenticate(username=username, password=password)
            if user is not None:
                auth.login(request, user)  # 登录
                request.session['user'] = username  # 将Session信息记录到浏览器
                response = HttpResponseRedirect('/event_manage/')
                return response
            else:
                return render(request, 'index.html', {'error': 'username or password error!'})
    

    使用authenticate()函数认证给出的用户名和密码,它接受两个参数:username和password,并且会在用户名密码正确的情况下返回一个user对象,否则authenticate()返回None。
    通过if语句判断authenticate()返回对象,如果不为None,则说明用户认证通过,调用login()函数进行登录。login()函数接收HttpRequest对象和一个user对象。
    使用之前创建的账号就能登录了。在这里:http://http://127.0.0.1:8000/index/

    3.3.3 关上窗户

    现在访问http://127.0.0.1:8000/event_manage/是不是发现直接登录成功了,那为什么还需要账号登录呢?因此需要把有些窗户关上,让用户只能登录才能访问。
    修改views.py。

    ......
    from django.contrib.auth.decorators import login_required
    .......
    # 发布会管理
    @login_required()
    def event_manage(request):
        # username = request.COOKIES.get('user', '')  # 读取浏览器cookie
        username = request.session.get('user', '')  # 读取浏览器session
        return render(request, "event_manage.html", {"user": username})
    ......
    

    只需要在这个函数前加上@login_required()即可。
    此时清浏览器缓存,再次访问http://127.0.0.1:8000/event_manage/发现报Page not found 404。

    Page not found.png
    仔细看这个URL,发现默认跳转到了包含“/accounts/login/”,为什么不让它直接到登录页面呢?
    接下来修改urls.py文件,新增路径配置。
    urlpatterns = [
    ......
        re_path(r'^$', views.index),
        re_path(r'^account/login/$', views.index)
    ]
    

    此时,你访问:
    http://127.0.0.1:8000/
    http://127.0.0.1:8000/index/
    http://127.0.0.1:8000/event_manage/
    都会跳转到登录界面。


    相关文章

      网友评论

          本文标题:第3章 Django视图

          本文链接:https://www.haomeiwen.com/subject/hzqgbftx.html