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

专题: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>

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/

转载于:https://www.jianshu.com/p/0807a3908ca0

猜你喜欢

转载自blog.csdn.net/weixin_34356555/article/details/91159646
今日推荐