(一)需求分析&技术实现
(二)初步搭建Django环境
(三)页面布局&Django模板
(四)SQL+Pandas初步处理数据
(五)前端表单交互
(六)Ajax异步传参与加载
(七)前端数据格式的处理
(八)DataTables接管前端表格
(九)Pyecharts实现交互图表
(十)静态图表的展示
(十一)“导出数据至Excel”功能
(十二)添加和配置缓存
(十三)用户登录系统
(十四)部署Django至生产环境
本章我们的目标是完成一个用户登录系统,使数据不至于裸奔。我们的需求是比较简单的,因为是自建自用的数据平台,用户权限可以在后台分配,也不涉及注册模块和密码找回等功能,就是简单的登录和登出。
Django有比较完善的原生用户和授权系统,包括内置的User和Group模型。一般情况下,我们不需要重写和扩展这些模型,直接应用即可。
首先,确保在工程的settings.py文件中有下方与用户权限相关的installed app和中间件:
INSTALLED_APPS = [
...
'django.contrib.auth',
'django.contrib.contenttypes',
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
Django原生系统的用户数据和sesssion数据都需要数据库存储,我们之前一直没有使用Django的数据库接口,这次要用了。我们可以使用django默认的sqlite3数据库,它是个单文件轻型数据库,与业务数据分离。settings.py里设置如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
因为是第一次使用Django数据库接口,我们需要执行migrate命令一次。如果在一般的Django项目中,migrate命令大概我们很早就很熟识了:
python manage.py migrate
再顺便把超级管理员账号创建了
python manage.py createsuperuser
其实我们进行到这个步骤时会有一种强烈感觉,我们依然在以Django MTV的的框架思路在搭建这个用户系统。上面完成的工作是M的部分,并且我们已经有了一条符合内置User model的超级管理员用户信息的数据。下面我们需要继续完成T和V的部分。
在template下新建registration文件夹,里面再新建login.html模板。
login模板还是继承自base.html,并且内容部分以{% block body %}{% endblock %}开头结尾。在第三章我们解释过这种模板继承的机制,并且这个login页面就和我们在第三章举的index页面的例子一模一样。
而login.html内容为:
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "chpa_data/base.html" %}
{% block body %}
<div id="pusher" class="pusher" style="padding-top:100px">
<div class="ui middle aligned center aligned grid">
<div class="column">
<h2 class="ui image header">
<div class="content">
用户登录
</div>
</h2>
<form method="post" action="{% url 'login' %}" class="ui large form">
{% csrf_token %}
{% if next %}
{% if user.is_authenticated %}
<div class="ui info message">您的账户没有权限浏览当前页面。 请尝试登录有权限的账号</div>
{% else %}
<div class="ui info message">未登录用户没有权限浏览当前页面,请登录</div>
{% endif %}
{% endif %}
<div class="ui stacked secondary segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon"></i>
{{ form.username }}
</div>
</div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon"></i>
{{ form.password }}
</div>
</div>
<input class="ui fluid large blue submit button" type="submit" value="登录">
<input type="hidden" name="next" value="{{ next }}">
</div>
{% if form.errors %}
<div class="ui info message">用户名或密码错误,请再次尝试。</div>
{% endif %}
</form>
<div class="ui message">
如登录困难,请联系管理员
</div>
</div>
</div>
</div>
...
{% endblock body %}
此时登录界面已经有了,但是布局有点诡异。
我们需要写一些css调整下:
{% block body %}
...
<style>
body {
background-color: #DADADA;
}
body > .grid {
height: 100%;
}
.column {
max-width: 450px;
}
.ui.footer.segment {
margin: 5em 0em 0em;
}
</style>
{% endblock body %}
Login.html的代码很好读懂,只有一个{{ next }}有些奇怪,但我们放一放,到后面相关部分再做解释。
下面配置URL,我们需要更改的是工程的urls.py,而不是chpa app的urls.py。添加登录权限相关url后它应该长这样:
urlpatterns = [
path('chpa/', include('chpa_data.urls')),
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
]
同时我们要在工程的settings.py里设置2个参数,明确登入和登出成功后分别的默认landing page:
LOGIN_REDIRECT_URL = '/chpa/index'
LOGOUT_REDIRECT_URL = '/accounts/login'
此时我们如果访问http://127.0.0.1:8088/accounts/login/,已经能使用这个登录界面了,我们甚至可以输入之前设置的超级管理员账户,登录成功后重定向到上方的/chpa/index页面。
但是,现在直接输入其他url能绕过此登录验证,我们还需要给所有与URL绑定的视图方法(本例中也就是views.py里的index, query, search, export4个)都加上@login_required装饰器, 如果涉及到多个装饰器的场合,@login_required装饰器放在最上面第一个加载:
from django.contrib.auth.decorators import login_required
@login_required
def index(request):
...
@login_required
@cache_page(60 * 60 * 24 * 30)
def query(request):
...
@login_required
@cache_page(60 * 60 * 24 * 30)
def search(request, column, kw):
...
@login_required
@cache_page(60 * 60 * 24 * 30)
def export(request, type):
...
此时可以回过头来解释login.html里{{ next }}的问题。
以query方法为例,@login_required装饰器会在query方法之前运行,即当任何URL成功定向到query方法后,@login_required将首先检查该用户是否已登录。如果他们已登录,则query将运行并返回结果。
而如果未登录,它将阻止query方法运行,而重定向至/login?next=%2Fquery。加载页面后此时的next后的参数为一个GET请求的参数被捕捉。所以这时login.html可以用django tag语法调用{{ next }}
login.html再使用<input type="hidden" name="next" value="{{ next }}">语句,把next作为一个隐藏元素在登录时发送,用户登录成功后此next相当于覆盖了默认的LOGIN_REDIRECT_URL ,把页面重定向回query方法。
简单地说,这个{{ next }}可以帮助我们在登录后重定向至登录前访问的网页。
最后,我们做一些收尾工作。因为我们需要Django自带的admin界面至少完成用户管理的工作,我们在header做一些admin的超链接以及登出的按钮等工作。此时的header.html为:
<div class="ui large top fixed inverted menu">
<div class="ui container">
<a href="{% url 'chpa:index' %}" class="item">首页</a>
{% if request.user.is_staff %}
<a href="{% url 'admin:index' %}" class="item">后台管理</a>
{% endif %}
</div>
<div class="right menu">
{% if not request.user.username %}
<div class="ui item">
未登录
</div>
{% else %}
<div class="ui item">
您好,{{ request.user.username }}
</div>
<a class="ui item" href="{% url 'logout' %}">
切换用户
</a>
{% endif %}
</div>
</div>
完成~