0%

初识 django project

实战01 login

实战01 login

一,首先进行

  1. 项目搭建,
1
2
3
4
5
6
7
8
mkvirtualenv -p python3 py3_django_bj  # 创建虚拟环境

# 进入虚拟环境后
pip3 install django
# 创建文件
mkdir test_django


  1. 项目创建
1
2
3
4
5
django-admin startproject test_django . # 注意这个点
# 创建app子应用
python manage.py startapp app


  1. 设置 时区

    在项目的settings文件中,如下所示

  1. 设计数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.db import models

# Create your models here.
class User(models.Model):
gender = (
('male', "男"),
('female', "女"),
)

name = models.CharField(max_length=128, unique=True)
password = models.CharField(max_length=256)
email = models.EmailField(unique=True)
sex = models.CharField(max_length=32, choices=gender, default="男")
c_time = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.name

class Meta:
ordering = ["-c_time"]
verbose_name = "用户"
verbose_name_plural = "用户"
  1. 设置数据库后端

    默认是sqlite数据库,

1
2
3
4
5
6
7
8
9
# Database


DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

MySQL的配置如下

1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'mysite', # 数据库名,先前创建的
'USER': 'root', # 用户名,可以自己创建用户
'PASSWORD': '****', # 密码
'HOST': '192.168.1.123', # mysql服务所在的主机ip
'PORT': '3306', # mysql服务端口
}
}
  1. 注册app

注册app

  1. 创建记录和数据表

    进入pycharm 的terminal终端, 执行以下命令

    1
    python manage.py makemigrations

model

Django自动为我们创建了app\migrations\0001_initial.py文件,保存了我们的第一次数据迁移工作,也就是创建了User模型。

接着执行下面的命令:

1
python manage.py migrate

二. admin后台


在我们开发的初期,没有真实的用户数据,也没有完整的测试环境,为了测试和开发的方便,通常我们会频繁地使用Django给我们提供的Admin后台管理界面,创建测试用例,观察模型效果等等

  1. 因为admin 已经注册了, 所以无需再次注册,

  2. 进入app/admin.py 文件, 代码如下

    1
    2
    3
    4
    5
    6
    7
    from django.contrib import admin

    # Register your models here.

    from . import models

    admin.site.register(models.User)

    暂时简单点, 直接注册就好了

创建超级管理员

Django的admin后台拥有完整的较为安全的用户认证和授权机制,防护等级还算可以。

要进入该后台,需要创建超级管理员,该管理员和我们先前创建的User用户不是一个概念,要注意区别对待。

同样在Pycharm的终端中,执行下面的命令:

1
python manage.py createsuperuser

三,使用Admin后台

创建好超级管理员后,就可以启动我们的开发服务器了,然后在浏览器中访问http://127.0.0.1:8000/admin/地址,可以看到如下的登录界面:

1611027106282

1611027127248

注意,图中下方的认证和授权是admin应用自身的账户管理,上面的app栏目才是我们创建的login应用所对应的User模型。

点击app栏目中的用户链接,进入用户列表界面,发现是空的,因为我们当前没有任何用户。点击右上方的增加用户按钮,我们创建几个测试用户试试:

1611027421214

但是,实际上这里还有点小问题,那就是密码相关。密码不能保存为明文,这个问题我们后面再解决;其次,不可以这么随意的修改和设置密码,为了展示的方便性,我们先这样

添加了3个用户

1611027618523

admin的使用和配置博大精深,但在本实战项目里,我们暂时把它当做一个数据库管理后台使用

url路由和视图

前面我们已经创建好数据模型了,并且在admin后台中添加了一些测试用户。下面我们就要设计好站点的url路由、对应的处理视图函数以及使用的前端模板了。

  1. 路由设计

    我们初步设想需要下面的四个URL:

    URL 视图 模板 说明
    /index/ login.views.index index.html 主页
    /login/ login.views.login login.html 登录
    /register/ login.views.register register.html 注册
    /logout/ login.views.logout 无需专门的页面 退出

    重要说明:由于本项目目的是打造一个针对管理系统、应用程序等需求下的可重用的登录/注册app,而不是门户网站、免费博客等无需登录即可访问的网站,所以在url路由、跳转策略和文件结构的设计上都是尽量自成体系。具体访问的策略如下

    • 未登录人员,不论是访问index还是login和logout,全部跳转到login界面
    • 已登录人员,访问login会自动跳转到index页面
    • 已登录人员,不允许直接访问register页面,需先logout
    • 登出后,自动跳转到login界面

    考虑到登录注册系统属于站点的一级功能,为了直观和更易于接受,这里没有采用二级路由的方式,而是在根路由下直接编写路由条目,同样也没有使用反向解析名(name参数)。所以,在重用本app的时候,一定要按照app使用说明,加入相应的url路由。

1611028151106

四,编写 视图函数

1611063774388

现在我们的工作目录

1611063822972

访问主页

1611063870554

1.login 页面编写

1611064067731

2. 引入Bootstrap 4

Bootstrap4就是最好的CSS框架之一!

想要在HTML页面中使用Bootstrap4,最方便的方法就是使用国内外的免费CDN(如果app的使用环境不可以使用外部网络,也可以使用内部的CDN,或者静态文件)。

这里推荐BootCDN:https://www.bootcdn.cn/,速度比较快,有大量的不同版本的CDN。

1611064272802

jQuery

1611064538948

1611064489803

五,静态文件的引入

继续在/login/static/login目录下创建一个css和一个image目录,css中添加我们为登录视图写的css文件,这里是login.cs![17](/17.png)![17](/17.png)s,image目录中,拷贝进来你想要的背景图片,这里是bg.jpg。最终目录结构如下:

1611065555604

主要是引入了login.css文件,注意最开头的{% load static %},表示我们要加载静态文件。

1611065927558

登录视图

1
2
3
4
5
6
7
def login(request):
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
print(username, password)
return redirect('/index/')
return render(request, 'login/login.html')

说明:

  • 每个视图函数都至少接收一个参数,并且是第一位置参数,该参数封装了当前请求的所有数据;

  • 通常将第一参数命名为request,当然也可以是别的;

  • request.method中封装了数据请求的方法,如果是“POST”(全大写),将执行if语句的内容,如果不是,直接返回最后的render()结果,也就是正常的登录页面;

  • request.POST封装了所有POST请求中的数据,这是一个字典类型,可以通过get方法获取具体的值。

  • 类似get('username')中的键‘username’是HTML模板中表单的input元素里‘name’属性定义的值。所以在编写form表单的时候一定不能忘记添加name属性。

  • 利用print函数在开发环境中验证数据;

  • 利用redirect方法,将页面重定向到index页。

    启动服务器,然后在http://127.0.0.1:8000/login/的表单中随便填入用户名和密码,然后点击提交。然而,页面却出现了错误提示,如下图所示:

1611066293446

解决这个问题的办法其实在Django的Debug错误页面已经给出了,我们需要在前端页面的form表单内添加一个{% csrf_token %}标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<form class="form-login" action="/login/" method="post">
{% csrf_token %}
<h3 class="text-center">欢迎登录</h3>
<div class="form-group">
<label for="id_username">用户名:</label>
<input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
</div>
<div class="form-group">
<label for="id_password">密码:</label>
<input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
</div>
<div>
<a href="/register/" class="text-success " ><ins>新用户注册</ins></a>
<button type="submit" class="btn btn-primary float-right">登录</button>
</div>
</form>

数据验证

1
2
3
4
5
6
7
8
9
def login(request):
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
if username.strip() and password: # 确保用户名和密码都不为空
# 用户名字符合法性验证
# 密码长度验证
# 更多的其它验证.....
return redirect('/index/')
  • get方法是Python字典类型的内置方法,它能够保证在没有指定键的情况下,返回一个None,从而确保当数据请求中没有username或password键时不会抛出异常;
  • 通过if username and password:确保用户名和密码都不为空;
  • 通过strip方法,将用户名前后无效的空格剪除;
  • 更多的数据验证需要根据实际情况增加,原则是以最低的信任度对待发送过来的数据。

账户密码 验证

注意其中添加了一句from . import models,导入我们先前编写好的model模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


from . import models


def login(request):
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
if username.strip() and password: # 确保用户名和密码都不为空
# 用户名字符合法性验证
# 密码长度验证
# 更多的其它验证.....
try:
user = models.User.objects.get(name=username)
except:
return render(request, 'login/login.html')
if user.password == password:
return redirect('/index/')
return render(request, 'login/login.html')

添加提示信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
message = '请检查填写的内容!'
if username.strip() and password:
# 用户名字符合法性验证
# 密码长度验证
# 更多的其它验证.....
try:
user = models.User.objects.get(name=username)
except :
message = '用户不存在!'
return render(request, 'login/login.html', {'message': message})

if user.password == password:
print(username, password)
return redirect('/index/')
else:
message = '密码不正确!'
return render(request, 'login/login.html', {'message': message})
else:
return render(request, 'login/login.html', {'message': message})

说明: 这里增加了message变量,用于保存提示信息。当有错误信息的时候,将错误信息打包成一个字典,然后作为第三个参数提供给render方法。这个数据字典在渲染模板的时候会传递到模板里供你调用。

为了在前端页面显示信息,还需要对login.html进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<form class="form-login" action="/login/" method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
{% csrf_token %}
<h3 class="text-center">欢迎登录</h3>
<div class="form-group">
<label for="id_username">用户名:</label>
<input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
</div>
<div class="form-group">
<label for="id_password">密码:</label>
<input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
</div>
<div>
<a href="/register/" class="text-success " ><ins>新用户注册</ins></a>
<button type="submit" class="btn btn-primary float-right">登录</button>
</div>
</form>

Django的模板语言{% if xxx %}{% endif %}非常类似Python的if语句,也可以添加{% else %}分句。例子中,通过判断message变量是否为空,也就是是否有错误提示信息,如果有,就显示出来!这里使用了Bootstrap的警示信息类alert,你也可以自定义CSS或者JS。

1611067107437

Django表单

  1. 创建表单模型

在app/创建forms.py 文件进行 表单模型创建

1
2
3
4
5
6
from django import forms

class UserForm(forms.Form):
username = forms.CharField(label='用户名',max_length=128)
password = forms.CharField(label='用户名',max_length=256,widget=forms.PasswordInput)

说明:

  • 顶部要先导入forms模块
  • 所有的表单类都要继承forms.Form类
  • 每个表单字段都有自己的字段类型比如CharField,它们分别对应一种HTML语言中``内的一个input元素。这一点和Django模型系统的设计非常相似。
  • label参数用于设置``标签
  • max_length限制字段输入的最大长度。它同时起到两个作用,一是在浏览器页面限制用户输入不可超过字符数,二是在后端服务器验证用户输入的长度也不可超过。
  • widget=forms.PasswordInput用于指定该字段在form表单里表现为``,也就是密码输入框。
  1. 修改视图函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def login(request):
if request.method == "POST":
login_form = forms.UserForm(request.POST)
message = '请检查填写的内容!'
if login_form.is_valid():
# 用户名字符合法性验证
# 密码长度验证
# 更多的其它验证.....
username = login_form.cleaned_data.get('username')
password = login_form.cleaned_data.get('password')
try:
user = models.User.objects.get(name=username)
except :
message = '用户不存在!'
return render(request, 'login/login.html', locals())

if user.password == password:
print(username, password)
return redirect('/index/')
else:
message = '密码不正确!'
return render(request, 'login/login.html', locals())
else:
return render(request, 'login/login.html', locals())
login_form = forms.UserForm()
return render(request, 'login/login.html', locals())
# locals() 会自动构造成字典, 不需要在自己去构造, 但是也会传入一些多余的变量数据,造成数据冗余降低效率

  1. 修改login页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- 上述meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<!-- Bootstrap CSS -->
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
<link href="{% static 'login/css/login.css' %}" rel="stylesheet"/>
<title>登录</title>
</head>
<body>
<div class="container">
<div class="col">
<form class="form-login" action="/login/" method="post">
{% if message %}
<div class="alert alert-warning">{{ message }}</div>
{% endif %}
{% csrf_token %}
<h3 class="text-center">欢迎登录</h3>

{{ login_form }}

<div>
<a href="/register/" class="text-success " ><ins>新用户注册</ins></a>
<button type="submit" class="btn btn-primary float-right">登录</button>
</div>
</form>
</div>
</div> <!-- /container -->

<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
{# 以下三者的引用顺序是固定的#}
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script src="https://cdn.bootcss.com/popper.js/1.15.0/umd/popper.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>

</body>
</html>

说明:

​ 一个{{ login_form }}就直接完成了表单内容的生成工作!login_form这个名称来自你在视图函数中生成的form实例的变量名!

实际上除了通过{{ login_form }}简单地将表单渲染到HTML页面中了,还有下面几种方式:

  • {{ login_form.as_table }} 将表单渲染成一个表格元素,每个输入框作为一个标签
  • {{ login_form.as_p }} 将表单的每个输入框包裹在一个标签内
  • ```` 将表单渲染成一个列表元素,每个输入框作为一个``标签
------ 本文结束------

欢迎关注我的其它发布渠道