美文网首页
【Django在线教育平台】04.模板配置,完成用户登录相关页面

【Django在线教育平台】04.模板配置,完成用户登录相关页面

作者: 吾星喵 | 来源:发表于2019-06-04 14:31 被阅读0次

    专题:Django2.0.8+xadmin2实现在线学习网站

    Django2.0.8+xadmin2实现在线学习网站,课程、讲师、机构、用户收藏功能。GitHub地址:https://github.com/xyliurui/OnlineLearningPlatform ;Django版本:2.0.8

    更多内容请点击 我的博客 查看,欢迎来访。

    模板和资源位置

    在项目中创建templates目录,用于放置模板文件

    在项目中创建static目录,用来存放css, js等静态文件

    配置templates目录路径

    在项目下新建templates目录用于存放模板文件,在settings.py中添加templates的目录,让它能被搜索到。

    修改settings.py配置,添加模板文件的目录

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

    配置static目录路径

    用于放置静态文件。修改settings.py配置

    STATIC_URL = '/static/'
    
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static')
    ]
    

    创建临时的首页index.html

    在templates目录下,创建index.html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        暂无内容
    </body>
    </html>
    

    然后配置首页url,在项目主url中,使用Django提供的TemplateView用于处理静态文件

    from django.views.generic import TemplateView
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('xadmin/', xadmin.site.urls),
        path('', TemplateView.as_view(template_name='index.html'), name='index'),
    ]
    

    访问 http://127.0.0.1:8000/ 即可看到首页的内容

    创建登录

    登录login.html

    创建登录模板文件

    url配置跳转登录页面

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('xadmin/', xadmin.site.urls),
        path('', TemplateView.as_view(template_name='index.html'), name='index'),
        path('login/', TemplateView.as_view(template_name='login.html'), name='login'),
    ]
    

    访问 http://127.0.0.1:8000/login/ 即可访问登录页面

    使用函数实现用户登录user_login(request)

    配置url之前我们要书写好对应处理的view

    Django的view实际就是一个函数,接收request请求对象,处理后返回response对象。

    用户登录视图

    users/views.py

    from django.shortcuts import render
    
    
    # 当我们配置url被这个view处理时,自动传入request对象
    def user_login(request):
        if request.method == 'POST':
            pass
        elif request.method == 'GET':
            return render(request, 'login.html', {})
    

    用户登录url

    在项目的主url中添加

    from users.views import user_login
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('xadmin/', xadmin.site.urls),
        path('', TemplateView.as_view(template_name='index.html'), name='index'),
        # path('login/', TemplateView.as_view(template_name='login.html'), name='login'),
        path('login/', user_login, name='login'),
    ]
    

    现在访问 http://127.0.0.1:8000/login/ 仍会跳转到同一个页面

    用户登录表单

    templates/login.html

    可以看到form表单中有input。点击提交会把值提交到后台。我们需要修改action让它指向我们的后台相应地址。input中的name值会被传递到后台。回组成键值对形式。

    <h3 class="no-margins">登录</h3>
    <p class="m-t-md">登录到学习平台</p>
    <form method="post" action="/login/">
        <input name='username' type="text" class="form-control uname" placeholder="用户名" />
        <input name='password' type="password" class="form-control pword m-b" placeholder="密码" />
        {% csrf_token %}
        <a href="">忘记密码了?</a>
        <button type="submit" class="btn btn-success btn-block">登录</button>
    </form>
    

    BLOG_20190604_135101_16

    html页面内必须加上crsf token 才能传值到后台。

    系统会随机的给前端发一串符号,必须把这串符号带回来,才允许post

    用户登录post逻辑

    from django.shortcuts import render
    from django.contrib.auth import login, authenticate
    
    
    # 当我们配置url被这个view处理时,自动传入request对象
    def user_login(request):
        if request.method == 'POST':
            user_name = request.POST.get('username', '')
            pass_word = request.POST.get('password', '')
    
            user = authenticate(username=user_name, password=pass_word)
    
            # 认证成功返回user对象,失败返回null
            if user:
                login(request, user)
                return render(request, 'index.html')
            else:
                return render(request, 'login.html', {})
    
        elif request.method == 'GET':
            return render(request, 'login.html', {})
    

    支持邮箱登录

    自定义authenticate方法

    users/views/py

    from django.contrib.auth.backends import ModelBackend
    from django.db.models import Q
    
    from .models import UserProfile
    
    
    # 自定义登录,可使用邮箱和账号
    class CustomBackend(ModelBackend):
        def authenticate(self, request, username=None, password=None, **kwargs):
            try:
                # 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
                user = UserProfile.objects.get(Q(username=username) | Q(email=username))
    
                # django的后台中密码加密:所以不能password==password
                # UserProfile继承的AbstractUser中有def check_password(self, raw_password)
    
                if user.check_password(password):
                    return user
            except Exception as e:
                return None
    

    添加登录后台设置

    修改settings.py,添加

    # 设置邮箱和用户名均可登录
    AUTHENTICATION_BACKENDS = (
        'users.views.CustomBackend',
    )
    

    添加登录错误提示

    模板添加错误信息提示

    <form method="post" action="/login/">
        <input name='username' type="text" class="form-control uname" placeholder="用户名" />
        <input name='password' type="password" class="form-control pword m-b" placeholder="密码" />
    
        {% if msg %}
            <div class="alert alert-danger" style="padding: 5px;">
                    {{ msg }}
            </div>
        {% endif %}
    
        {% csrf_token %}
        <a href="">忘记密码了?</a>
        <button type="submit" class="btn btn-success btn-block">登录</button>
    </form>
    

    视图添加错误信息返回

    def user_login(request):
        if request.method == 'POST':
            user_name = request.POST.get('username', '')
            pass_word = request.POST.get('password', '')
    
            user = authenticate(username=user_name, password=pass_word)
    
            # 认证成功返回user对象,失败返回null
            if user:
                login(request, user)
                return render(request, 'index.html')
            else:
                return render(request, 'login.html',
                              {
                                  'msg': '用户名或密码错误!'
                              })
    
        elif request.method == 'GET':
            return render(request, 'login.html', {})
    

    基于类的视图实现用户登录LoginView(View)

    基础教程中基本上都是基于函数来做的,其实更推荐基于类来做。基于类可以带来不少好处

    基于类的登录视图LoginView(View)

    from django.views.generic import View
    
    
    # 基于类的视图实现登录
    class LoginView(View):
        def get(self, request):
            return render(request, 'login.html', {})
    
        def post(self, request):
            user_name = request.POST.get('username', '')
            pass_word = request.POST.get('password', '')
    
            user = authenticate(username=user_name, password=pass_word)
    
            # 认证成功返回user对象,失败返回null
            if user:
                login(request, user)
                return render(request, 'index.html')
            else:
                return render(request, 'login.html',
                              {
                                  'msg': '用户名或密码错误!'
                              })
    

    基于类的登录url

    from users.views import user_login, LoginView
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('xadmin/', xadmin.site.urls),
        path('', TemplateView.as_view(template_name='index.html'), name='index'),
        # path('login/', TemplateView.as_view(template_name='login.html'), name='login'),
        # path('login/', user_login, name='login'),
        path('login/', LoginView.as_view(), name='login'),  # 基于类方法实现登录,这里是调用它的方法
    ]
    

    form字段验证

    验证最大长度,是否为空等一系列。

    users下新建forms文件。编辑

    from django import forms
    
    
    class LoginForm(forms.Form):
        # 用户名和密码不能为空
        username = forms.CharField(required=True)
        password = forms.CharField(required=True, min_length=5)
    

    form加入到登录逻辑LoginView(View)

    定义好forms之后我们来使用它做验证。

    from .forms import LoginForm
    
    
    # 基于类的视图实现登录
    class LoginView(View):
        def get(self, request):
            return render(request, 'login.html', {})
    
        def post(self, request):
            login_form = LoginForm(request.POST)
    
            if login_form.is_valid():
                user_name = request.POST.get('username', '')
                pass_word = request.POST.get('password', '')
    
                user = authenticate(username=user_name, password=pass_word)
    
                # 认证成功返回user对象,失败返回null
                if user:
                    login(request, user)
                    return render(request, 'index.html')
                else:
                    return render(request, 'login.html',
                                  {
                                      'msg': '用户名或密码错误!',
                                      'login_form': login_form,
                                  })
            else:
                return render(request, 'login.html',
                              {
                                  'login_form': login_form,
                              })
    

    完善错误提示login.html

    <h3 class="no-margins">登录</h3>
    <p class="m-t-md">登录到学习平台</p>
    <form method="post" action="/login/">
        <input name='username' value="{% if login_form.username.value %}{{ login_form.username.value }}{% endif %}" type="text" class="form-control uname" placeholder="用户名" />
        {% if login_form.errors.username %}
            <span id="cname-error" class="help-block m-b-none"> {{ login_form.errors.username.as_text }}</span>
        {% endif %}
    
        <input name='password' type="password" class="form-control pword m-b" placeholder="密码" />
        {% if login_form.errors.password %}
            <span id="cname-error" class="help-block m-b-none"> {{ login_form.errors.password.as_text }}</span>
        {% endif %}
    
        {% if msg %}
            <div class="alert alert-danger" style="padding: 5px;">
                {{ msg }}
            </div>
        {% endif %}
    
        {% csrf_token %}
        <a href="">忘记密码了?</a>
        <button type="submit" class="btn btn-success btn-block">登录</button>
    </form>
    
    • 写了一个类继承Django的view,然后写了get post方法(get/post的if是Django替我们完成的)
    • 在url中调用Loginviewas_view方法需要加上括号,进行调用。
    • Django的form进行表单验证并把error值传到前台。
    • is_valid方法,验证表单

    session和cookie自动登录机制

    cookie的存储

    cookie是浏览器支持的一种本地存储方式。以dict,键值对方式存储。

    {"sessionkey": "123"}
    

    浏览器会自动对于它进行解析。

    http请求是一种无状态的请求

    用户向服务器发起的两次请求之间是没有状态的。也就是服务器并不知道这是同一个用户发的。

    a浏览一个新闻,b浏览一个新闻,服务器只需要把新闻返回给客户端就可以,但在没有登录的情况下浏览某些网站,例如淘宝,也是给我们记住的,a浏览了哪些商品,b浏览了哪些商品。

    有状态请求

    做到记住用户:

    浏览器a在向服务器发起请求,服务器会自动给浏览器a回复一个id,浏览器a把id放到cookie当中,在下一次请求时带上这个cookie里的id值向浏览器请求,服务器就知道你是哪个浏览器发过来的了。

    服务器a发回来的id会放到服务器a的域之下。不能跨域访问cookie。

    使用浏览器随便打开一个网页,然后f12打开。会找到存储在浏览器本地的cookie值。点击clear all清空所有的cookie f5刷新页面,会发现又把这些cookie值进来。

    如果将用户名和密码直接保存在cookie,可以实现最垃圾最简略版本的自动登录。

    解决cookie放在本地不安全的问题(session)

    用户在第一次请求后,浏览器回复的id既可以是用户的user id, 也可以一段任意的字符串,我们把它叫做session id

    根据用户名和密码,服务器会采用自己的规则生成session id。这个session id保存在本地cookie。浏览器请求服务器会携带。

    • 输入用户名 & 密码
    • 调用 login(), 后端程序会根据用户名密码生成session id。保存在数据库中。
    • 用户登录之后,需要通过这个session id取出这些基本信息。

    Django的默认表中的session表就记录了用户登录时,后端我们Django为用户生成的sessionid。

    可以看到session key value 和过期时间。

    我们可以清空这张表的数据。运行项目进行登录。

    可以看到我们刚刚生成的session id。

    此时通过f12查看浏览器在本地存储的session id。可以看到如下图和我们数据库中的一致。

    session_key 发到浏览器叫做session id

    通过session id 用户访问任何一个页面都会携带,服务器就会认识。

    settings.py中,

    'django.contrib.sessions',
    

    这个app会拦截我们每次的request请求,在request中找到session id,然后去数据表中进行查询。
    然后通过session key 去找到session data。此时直接为我们取出了user。

    在服务器返回浏览器的response中也会直接加上session id

    cookie是浏览器本地存储机制,存在域名之下,存储不安全。

    服务器在返回id时通过规则生成一串字符,并设置了过期时间。存储在服务器端(数据库)

    参考 http://projectsedu.com/2016/10/17/django%E4%BB%8E%E8%AF%B7%E6%B1%82%E5%88%B0%E8%BF%94%E5%9B%9E%E9%83%BD%E7%BB%8F%E5%8E%86%E4%BA%86%E4%BB%80%E4%B9%88/

    相关文章

      网友评论

          本文标题:【Django在线教育平台】04.模板配置,完成用户登录相关页面

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