Django项目

django
Author

dd21

Published

December 5, 2022

Django项目

一、 git的使用:

1.1 安装git:

Ubuntu下安装git

sudo apt-get install git

Windows下安装git

进入官网,下载安装包,选好路径傻瓜安装

1.2 查看是否安装成功

git --version

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yjDghJAj-1588223223775)(/../../../../AppData/Local/Temp/1564464652239.png)]

1.3 创建本地仓库

1.3.1 创建本地仓库

创建一个用于存放项目的文件夹(Django_project_test)

进入到项目文件夹,右键打开git bush

(一般情况下文件夹以 . 开头的都是隐藏文件夹, 里面的文件切记不可以修改。)

Git init    # 初始化会创建一个.git隐藏文件夹   这个文件夹为版本库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nTxHa1vO-1588223223780)(/1564464822626.png)]

1.3.2 配置个人信息

# 配置全局信息
git config --global user.name 'xiaofei'
git config --global user.email 'izhangtengfei@163.com'

# 配置信息会保存在家目录下面
~/.gitconfig
# 配置个人项目信息
git config user.name 'xiaofei'
git config user.email 'izhangtengfei@163.com'

# 配置信息会保存在当前目录下的./git/config下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZJmNAoQF-1588223223782)(/1564466169672.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xa0MbzDq-1588223223786)(/1564466511165.png)]

1.3.3 添加文件

创建好了本地仓库,就 准备开始开发了。

在项目文件夹下创建readme.txt 文件, 并输入内容。

将文件添加到代码库分两步

①添加到暂存区

git add readme.txt    # 将文件添加到暂存区

②将暂存区的文件提交到仓库。

git commit -m '备注'  # 备注处写入备注信息

git commit 命令 -m 参数后输入的是提交的说明。 命令执行成功后显示几个文件被改动,加了多少行,少了多少行。每次提交都会生成一个新的版本。

(此次修改之前readme.txt是一个空的文件)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ph8FBnkm-1588223223788)(/1564468015460.png)]

修改后的文件为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9S9h9D2a-1588223223789)(/1564468123232.png)]

1.3.4历史版本

工作一段时间后,我们提交了很多次到本地仓库,也会产生很多的版本。怎么查看记录。

git log     # 查看历史操作记录  不可以查看删除的commit记录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AJXBX74f-1588223223790)(/1564468383357.png)]

git reflog     # git reflog 可以查看所有分支的操作记录,包含已经删除的commit记录

1.3.5 版本回退

git reset --hard HEAD^

# 使用这种回退没命令会把回退完的节点作为当前节点,进行回退,例如现在1.3使用 git reset --hard HEAD^ 会回退到1.2版本。如果再次使用 git reset --head HEAD^ 版本就会回退到1.1以此类推。

HEAD表示当前最新版本

HEAD^表示当前最新版本的前一个版本

HEAD^^表示当前最新版本的前两个版本,有几个^就回退几个版本

HEAD~1表示最新版本的前一个版本

HEAD~3表示最新版本的前3个版本, 后面数字是几就代表最新新版本之前的第几个版本

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vQ3p4oAd-1588223223791)(/1564474137454.png)]

执行命令完

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i496Fdg9-1588223223792)(/1564474104120.png)]

git reset--hard 版本号 (版本号就是reflog中的版本号)

版本非常多时选择这种方法。版本号就是每次commit生成的哈希(hash)值,只取用前几位数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VBcWKQNz-1588223223793)(/1564474725121.png)]

回退完的结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hx27iITp-1588223223794)(/1564474758460.png)]

1.3.6 撤销修改

①查看状态

运行git status命令会显示当前工作区,暂存区,仓库的状态。当工作区的所有代码都提交到仓库 并和仓库保持一致时会显示:

On branch master
nothing to commit, working tree clean

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJEAfuPS-1588223223795)(/1564476920664.png)]

修改之前:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Kr69uUA-1588223223797)(/1564477007021.png)]

修改之后的:

# 修改完之后记得ctrl+s保存
# 之后执行  git status查看状态
git status 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e3PPxYSl-1588223223798)(/1564477127496.png)]

bush窗口会显示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-szfpjyTP-1588223223799)(/1564477287358.png)]

②撤销工作区的修改
# 只进行了保存操作,没有进行git add .(添加到暂存区)的操作。
# 直接执行 git checkout 文件名,即可撤销修改。
git checkout readme.txt


# 如多打开文件没有执行任何改动直接执行回退命令,会直接将整个文件清空。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O61t9rVr-1588223223800)(/1564477632643.png)]

修改之前的样子

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7tj6RAKy-1588223223801)(/1564477905800.png)]

修改之后

工作区:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3up2wYD-1588223223802)(/1564477989929.png)]

进入bush进行状态查看:

# git 会提醒你如何保存修改到本地仓库。我们想执行的是撤销操作不是添加操作。不必理会这里的提示
执行以下代码会将

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fRZnkKAn-1588223223803)(/1564478069021.png)]

③撤销暂存区的代码修改

工作区:

修改之前的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOI8qw13-1588223223804)(/1564479231904.png)]

修改之后的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F82PChA0-1588223223805)(/1564479050323.png)]

bush:

Fly@DESKTOP-G76LKOK MINGW64 /d/all/Django_project_test (master)
# 查看状态
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

Fly@DESKTOP-G76LKOK MINGW64 /d/all/Django_project_test (master)
# 添加到暂存区
$ git add .

Fly@DESKTOP-G76LKOK MINGW64 /d/all/Django_project_test (master)
# 查看状态确认是否添加到暂存区
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   readme.txt


Fly@DESKTOP-G76LKOK MINGW64 /d/all/Django_project_test (master)
# 撤回到工作区
$ git reset HEAD readme.txt
Unstaged changes after reset:
M       readme.txt

Fly@DESKTOP-G76LKOK MINGW64 /d/all/Django_project_test (master)
# 确认状态是否已经撤回到工作区
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

Fly@DESKTOP-G76LKOK MINGW64 /d/all/Django_project_test (master)

# 执行工作区的修改撤回命令
$ git checkout readme.txt
Updated 1 path from the index

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mGMbKkeq-1588223223806)(/1564480056766.png)]

1.3.7 对比文件

  1. 对比本地仓库与工作区

    # 注意!!! 创建文件时不要用文档打开,打开后再用notepad编辑会出现乱码,直接用notepad打开不会出现中文乱码
    
    git diff HEAD -- 文件名     # 没有产生修改不会显示

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOf9JQ9Z-1588223223807)(/1564481673195.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-alMei6TF-1588223223810)(/1564481724616.png)]

  1. 对比本地仓库个版本代码

    进入cd到文件所在的夹才可以进行对比

    git diff HEAD HEAD^ -- 文件名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFQYW80i-1588223223811)(/1564481844704.png)]

1.3.8 文件删除

①没有添加到库中的文件

# 直接删除即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fKn2h6M6-1588223223812)(/1564485120860.png)]

②添加到暂存区但是没有提交的文件

# 先撤回到工作区
git reset HEAD 文件名
# 直接执行删除
①中的操作

③已经提交到版本库

# 删除文件
①的操作
# 运行git add .
# 提交
git commit -m '删除版本库'
(这个方法类似于覆盖)

1.4远程仓库

1.4.1 添加远程仓库

① 克隆

克隆就是将远程的代码仓库拉回到本地仓库,这种方式适用于远程仓库已经有东西了

进入到项目所在文件夹打开bash窗口。

#         clone的http地址
git clone https://gitee.com/itengfei/first_test.git    

执行完以上命令的结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LCEwiUkT-1588223223814)(/1564561104558.png)]

②本地已经初始化了一个仓库,与远程仓库建立连接再push

使用http连接

每次push都会输入用户名和密码

# 创建仓库 
git init
# 建立远程连接
git remote add origin https://gitee.com/itengfei/first_test.git
# 将代码推送上去 origin是远程仓库的名字,master是推送的分支
git push -u origin master

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHGqJ4k5-1588223223816)(/1564562718031.png)]

用户名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qNBp9b3B-1588223223817)(/1564562954745.png)]

密码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X4EjStpf-1588223223818)(/1564565420742.png)]

将远程仓库拉到本地仓库

git pull origin master

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vLcOLOIu-1588223223819)(/1564565931844.png)]

使用ssh连接

公钥管理 :https://gitee.com/help/categories/38

添加个人公钥

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sBJpg55U-1588223223821)(/1564567213412.png)]

点击添加个人公钥接下来进入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kXIQ4F5B-1588223223822)(/1564567384146.png)]

生成公钥: https://gitee.com/help/articles/4181#article-header0

①生成秘钥

# 生成公钥     "邮箱"
 ssh-keygen -t rsa -C "izhangtengfei@163.com"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZxrzWCm-1588223223823)(/1564567870692.png)]

②进入到家目录查看生成的秘钥

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5KmrHX7a-1588223223824)(/1564567851652.png)]

推荐notepad打开 .pub 文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jrE7X4R4-1588223223826)(/1564568079139.png)]

.pub文件中的内容全部复制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H9dlXbzX-1588223223827)(/1564568499541.png)]

粘贴到网站的ssh公钥中

图上漏了 ssh-rsa

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7tcRXx0-1588223223828)(/1564568295187.png)]

输入密码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oj8KXoUA-1588223223829)(/1564568642602.png)]

添加完成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gQRIi3gP-1588223223831)(/1564568676759.png)]

连接

复制ssh

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RuRpJCzL-1588223223832)(/1564568952113.png)]

①删除原先http建立的连接
# 查看远程连接
git remote 
# 删除origin这个远程连接
git remote remove origin

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tLeF3zwT-1588223223834)(/1564569486795.png)]

②建立连接
 # 建立连接
 git remote add origin git@gitee.com:itengfei/first_test.git
 # 推送代码  推送到的地址     master 本地推送上去的分支
 git push origin master

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dmUq3BLM-1588223223835)(/1564569731810.png)]

修改内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D75ziSnV-1588223223836)(/1564569903274.png)]

查看修改结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lT6pfxLx-1588223223837)(/1564569936912.png)]

1.5 分支管理

​ 正常的开发项目中都是多人协作,每个人的任务一般不会一天就完成,如果把没有完成的代码提交到远程仓库会影响被人工作。git提供了分支的功能就不用担心了,可以创建一个自己的分支,在上面干活,想提交就提交,等到工作完成再一次性合并到原来的分支。

①创建分支

新建git仓库时会默认创建一个master分支,他叫主分支。

一般情况下我们不会直接在主分支上干活,它主要用来发布版本。

使用 git branch命令查看当前分支

# 创建一个开发分支
git branch develop
②切换分支
# 切换到develop分支
git checkout develop
③ 可以把①和②合并

创建并切换到新建分支

-b 表示 创并切换。

# ①②合并成一条语句 创建并切换到新建分支
git checkout -b develop

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uDJCMZmJ-1588223223838)(/1564566781773.png)]

④合并分支

创建好develop分支后,你开始干活,完成上级领导交给你的任务,根据用户的手机壳颜色更换app主题,5分钟之后开发完毕,提交:

$ git add change_theme.py

Administrator@DESKTOP-F3CCKFR MINGW64 ~/Desktop/haha (develop)
$ git commit -m 'complete change theme'
[develop 5b83e32] complete change theme
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 change_theme.py

经过测试功能完成,现在我们要合并到master分支

首先切换到master

$ git checkout master
Switched to branch 'master'

查看工作区,你会发现刚才开发的功能文件没有了,不要惊慌,因为那个提交是在develop分支上,现在我们把develop分支的工作合并到master分支上:

$ git merge develop
Updating 7173db6..5b83e32
Fast-forward
 change_theme.py | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 change_theme.py

git merge命令用于合并指定分支到当前分支。合并后,在查看文件夹发现,change_theme.py文件又回来了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8OStZW0z-1588223223839)(/1564632106820.png)]

⑤删除分支

合并完之后你也可以删除掉develop分支:

$ git branch -d develop
Deleted branch develop (was 5b83e32).
$ git branch
* master

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1T99hEi-1588223223840)(/1564632274888.png)]

二、项目创建

2.1 创建Django项目

在Ubuntu上创建项目,实际部署大多是linux系统

# 复制本机的Python环境 -p后面是 Python所在的路径   后面试复制后的环境名
mkvirtualenv -p /usr/bin/python3.6 tzproject

2.1.1安装Django

选择Django2.1.x最新版本,我们用的是2.1.10

# 安装
pip install django==2.1.10

2.1.2 创建Django项目

# 进入到存放Django项目的文件夹
cd ~/dj_project/
# 创建Django项目
django-admin startproject tz_project
# 查看环境
workon
# 进入到环境
workon tz_project

2.1.3创建pycharm项目

通过pycharm的ssh连接虚拟机(服务器)

①选择项目目录

选择一个存项目的本地文件,夹注意django项目和pycharm项目的根目录一定要保持一致,不要用一个pycharm的项目,管理多个django项目。

②配置远程解释器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIDWEsDn-1588223223842)(/1565081046814.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9r12HJNZ-1588223223843)(/1565081616004.png)]

③配置远程项目路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hP6raF3l-1588223223844)(/1565081700289.png)]

④设置同步配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4QdxbLCM-1588223223845)(/1565081832839.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FgQLYdcg-1588223223846)(/1565082202430.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-41b5u4Ma-1588223223847)(/1565081924013.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZAcIw3pj-1588223223848)(/1565082109829.png)]

⑤下载代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLqIkl03-1588223223849)(/1565082467843.png)]

⑥创建Djangosever

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8m27THfv-1588223223849)(/1565083200722.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-45PhqKNr-1588223223850)(/1565083261276.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ns3oqlGh-1588223223851)(/1565083309002.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4h20zbx-1588223223852)(/1565083378768.png)]

2.1.4 配置数据库

①创建数据库

create database test_1

②创建用户

# 创建一个名为fly可以远程访问的用户密码为Pythonvip
create user 'fly'@'%' identified by 'pythonvip'

③授权

https://www.cnblogs.com/wuxunyan/p/9095016.html

# 所有权限给fly
grant all privileges on test_1.* to 'fly'@'%' ;

④ 配置settings文件

安装mysqlclient

# 首先更新ubuntu
sudo apt-get update 
# 然后按照依赖库
sudo apt-get install default-libmysqlclient-dev
# 再切换到虚拟环境中
workon tzproject
# 最后安装mysqlclient
pip install mysqlclient -i https://pypi.doubanio.com/simple
# 方法一
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',               # 数据库引擎
        'NAME': 'test_1',                                # 数据库名
        'USER': 'fly',                                  # 用户名
        'PASSWORD': 'pythonvip',                            # 密码
        'HOST': '127.0.0.1',                                # 主机IP
        'PORT': 3306                                        # 端口
    }
}

方法二:

创建utils工具文件夹,存放工具

将数据库配置信息存到一个文件,在settings文件中将其引入。(推荐)

新建数据库配置文件db.cnf(名字随意)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ye6aG9rg-1588223223853)(/1565084327733.png)]

# 方法二db.cnf文件
[client]
database = tzproject
user = dj_user
password = pythonvip
host = 127.0.0.1
port = 3306
default-character-set = utf8

pymysql的使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWlo4cN8-1588223223854)(/1565084964896.png)]

2.1.5 配置redis缓存

用于存放用户session信息,以及需要缓存的各种信息。

# 在虚拟机中安装django-redis
pip install django-redis    
# settings.py文件中指定redis配置

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    'session': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    },
}

# session存储缓存设置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'session'

文档地址:https://django-redis-chs.readthedocs.io/zh_CN/latest/

django文档:https://docs.djangoproject.com/en/2.2/topics/http/sessions/#using-cached-sessions

2.1.6 配置日志器

用于记录系统运行过程中的各种日志信息。

在项目根目录中创建一个logs文件夹,用于存放日志文件

# 日志
LOGGING = {
    # 版本
    'version': 1,
    # 是否禁用已存在的日志器
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {lineno:d} {message}',
            'style': '{',
        },
        'simple': {
            'format': '{levelname} {module} {lineno:d} {message}',
            'style': '{',
        },
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            'level': 'INFO',
            # 这个handler可以记录一组日志文件
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(BASE_DIR, 'logs/tz_django.log'),
            # 单个日志文件最大字节数
            'maxBytes': 300*1024*1024,
            # 日志文件个数
            'backupCount': 10,
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'level': 'INFO',  # 日志器接收的最低级别
            'propagate': True,
        },
    },
}
# 使用方法
# import the logging library
import logging

# Get an instance of a logger
# 此处的django为,settings.py文件中,LOGGING配置下的loggers中定义的日志器名称
logger = logging.getLogger('django')    

def my_view(request, arg1, arg):
    ...
    if ***:
        # Log an error message
        logger.error('Something went wrong!')

官方文档:https://docs.djangoproject.com/en/2.2/topics/logging/#module-django.utils.log

2.1.7 时区配置

# 修改语言
LANGUAGE_CODE = 'zh-hans'
# 修改时区
TIME_ZONE = 'Asia/Shanghai'

2.1.8 静态文件配置

在项目根目录创建一个static文件夹, 用来存放静态文件(css, js, img, font 等等)

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

2.1.9 加载优化

接着分别在apps文件夹和项目根目录文件夹上右键,加入到Source Root中,优化导入路径(pycharm提示)。

import os
import sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 将app添加到sys中,优化调用
sys.path.insert(0, BASE_DIR)
sys.path.insert(1, os.path.join(BASE_DIR, 'apps'))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ATw9Q2y1-1588223223856)(/1565086240145.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UF97hUlH-1588223223857)(/1565086403244.png)]

添加apps

将创建的app放到apps中

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'news',
]

三、模板抽取

3.1 创建templates文件夹

创建templates文件夹用来存放html文件

3.1.1 创建base模板

用来继承页面中的相同文件,减少代码的冗余

3.1.2 设置templates路径

记得配置templates在settings文件中的路径

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

3.1.3 分析页面,抽取出相同的部分

利用 block  名字  和  endblock 来分离出不同的部分

然后根据每个页面的不同填入不同的东西

并修改硬写部分

路径结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QyzJ1EOW-1588223223858)(/1565757006289.png)]

①base.html文件

<!--base.html文件-->

{{% load static %}}



<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">


    <title>{{% block title %}}{{% endblock title %}}</title>


    <link rel="stylesheet" href={{% static "css/base/reset.css" %}}>
    <link rel="stylesheet" href={{% static "css/base/common.css" %}}>

    <link rel="stylesheet" href={{% static "css/base/side.css" %}}>
    <link rel="stylesheet" href="http://at.alicdn.com/t/font_684044_un7umbuwwfp.css">
    <!--特殊的css-->
    {{% block css %}}
    {{% endblock css %}}
</head>
<body>
<!-- header start -->
<header id="header">
    <div class="mw1200 header-contain clearfix">
        <!-- logo start -->
        <h1 class="logo">
            <a href="javascript:void(0);" class="logo-title">Python</a>
        </h1>
        <!-- logo end -->
        <!-- nav start -->
        <nav class="nav">
            <ul class="menu">
                <li><a href="{{% url 'news:index' %}}">首页</a></li>
                <li><a href="{{% url 'course:index' %}}">在线课堂</a></li>
                <li><a href="{{% url 'doc:index' %}}">下载文档</a></li>
                <li><a href="search.html">搜索</a></li>
            </ul>
        </nav>
        <!-- nav end -->
        <!-- login start -->
        <div class="login-box">
            <div>
                <i class="PyWhich py-user"></i>
                <span>
                  <a href="{{% url 'user:login' %}}" class="login">登录</a> / <a href="{{% url 'user:register' %}}"
                                                                         class="reg">注册</a>
              </span>
            </div>
            <div class="author hide">
                <i class="PyWhich py-user"></i>
                <span>qwertyui</span>
                <ul class="author-menu">
                    <li><a href="javascript:void(0);">后台管理</a></li>
                    <li><a href="javascript:void(0);">退出登录</a></li>
                </ul>
            </div>
        </div>
        <!-- login end -->
    </div>
</header>
<!-- header end -->

<!-- main start -->
<!-- 除去页眉页脚的内容 -->
{{% block main %}}
    <main id="main">
        <div class="w1200 clearfix">
            <!-- main-contain start  -->
            <!-- 主体内容 start  -->
            {{% block main_contain %}}

            {{% endblock main_contain %}}
            <!-- 主体内容  end -->
            <!-- main-contain  end -->

            <!-- side start -->
            <!-- 边上的内容 -->
            {{% block side %}}
                <aside class="side">
                    <div class="side-activities">
                        <h3 class="activities-title">在线课堂<a href="javascript:void(0)">更多</a></h3>
                        <div class="activities-img">
                            <a href="javascript:void(0);" target="_blank">
                                <img src={{% static "images/english.jpg" %}} alt="title">
                            </a>
                            <p class="activities-tips">对话国外小姐姐</p>
                        </div>
                        <ul class="activities-list">
                            <li>
                                <a href="javascript:void(0);" target="_blank">
                                    <span class="active-status active-start">报名中</span>
                                    <span class="active-title"><a
                                            href="https://www.shiguangkey.com/course/2432"> Django 项目班</a></span>
                                </a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank">
                                    <span class="active-status active-end">已结束</span>
                                    <span class="active-title"><a
                                            href="https://www.shiguangkey.com/course/2321">Python入门基础班</a></span>
                                </a>
                            </li>
                        </ul>
                    </div>
                    <div class="side-attention clearfix">
                        <h3 class="attention-title">关注我</h3>
                        <ul class="side-attention-address">
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i
                                        class="PyWhich py-GitHub"></i>XinLan</a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-zhihu"
                                                                                 style="color:rgb(0, 108, 226);"></i>XinLan</a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-weibo"
                                                                                 style="color:rgb(245,92,110);"></i>XinLan</a>
                            </li>
                        </ul>
                        <div class="side-attention-qr">
                            <p>扫码关注</p>
                        </div>
                    </div>
                    <!--hot_news模板-->
                    {{% block hotnews %}}
                    {{% endblock hotnews %}}

                </aside>
            {{% endblock side %}}
            <!-- side end -->
        </div>
    </main>
{{% endblock main %}}
<!-- main end -->

<!-- footer start -->
<!-- 页脚 start -->
{{% block foot %}}

{{% endblock foot %}}
<!-- footer end -->
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
{#<script src="{{% static 'js/base/common.js' %}}"></script>#}
<script src="{{% static 'js/base/message.js' %}}"></script>

<!-- js start -->
{{% block script %}}
{{% endblock script %}}
</body>
</html>

②news.html文件

{{% extends 'base/base.html' %}}
{{% load static %}}
{{% block title %}}
    IndexPage
{{% endblock title %}}

{{% block css %}}
    <link rel="stylesheet" href="{{% static 'css/news/index.css' %}}">
    <script>IMemuIndex=0</script>
{{% endblock css %}}




<!-- main start -->
{{% block main %}}
    <main id="main">
        <div class="w1200 clearfix">
            <!-- main-contain start  -->
            {{% block main_contain %}}
                <div class="main-contain">
                    <!-- banner start -->

                    <div class="banner">
                        <ul class="pic">
                            <!--淡入淡出banner-->


                            <li><a href="javascript:void(0);"><img src={{% static "images/linux.jpg" %}} alt="test"></a>
                            </li>


                            <li><a href="javascript:void(0);"><img src={{% static "images/python_gui.jpg" %}} alt="test"></a>
                            </li>


                            <li><a href="javascript:void(0);"><img
                                    src={{% static "images/python_function.jpg" %}} alt="test"></a></li>


                            <li><a href="javascript:void(0);"><img
                                    src={{% static "images/python_advanced.jpg" %}} alt="test"></a></li>


                            <li><a href="javascript:void(0);"><img
                                    src={{% static "images/jichujiaochen.jpeg" %}} alt="test"></a></li>


                            <li><a href="javascript:void(0);"><img src={{% static "images/python_web.jpg" %}} alt="test"></a>
                            </li>


                        </ul>
                        <a href="javascript:void(0);" class="btn prev">
                            <i class="PyWhich py-arrow-left"></i></a>
                        <a href="javascript:void(0);" class="btn next">
                            <i class="PyWhich py-arrow-right"></i></a>
                        <ul class="tab">
                            <!-- 按钮数量必须和图片一致 -->


                            <li></li>


                            <li></li>


                            <li></li>


                            <li></li>


                            <li></li>


                            <li></li>


                        </ul>
                    </div>

                    <!-- banner end -->

                    <!-- content start -->
                    <div class="content">
                        <!-- recommend-news start -->
                        <ul class="recommend-news">
                            <li>
                                <a href="https://www.shiguangkey.com/course/2432" target="_blank">
                                    <div class="recommend-thumbnail">
                                        <img src={{% static "images/python_gui.jpg" %}} alt="title">
                                    </div>
                                    <p class="info">Python GUI 教程 25行代码写一个小闹钟</p>
                                </a>
                            </li>

                            <li>
                                <a href="https://www.shiguangkey.com/course/2432" target="_blank">
                                    <div class="recommend-thumbnail">
                                        <img src={{% static "images/python_advanced.jpg" %}} alt="title">
                                    </div>
                                    <p class="info">python高性能编程方法一</p>
                                </a>
                            </li>

                            <li>
                                <a href="https://www.shiguangkey.com/course/2432" target="_blank">
                                    <div class="recommend-thumbnail">
                                        <img src={{% static "images/jichujiaochen.jpeg" %}} alt="title">
                                    </div>
                                    <p class="info">python基础 split 和 join函数比较</p>
                                </a>
                            </li>
                        </ul>
                        <!-- recommend-news end -->

                        <!--  news-nav start-->
                        <nav class="news-nav">
                            <ul class="clearfix">
                                <li class="active"><a href="javascript:void(0)">最新资讯</a></li>

                                <li><a href="javascript:void(0)" data-id="1">python框架</a>
                                </li>

                                <li><a href="javascript:void(0)" data-id="2">Python基础</a>
                                </li>

                                <li><a href="javascript:void(0)" data-id="3">Python高级</a>
                                </li>

                                <li><a href="javascript:void(0)" data-id="4">Python函数</a>
                                </li>

                                <li><a href="javascript:void(0)" data-id="5">PythonGUI</a>
                                </li>

                                <li><a href="javascript:void(0)" data-id="6">Linux教程</a>
                                </li>

                            </ul>
                        </nav>
                        <!--  news-nav end -->

                        <!-- news-contain start -->
                        <div class="news-contain">
                            <ul class="news-list">

                                <li class="news-item">
                                    <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                                       target="_blank">
                                        <img src={{% static "images/linux.jpg" %}} alt="linux查找文件或目录命令"
                                             title="linux查找文件或目录命令">
                                    </a>
                                    <div class="news-content">
                                        <h4 class="news-title"><a
                                                href="#">linux查找文件或目录命令</a>
                                        </h4>
                                        <p class="news-details">linux查找文件或目录命令,前提:知道文件或者目录的具体名字,例如:sphinx.conffind
                                            查找find / -name
                                            dirname 查找目录find -name...</p>
                                        <div class="news-other">
                                            <span class="news-type">Linux教程</span>
                                            <span class="news-time">11/11 18:24</span>
                                            <span class="news-author">python</span>
                                        </div>
                                    </div>
                                </li>

                                <li class="news-item">
                                    <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                                       target="_blank">
                                        <img src={{% static "images/linux.jpg" %}} alt="linux下svn命令的使用"
                                             title="linux下svn命令的使用">
                                    </a>
                                    <div class="news-content">
                                        <h4 class="news-title"><a
                                                href="https://www.shiguangkey.com/course/2432/887">linux下svn命令的使用</a>
                                        </h4>
                                        <p class="news-details">1、将文件checkout到本地目录svn checkout path(path是服务器上的目录) 例如:svn
                                            checkout
                                            svn://192.168.1.1/pro/domain 简写:svn co2、往版本库中添加新的文件 svn addfile 例如:svn add
                                            te...</p>
                                        <div class="news-other">
                                            <span class="news-type">Linux教程</span>
                                            <span class="news-time">11/11 18:24</span>
                                            <span class="news-author">python</span>
                                        </div>
                                    </div>
                                </li>

                                <li class="news-item">
                                    <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                                       target="_blank">
                                        <img src={{% static "images/linux.jpg" %}} alt="实现linux和windows文件传输"
                                             title="实现linux和windows文件传输">
                                    </a>
                                    <div class="news-content">
                                        <h4 class="news-title"><a
                                                href="https://www.shiguangkey.com/course/2432/886">实现linux和windows文件传输</a>
                                        </h4>
                                        <p class="news-details">
                                            其实这个题目有点大,这里介绍的只是linux和windows文件传输中的一种,但是这种方法却非常实用,那就是:ZModem协议具体是linux命令是:rz...</p>
                                        <div class="news-other">
                                            <span class="news-type">Linux教程</span>
                                            <span class="news-time">11/11 18:24</span>
                                            <span class="news-author">python</span>
                                        </div>
                                    </div>
                                </li>

                                <li class="news-item">
                                    <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                                       target="_blank">
                                        <img src={{% static "images/linux.jpg" %}} alt=".htaccess配置详解"
                                             title=".htaccess配置详解">
                                    </a>
                                    <div class="news-content">
                                        <h4 class="news-title"><a
                                                href="https://www.shiguangkey.com/course/2432">.htaccess配置详解</a>
                                        </h4>
                                        <p class="news-details">  .htaccess文件设置基础教程 如果你设置好了比如常用的404页面 301重定向
                                            页面还有500页面等会设置了
                                            无非对你的seo技术有很大帮助那么 .htaccess文件到底怎么设置呢  - .htaccess 文件(或者&quot;分布式...</p>
                                        <div class="news-other">
                                            <span class="news-type">Linux教程</span>
                                            <span class="news-time">11/11 18:24</span>
                                            <span class="news-author">python</span>
                                        </div>
                                    </div>
                                </li>

                                <li class="news-item">
                                    <a href="https://www.shiguangkey.com/course/2432" class="news-thumbnail"
                                       target="_blank">
                                        <img src={{% static "images/linux.jpg" %}} alt="使用nohup命令让linux程序后台运行"
                                             title="使用nohup命令让linux程序后台运行">
                                    </a>
                                    <div class="news-content">
                                        <h4 class="news-title"><a
                                                href="https://www.shiguangkey.com/course/2432">使用nohup命令让linux程序后台运行</a>
                                        </h4>
                                        <p class="news-details">使用nohup让程序永远后台运行Unix/Linux下一般比如想让某个程序在后台运行,很多都是使用 &amp;
                                            在程序结尾来让程序自动运行。比如我们要运行mysql在后台:/usr/local/mysql/bin/mysqld_safe --user=mysql
                                            &amp;但是...</p>
                                        <div class="news-other">
                                            <span class="news-type">Linux教程</span>
                                            <span class="news-time">11/11 18:24</span>
                                            <span class="news-author">python</span>
                                        </div>
                                    </div>
                                </li>

                            </ul>
                        </div>
                        <!-- news-contain end -->

                        <!-- btn-more start -->
                        <a href="javascript:void(0);" class="btn-more">加载更多</a>
                        <!-- btn-more end -->
                    </div>
                    <!-- content end -->
                </div>
            {{% endblock main_contain %}}
            <!-- main-contain  end -->

            <!-- side start -->
            {{% block side %}}
                <aside class="side">
                    <div class="side-activities">
                        <h3 class="activities-title">在线课堂<a href="javascript:void(0)">更多</a></h3>
                        <div class="activities-img">
                            <a href="javascript:void(0);" target="_blank">
                                <img src={{% static "images/english.jpg" %}} alt="title">
                            </a>
                            <p class="activities-tips">对话国外小姐姐</p>
                        </div>
                        <ul class="activities-list">
                            <li>
                                <a href="javascript:void(0);" target="_blank">
                                    <span class="active-status active-start">报名中</span>
                                    <span class="active-title"><a
                                            href="https://www.shiguangkey.com/course/2432"> Django 项目班</a></span>
                                </a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank">
                                    <span class="active-status active-end">已结束</span>
                                    <span class="active-title"><a
                                            href="https://www.shiguangkey.com/course/2321">Python入门基础班</a></span>
                                </a>
                            </li>
                        </ul>
                    </div>
                    <div class="side-attention clearfix">
                        <h3 class="attention-title">关注我</h3>
                        <ul class="side-attention-address">
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i
                                        class="PyWhich py-GitHub"></i>XinLan</a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-zhihu"
                                                                                 style="color:rgb(0, 108, 226);"></i>XinLan</a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-weibo"
                                                                                 style="color:rgb(245,92,110);"></i>XinLan</a>
                            </li>
                        </ul>
                        <div class="side-attention-qr">
                            <p>扫码关注</p>
                        </div>
                    </div>

                    <div class="side-hot-recommend">
                        <h3 class="hot-recommend">热门推荐</h3>
                        <ul class="hot-news-list">

                            <li>
                                <a href="javascript:void(0)" class="hot-news-contain clearfix">
                                    <div class="hot-news-thumbnail">
                                        <img src={{% static "images/python_web.jpg" %}}
                                                     alt="">
                                    </div>
                                    <div class="hot-news-content">
                                        <p class="hot-news-title">Django调试工具django-debug-toolbar安装使用教程</p>
                                        <div class="hot-news-other clearfix">
                                            <span class="news-type">python框架</span>
                                            <!-- 自带的 -->
                                            <time class="news-pub-time">11月11日</time>
                                            <span class="news-author">python</span>
                                        </div>
                                    </div>
                                </a>
                            </li>

                        </ul>
                    </div>


                </aside>
            {{% endblock side %}}
            <!-- side end -->
        </div>
    </main>
{{% endblock main %}}
<!-- main end -->

<!-- footer start -->
{{% block foot %}}
    <footer id="footer">
        <div class="footer-box">
            <div class="footer-content">
                <p class="top-content">
                    <span class="link">
                        <a href="javascript:void(0)">关于Python</a> |
                        <a href="javascript:void(0)">我就是我</a> |
                        <a href="javascript:void(0)">人生苦短</a> |
                        <a href="javascript:void(0)">我用Python</a>
                    </span>
                    <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> XinLan</span>
                </p>
                <p class="bottom-content">
                    <span>地址: xxxx</span>
                    <span>联系方式: <a href="tel:400-1567-315">400-1567-315</a> (24小时在线)</span>
                </p>
            </div>
            <p class="copyright-desc">
                Copyright &copy; 2008 - 2018 xxx有限公司. All Rights Reserved
            </p>
        </div>
    </footer>
{{% endblock foot %}}
<!-- footer end -->

{{% block script %}}
    <script src="{{% static 'js/news/index.js' %}}"></script>
    <script src="{{% static 'js/base/common.js' %}}"></script>
{{% endblock script %}}

③doc.html文件

{{% extends 'base/base.html' %}}
{{% load static %}}


{{% block title %}}
    payInfo
{{% endblock title %}}

{{% block css %}}
    <link rel="stylesheet" href="{{% static 'css/doc/docDownload.css' %}}">
    <script>IMemuIndex = 2</script>
{{% endblock css %}}
<body>


<!-- main start -->
{{% block main %}}
    <main id="main">
        <div class="w1200 clearfix">
            <!-- main-contain start  -->
            {{% block main_contain %}}
                <div class="main-contain ">
                    <div class="banner">
                        <img src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1802845035,3786939119&fm=26&gp=0.jpg"
                             alt="">
                    </div>
                    <div class="pay-doc-contain">
                        <ul class="pay-list">

                            <li class="pay-item">
                                <div class="pay-img doc"></div>
                                <div class="d-contain">
                                    <p class="doc-title">python cookbook 3.0 教程</p>
                                    <p class="doc-desc">强烈推荐的python 教程。</p>

                                    <!-- /www/?xxx -->
                                    <a href="#" class="pay-price">下载</a>
                                </div>
                            </li>

                            <li class="pay-item">
                                <div class="pay-img doc"></div>
                                <div class="pay-contain">
                                    <p class="pay-title">流畅的Python</p>
                                    <p class="pay-desc">【本书特色】

                                        本书由奋战在Python开发一线近20年的Luciano Ramalho执笔,Victor Stinner、Alex
                                        Martelli等Python大咖担纲技术审稿人,从语言设计层面剖析编程细节,兼顾Python 3和Python
                                        2,告诉你Python中不亲自动手实践就无法理解的语言陷阱成因和解决之道,教你写出风格地道的Python代码。</p>

                                    <!-- /www/?xxx -->
                                    <a href="#" class="pay-price">下载</a>
                                </div>
                            </li>

                            <li class="pay-item">
                                <div class="pay-img doc"></div>
                                <div class="pay-contain">
                                    <p class="pay-title">深入Flask</p>
                                    <p class="pay-desc">深入Flask,强烈推荐!</p>

                                    <!-- /www/?xxx -->
                                    <a href="#" class="pay-price">下载</a>
                                </div>
                            </li>

                        </ul>
                    </div>
                </div>
            {{% endblock main_contain %}}
            <!-- main-contain  end -->

            <!-- side start -->
            {{% block side %}}
                <aside class="side">
                    <div class="side-activities">
                        <h3 class="activities-title">在线课堂<a href="javascript:void(0)">更多</a></h3>
                        <div class="activities-img">
                            <a href="javascript:void(0);" target="_blank">
                                <img src="https://res.shiguangkey.com//file/201804/26/20180426142628123364782.jpg"
                                     alt="title">
                            </a>
                            <p class="activities-tips">对话国外小姐姐</p>
                        </div>
                        <ul class="activities-list">
                            <li>
                                <a href="javascript:void(0);" target="_blank">
                                    <span class="active-status active-start">报名中</span>
                                    <span class="active-title"><a href="https://www.shiguangkey.com/course/2432"> Django 项目班</a></span>
                                </a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank">
                                    <span class="active-status active-end">已结束</span>
                                    <span class="active-title"><a href="https://www.shiguangkey.com/course/2321">Python入门基础班</a></span>
                                </a>
                            </li>
                        </ul>
                    </div>
                    <div class="side-attention clearfix">
                        <h3 class="attention-title">关注我</h3>
                        <ul class="side-attention-address">
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i
                                        class="PyWhich py-GitHub"></i>XinLan</a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-zhihu"
                                                                                 style="color:rgb(0, 108, 226);"></i>XinLan</a>
                            </li>
                            <li>
                                <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-weibo"
                                                                                 style="color:rgb(245,91,94)"></i>XinLan</a>
                            </li>
                        </ul>
                        <div class="side-attention-qr">
                            <p>扫码关注</p>
                        </div>
                    </div>


                </aside>
            {{% endblock side %}}
            <!-- side end -->


        </div>
    </main>
{{% endblock main %}}

<!-- main end -->

<!-- footer start -->
{{% block foot %}}
    <footer id="footer">
        <div class="footer-box">
            <div class="footer-content">
                <p class="top-content">
                    <span class="link">
                        <a href="javascript:void(0)">关于Python</a> |
                        <a href="javascript:void(0)">我就是我</a> |
                        <a href="javascript:void(0)">人生苦短</a> |
                        <a href="javascript:void(0)">我用Python</a>
                    </span>
                    <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> XinLan</span>
                </p>
                <p class="bottom-content">
                    <span>地址: xxxx</span>
                    <span>联系方式: <a href="tel:400-1567-315">400-1567-315</a> (24小时在线)</span>
                </p>
            </div>
            <p class="copyright-desc">
                Copyright &copy; 2008 - 2018 xxx有限公司. All Rights Reserved
            </p>
        </div>
    </footer>
{{% endblock foot %}}
<!-- footer end -->
{{% block script %}}
    <script src="{{% static 'js/base/common.js' %}}"></script>
{{% endblock script %}}

④user/login.html文件

{{% extends 'base/base.html' %}}
{{% load static %}}

{{% block css %}}
    <link rel="stylesheet" href="{{% static 'css/user/auth.css' %}}">
{{% endblock css %}}

{{% block title %}}
    login
{{% endblock %}}

<!-- container start -->
{{% block main %}}
    <main id="container">
        <div class="login-contain">
            <div class="top-contain">
                <h4 class="please-login">请登录</h4>
{#                <a href="javascript:void(0);" class="register">立即注册 &gt;</a>#}
                <a href="{{% url 'user:register' %}}" class="register">立即注册 &gt;</a>
            </div>
            <form action="" method="post" class="form-contain">
                <div class="form-item">
                    <input type="tel" placeholder="请输入手机号" name="mobile" class="form-control" autocomplete="off">
                </div>
                <div class="form-item">
                    <input type="password" placeholder="请输入密码" name="password" class="form-control">
                </div>
                <div class="form-item clearfix">
                    <label>
                        <input type="checkbox" name="remember">
                        <span>记住我</span>
                    </label>
                    <a href="javascript:void(0);" class="forget-password">忘记密码?</a>
                </div>
                <div class="form-login">
                    <input type="submit" value="登录" class="login-btn">
                </div>
            </form>
        </div>
    </main>
{{% endblock main %}}
<!-- container end -->

<!-- footer start -->
{{% block foot %}}
    <footer id="footer">
        <div class="footer-box">
            <div class="footer-content">
                <p class="top-content">
                    <span class="link">
                        <a href="javascript:void(0)">关于Python</a> |
                        <a href="javascript:void(0)">我就是我</a> |
                        <a href="javascript:void(0)">人生苦短</a> |
                        <a href="javascript:void(0)">我用Python</a>
                    </span>
                    <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> XinLan</span>
                </p>
                <p class="bottom-content">
                    <span>地址: xxxx</span>
                    <span>联系方式: <a href="tel:400-1567-315">400-1567-315</a> (24小时在线)</span>
                </p>
            </div>
            <p class="copyright-desc">
                Copyright &copy; 2008 - 2018 xxx有限公司. All Rights Reserved
            </p>
        </div>
    </footer>
{{% endblock %}}

<!-- footer end -->
{{% block script %}}
{#<script src="{{% static 'js/user/register.js' %}}"></script>#}
<script src="{{% static 'js/user/login.js' %}}"></script>
{{% endblock script %}}

⑤register.html文件

{{% extends 'base/base.html' %}}
{{% load static %}}

{{% block title %}}
    Register
{{% endblock title %}}
{{% block css %}}
  <link rel="stylesheet" href="{{% static 'css/user/auth.css' %}}">
    <script>IMemuIndex=false</script>
{{% endblock css %}}


<!-- container start -->
{{% block main %}}
<main id="container">
  <div class="register-contain">
    <div class="top-contain">
      <h4 class="please-register">请注册</h4>
{#      <a href="javascript:void(0);" class="login">立即登录 &gt;</a>#}
      <a href="{{% url 'user:login' %}}" class="login">立即登录 &gt;</a>
    </div>
    <form action="" method="post" class="form-contain">
        {{% csrf_token %}}

      <div class="form-item">
        <input type="text" placeholder="请输入用户名" name="username" class="form-control" id="username" autocomplete="off">
      </div>
      <div class="form-item">
        <input type="password" placeholder="请输入密码" name="password" class="form-control">
      </div>
      <div class="form-item">
        <input type="password" placeholder="请输入确认密码" name="password_repeat" class="form-control">
      </div>
      <div class="form-item">
        <input type="tel" placeholder="请输入手机号" name="mobile" class="form-control" autocomplete="off" >
      </div>
      <div class="form-item">
        <input type="text" placeholder="请输入图形验证码" name="captcha_graph" class="form-captcha">
        <a href="javascript:void(0);" class="captcha-graph-img">
          <img src="{{% url 'image_code:image_code' %}}" alt="验证码" title="点击刷新">
        </a>
      </div>
      <div class="form-item">
        <input type="text" placeholder="请输入短信验证码" name="sms_captcha" class="form-captcha" autocomplete="off">
        <a href="javascript:void(0);" class="sms-captcha" title="发送验证码">获取短信验证码</a>
      </div>
      <div class="form-item">
        <input type="submit" value="立即注册" class="register-btn">
      </div>
    </form>
  </div>
</main>
{{% endblock main %}}
<!-- container end -->

<!-- footer start -->
{{% block foot %}}
<footer id="footer">
  <div class="footer-box">
    <div class="footer-content">
      <p class="top-content">
                    <span class="link">
                        <a href="javascript:void(0)">关于Python</a> |
                        <a href="javascript:void(0)">我就是我</a> |
                        <a href="javascript:void(0)">人生苦短</a> |
                        <a href="javascript:void(0)">我用Python</a>
                    </span>
        <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> XinLan</span>
      </p>
      <p class="bottom-content">
        <span>地址: xxxx</span>
        <span>联系方式: <a href="tel:400-1567-315">400-1567-315</a> (24小时在线)</span>
      </p>
    </div>
    <p class="copyright-desc">
      Copyright &copy; 2008 - 2018 xxx有限公司. All Rights Reserved
    </p>
  </div>
</footer>
{{% endblock foot %}}
<!-- footer end -->
{{% block script %}}
{#    <script src="{{% static 'js/base/message.js' %}}"></script>#}
    <script src="{{% static 'js/user/register.js' %}}"></script>
    <script src="{{% static 'js/base/common.js' %}}"></script>
{{% endblock script %}}

⑥course.html文件

{{% extends 'base/base.html' %}}
{{% load static %}}



{{% block title %}}
    coursePage
{{% endblock title %}}
{{% block css %}}
    <link rel="stylesheet" href="{{% static 'css/course/course.css' %}}">
    <script>IMemuIndex = 1</script>
{{% endblock css %}}



{{% block main %}}
    <main id="course-container">
        <div class="w1200">
            <ul class="course-list">

                <li class="course-item">
                    <a href="https://www.shiguangkey.com/course/2432">
                        <img class="course-img" src={{% static "images/python_advanced.jpg" %}}
                                alt="潭州英语">
                        <div class="course-content">
                            <p class="course-info">python 全栈开发</p>
                            <p class="course-author">不动(python金牌讲师)</p>
                            <p class="course-price free">免费</p>
                        </div>
                    </a>
                </li>

                <li class="course-item">
                    <a href="https://www.shiguangkey.com/course/2432">
                        <img class="course-img" src={{% static "images/python_web.jpg" %}}
                                alt="潭州英语">
                        <div class="course-content">
                            <p class="course-info">django 项目</p>
                            <p class="course-author">XinLan(python讲师)</p>
                            <p class="course-price free">免费</p>
                        </div>
                    </a>
                </li>

            </ul>
        </div>
    </main>
{{% endblock main %}}

<!-- footer start -->
{{% block foot %}}
    <footer id="footer">
        <div class="footer-box">
            <div class="footer-content">
                <p class="top-content">
                    <span class="link">
                        <a href="javascript:void(0)">关于Python</a> |
                        <a href="javascript:void(0)">我就是我</a> |
                        <a href="javascript:void(0)">人生苦短</a> |
                        <a href="javascript:void(0)">我用Python</a>
                    </span>
                    <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> XinLan</span>
                </p>
                <p class="bottom-content">
                    <span>地址: xxxx</span>
                    <span>联系方式: <a href="tel:400-1567-315">400-1567-315</a> (24小时在线)</span>
                </p>
            </div>
            <p class="copyright-desc">
                Copyright &copy; 2008 - 2018 xxx有限公司. All Rights Reserved
            </p>
        </div>
    </footer>
{{% endblock foot %}}
<!-- footer end -->
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
{{% block script %}}
    <script src="{{% static 'js/base/common.js' %}}"></script>
{{% endblock script %}}

3.2 创建static文件夹

创建static文件夹用来存放静态文件(css/js/img/)

文件结构,每个模板的静态文件创建并放到不同的文件夹中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8aQddVf6-1588223223861)(/1565757977142.png)]

①css文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2uT6VJ3e-1588223223862)(/1565758060491.png)]

②js文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22AmnnTu-1588223223863)(/1565758133004.png)]

③images文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7GDefwPC-1588223223864)(/1565758178816.png)]

3.3 编写views文件和路由配置(编写urls文件)

以news为例,其他类推

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xueBXiwF-1588223223865)(/1565758673646.png)]

3.3.1 views.py

from django.shortcuts import render

# Create your views here.

# news主页
def index(request):
    return render(request, 'news/index.html')

3.3.2 urls.py

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GndDBNH0-1588223223866)(/1565758901074.png)]

# 主路由

from django.contrib import admin
from django.urls import path, include
from . import views

urlpatterns = [
    #
    # path('admin', admin.site.urls),
    # 分配news子路由
    path('news/', include('news.urls')),
    # 分配course子路由
    path('course/', include('course.urls')),
    # 分配doc子路由
    path('doc/', include('doc.urls')),
    # 分配user子路由
    path('user/', include('user.urls')),
    # 分配image_code子路由
    path('verification/', include('verification.urls'))
]
# news的路由
from django.urls import path

from . import views

app_name = 'news'

urlpatterns = [
    path('', views.index, name='index')
]

3.4 填上缺少的block

根据每个页面的不同填上不同的内容

3.1.3中已经填上

四、注册功能

4.1 模型设置

4.1.1 字段分析

-用户名

-密码

-手机号

-邮箱

4.1.2 模型设计

django的强大之处在于开发效率高,内置了权限模块之类的很多常用功能。在开始一个新的django项目时,如果权限模块中的User模型不满足项目要求,我们需要扩展或者自定义User模型。

扩展User模型有两种方法

  1. 如果你不需要改变数据库存储内容,只是改变行为,那么可以建立有一个基于User模型的代理模型。
  2. 如果想存储与User模型关联的信息,可以使用OneToOneField到包含其他信息字段的模型。这种one-to-one模型经常被称作Profile模型,因为它可能存储站点用户的非身份验证的相关信息。例如:
from django.contrib.auth.models import User

class Employee(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.CharField(max_length=100)

自定义User模型

如果不想使用django内置的权限系统,当然你需要自定义用户模型,这种情况不讨论。当然也不建议这么做,django内置权限系统有大的自定义功能扩展,而不是重复造轮子。

开启一个新项目,官方强烈推荐用户自定义用户模型,即是默认的用户模型目前已经足够,但是未来可能会要扩展。

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

注意:不要忘记在settings.py中设置AUTH_USER_MODEL指向它。

一旦已经创立数据库表之后再去修改AUTH_USER_MODEL,会困难很多,因为它会影响外键和多对多关系。这个改动并不能自动完成,需要手动修复(巨坑)。

官方文档

4.1.3 user的models文件

设置models文件来建立模型,这里用Django内置的User一定要注意!!!!!

我们重写了User类,我们要让整个框架知道我们重写了User类,就要在settings文件中配置

②settings.py

# 自定义用户模型
AUTH_USER_MODEL = 'user.User'

①models.py

from django.db import models
# 导入Django的内置User模型类 取个别名因为下面会用到相同的名冲突
from django.contrib.auth.models import AbstractUser, UserManager as _UserManager


# 这个类继承_UserManager类
class UserManger(_UserManager):
    """"
      修改必须输入email的这个行为
      重写create_superuser方法,去掉创建super_user必须的email的行为
    """
    def create_superuser(self, username,  password, email=None, **extra_fields):
        super().create_superuser(username=username, password=password, email=email, **extra_fields)


class User(AbstractUser):
    """
    自定义user模型, 添加手机,email_active
    """
    mobile = models.CharField(verbose_name='手机号', max_length=11, unique=True, help_text='手机号', error_messages={'unique': '此手机号码已注册'})

    email_active = models.BooleanField(verbose_name='邮箱状态', default=False)

    class Meta:
        db_table = 'tb_user'      # 指定数据库表名
        verbose_name = '用户'      # 在admin站点中的显示名称
        verbose_name_plural = verbose_name    # 复数

    # 返回的字段名
    def __str__(self):
        return self.username

    # 通过createsuperuser 这个命令创建用户时,需要的字段
    REQUIRED_FIELDS = ['mobile']

    # 通过管理器来执行
    objects = UserManger()

③迁移

# 数据库迁移 
python manage.py makemigrations
# 迁移生效
python manage.py migrate

我们这里用的是自己写的admin后台,没有用Django内置的后台

所以我们将settings.py—-> INSTALLED_APPS—->django.contrib.admin 注释掉(删掉),urls.py 主路由中的admin路由也一定要注释掉,要不然无法迁移!!!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nK23FfrY-1588223223867)(/1565761073556.png)]

去掉admin路由

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iYU6CJHf-1588223223868)(/1565761243867.png)]

④创建一个用户

(tzproject) ~/code/tztz$ python manage.py createsuperuser
用户名: admin
手机号: 158xxxxxxxx
Password: 
Password (again): 
密码长度太短。密码必须包含至少 8 个字符。
这个密码太常见了。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

4.2 功能需求分析

4.2.1 接口设计思路

  • 分析业务逻辑,明确在这个业务中需要涉及到几个相关子业务,将每个子业务当做一个接口来设计
  • 分析接口的功能任务,明确接口的访问方式与返回数据:
    • 接口的请求方式,如GET,POST,PUT等
    • 接口的URL路径定义
    • 需要接受的参数及参数格式(如路径参数,查询字符串,请求表单,JSON等)
    • 返回的数据及数据格式

4.2.2 注册功能分析

①流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f1t4hlXN-1588223223869)(/登录注册流程图.png)]

②功能

根据流程图结构分析注册业务包含以下功能

  • 注册页面
  • 图片验证码
  • 用户名检测是否注册
  • 手机号检测是否注册
  • 短信验证码
  • 注册保存用户数据

因为图片验证码,短信验证码考虑到后续可能会在其他业务中用到,因此将验证码功能独立出来,创建一个新应用verification用来校验

4.3 图形验证码功能实现

4.3.1 接口设计

接口说明:

类目 说明
请求方式 GET
url定义 /verification/image_code/
参数格式 查询参数

参数说明:

参数名 类型 是否必须 描述
rand 字符串 随机值

4.3.2后端代码

①将验证码生成模块复制到根目录utils文件夹下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UejbHgeM-1588223223870)(/1565762750062.png)]

②创建新的app verification专门用来处理验证

记得在settings文件中注册app

cd ~/code/tztz/apps/
python ../manage.py startapp verification

③verification/views.py代码如下:

import logging

from django.http import HttpResponse

from utils.captcha.captcha import captcha
from . import constants
# 拿到Django的日志器
logger = logging.getLogger('django')


def image_code_view(request):
    """
    生成图片验证码
    url:/image_code/
    :param request:
    :return:
    """
    # 返回两个值,一个是生成的text文本,另一个是image图片
    text, image = captcha.generate_captcha()
    # 将验证码存入session中name为'image_code'
    request.session['image_code'] = text
    # 设置session过期时间 单位秒
    # constants设置常量便于后期修改数值
    request.session.set_expiry(constants.IMAGE_CODE_EXPIRES)
    # 将生成的image_code的text记录到日志器中
    logger.info('Image code:{}'.format(text))
    # 返回响应为二进制数据,数据类型为image/jpg,这样返回的数据可以直接渲染到浏览器页面上
    return HttpResponse(content=image, content_type='image/jpg')

④verification/urls.py代码如下:

from django.urls import path
from . import views
# url的命名空间
app_name = 'verification'

urlpatterns = [
    path('image_code/', views.image_code_view, name='image_code'),
]

⑤主urls文件

from django.contrib import admin
from django.urls import path, include
from . import views

urlpatterns = [
    #
    # path('admin', admin.site.urls),
    # 分配news子路由
    path('news/', include('news.urls')),
    # 分配course子路由
    path('course/', include('course.urls')),
    # 分配doc子路由
    path('doc/', include('doc.urls')),
    # 分配user子路由
    path('user/', include('user.urls')),
    # 分配verification子路由
    path('verification/', include('verification.urls'))
]

4.4 注册页面

4.4.1 接口设计

①接口说明

类目 说明
请求方式 GET
url定义 /verification/register/
参数格式 无参数

②返回结果:

注册页面

4.4.2后端代码

①user/views.py

from django.shortcuts import render
from django.views import View


class RegisterView(View):
    def get(self, request):
        return render(request, 'user/register.html')

②user/urls.py

from django.urls import path, include
from . import views
app_name = 'user'

urlpatterns = [
    path('register/', views.RegisterView.as_view(), name='register')
]

③根urls.py

from django.urls import path, include

urlpatterns = [
    
    path('user/', include('user.urls'))
]

4.4.3 前端页面代码

①user/register.html代码如:

3.1.3中的⑤

4.4.4 js代码

点击验证码图片刷新的js代码如下:

$(() => {

    // 1.点击刷新图像验证码
    let $img = $('.form-contain .form-item .captcha-graph-img img');


    $img.click(function () {
        // 加入rand随机数防止浏览器认为是相同的请求
        $img.attr('src', '/verification/image_code/?rand=' + Math.random())
    });
});

4.5 json响应数据结构设计

4.5.1 结构设计

实际项目是多人协同开发,特别是前后端交互,后端返回数据结构要一致。

{"errno": "0", "errmsg": "OK", "data": {...}}
字段 类型 说明
errno 字符串 错误编码
errmsg 字符串 错误信息
data json 返回数据

在项目根目录中utils文件夹下创建res_code.py文件,用于定义错误编码,代码如下:

class Code:
    OK = "0"
    DBERR = "4001"
    NODATA = "4002"
    DATAEXIST = "4003"
    DATAERR = "4004"
    METHERR = "4005"
    SMSERROR = "4006"
    SMSFAIL = "4007"

    SESSIONERR = "4101"
    LOGINERR = "4102"
    PARAMERR = "4103"
    USERERR = "4104"
    ROLEERR = "4105"
    PWDERR = "4106"

    SERVERERR = "4500"
    UNKOWNERR = "4501"


error_map = {
    Code.OK: "成功",
    Code.DBERR: "数据库查询错误",
    Code.NODATA: "无数据",
    Code.DATAEXIST: "数据已存在",
    Code.DATAERR: "数据错误",
    Code.METHERR: "方法错误",
    Code.SMSERROR: "发送短信验证码异常",
    Code.SMSFAIL: "发送短信验证码失败",

    Code.SESSIONERR: "用户未登录",
    Code.LOGINERR: "用户登录失败",
    Code.PARAMERR: "参数错误",
    Code.USERERR: "用户不存在或未激活",
    Code.ROLEERR: "用户身份错误",
    Code.PWDERR: "密码错误",

    Code.SERVERERR: "内部错误",
    Code.UNKOWNERR: "未知错误",
}

4.5.2 .快捷方法

为了方便定义一个快捷方法,在utils目录下创建json_res.py文件中添加如下代码:

from django.http import JsonResponse

from .res_code import Code


# 返回json——Response数据
def json_response(errno=Code.OK, errmsg='', data=None, kwargs=None):
    json_dict = {
        'errno': errno,
        'errmsg': errmsg,
        'data': data,
    }
    # 判断是否有kwargs 并且是否为字典
    if kwargs and isinstance(kwargs, dict):
        # 将kwargs更新到json_dict中
        json_dict.update(kwargs)
    return JsonResponse(json_dict)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7nppCtHa-1588223223871)(/1565765018299.png)]

4.6 判断用户是否注册的功能

4.6.1 接口设计

①接口说明

类目 说明
请求方法 GET
url定义 verification/username/(?P<username>\w{5,20})/
参数格式 url路径参数

②参数说明

参数名 类型 是否必须 描述
username 字符串 输入的用户名

返回结果:

 {
        'errno': errno,
        'errmsg': errmsg,
        'data':  {
            'username': username,
            'count': User.objects.filter(username=username).count()
                },
    }

4.6.2 后端代码

①创建新的app verification专门用来处理验证

cd ~/code/tztz/apps/
python ../manage.py startapp verification

② verification/views.py代码

from user.models import User
from utils.json_res import json_response


# 验证用户名
def check_username(request, username):
  
    data = {
        'username': username,
        'count': 
        # 去数据库中过滤username=username的数量有多少
        User.objects.filter(username=username).count()
    }
    # 返回json数据
    return json_response(data=data)

③verification/urls.py代码

from django.urls import path, re_path

from . import views

app_name = 'image_code'

urlpatterns = [
    # 图片验证码生成
    path('image_code/', views.image_code, name='image_code'),
    # 用户名校验
    re_path('username/(?P<username>\w{5,20})/', views.check_username, name='check_username'),
   
]

4.6.3 前端页面代码

user/register.html代码如3.1.3中的⑤

4.6.4 前端js代码

$(()=>{
    // 定义状态变量
    let isUsernameReady = false,
        isPasswordReady = false,
        isMobileReady = false,
    
    
        // 2. 用户名校验, 光标离开就进行校验
    let $username = $('#username');
    // blur()光标移开username的反应
    $username.blur(fnCheckUsername);

    //反应是
    function fnCheckUsername() {
        // 进行校验之前先把值设置为false
        isUsernameReady = false;
        // 拿到username的值
        let sUsername = $username.val();
        if (sUsername === '') {
            message.showError('用户名不能为空');
            return
        }
        if (!(/^\w{5,20}$/).test(sUsername)) {
            message.showError('请输入5-11位的用户名');

        }
        $.ajax({
            // 前面的'/'一定不能少!!!!!!!
            // 前面的'/'一定不能少!!!!!!!
            // 前面的'/'一定不能少!!!!!!!
            url: '/verification/username/' + sUsername + '/',
            type: 'GET',
            dataType: 'json',
            success: function (res) {
                if (res.data.count === 0) {
                    message.showInfo(res.data.username + '可以注册');
                } else {
                    message.showError(res.data.username + '该用户名已经注册');

                }
            }
        })
    }
});

4.7 判断手机号码是否注册功能

4.7.1 接口设计

接口说明:

类目 说明
请求方法 GET
url定义 verification/mobile/(?P<mobile>\1[3-9]\d{9})/
参数格式 url路径参数

参数说明:

参数名 类型 是否必须 描述
moblie 字符串 输入的手机号码

返回结果:

json_dict = {
        'errno': errno,
        'errmsg': errmsg,
        'data': {
            'mobile': 138xxxxxx,
            'count': 查询数据库中的手机号码数量
                },
    }

4.7.2 后端代码

①verification/views.py代码

import logging
import random

from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from django.views import View
from django_redis import get_redis_connection

from utils.captcha.captcha import captcha
from . import constants
from user.models import User
from utils.res_code import json_response, Code, error_map
from .forms import CheckImageForm


# 手机号码校验
def check_mobile(request, mobile):
    """
       校验手机号是否存在
       url:/verification/mobile/(?P<mobile>1[3-9]\d{9})/
       :param request:
       :param mobile:
       :return:
       """
    data = {
        'mobile': mobile,
        'count': 
        # 到数据库中查询手机数量
        User.objects.filter(mobile=mobile).count(),
    }
    return json_response(data=data)
    

② verification/urls.py

from django.urls import path, re_path

from . import views

app_name = 'image_code'

urlpatterns = [
    # 图片验证码生成
    path('image_code/', views.image_code, name='image_code'),
    # 用户名校验
    re_path('username/(?P<username>\w{5,20})/', views.check_username, name='check_username'),
    # 手机号码校验
    re_path('mobile/(?P<mobile>1[3-9]\d{9})/', views.check_mobile, name='check_mobile'),
    path('sms_code/', views.SmsCodeView.as_view(), name='sms_codeView')
]

4.7.3 前端js代码

// 定义状态变量
    let isUsernameReady = false,
        isPasswordReady = false,
        isMobileReady = false,
      


// 3. 手机号码校验
    let $mobile = $('input[name="mobile"]');
    $mobile.blur(fnCheckMobile);

    function fnCheckMobile(res) {
        // 先设置false
        isMobileReady = false;
        // 拿到input框里的value
        let sMobile = $mobile.val();
        // 进行判断
        if (sMobile === '') {
            message.showError(
                '手机号不能为空'
            );
            return;
        }
        if (!(/^1[3-9]\d{9}$/).test(sMobile)) {
            message.showError(
                '请输入正确的11位手机号码'
            );
            return;
        }
        if((/^1[3-9]\d{9}$/).test(sMobile)){
            message.showSuccess(
                '手机号码可以使用'
            );

        }

        // 方法一(推荐)
        $.ajax({
            url: '/verification/mobile/' + sMobile + '/',
            type: 'GET',
            dataType: 'json',
        })

            .done((res) => {
                // alert(res.data.count);
                if (res.data.count !== 0) {
                    // 拿到res传递过来的json数据res.mobile就是json数据中的data.mobile
                    message.showError(res.data.mobile + '已经注册,请重新输入!')

                } else {
                    message.showInfo(res.data.mobile + '可以使用');
                    isMobileReady = true
                }
            })
            .fail(() => {
                message.showError('服务器超时')
            });


        // 方法二
        //  $.ajax({
        //     url: '/verification/mobile/'+ sMobile + '/',
        //     type: 'GET',
        //     dataType: 'json',
        //     success: function (data) {
        //         // alert(data.count);
        //         if(data.count !== 0){
        //             message.showError(data.mobile + '已经注册,请重新输入!')
        //         }else {
        //             message.showInfo(data.mobile + '可以正常使用!');
        //             isMobileReady = true
        //         }
        //     },
        //     error: function (xhr, msg) {
        //         message.showError('服务器超时,请重试!')
        //     }
        // });

    }

4.8 获取短信验证码功能

4.8.1 业务流程分析

  • 检查图片验证码是否正确
  • 检查是否在60s内发送记录
  • 生成短信验证码
  • 发送短信
  • 保存短信验证码与发送记录

4.8.2 接口设计

接口说明:

类目 说明
请求方法 POST
url定义 verification/sms_code/
参数格式 表单

参数说明:

参数名 类型 是否必须 描述
moblie 字符串 用户输入的手机号码
captcha 字符串 用户输入的验证码文本

返回结果:

 {
        'errno': '0',
        'errmsg': '短信发送成功',
        
    }

4.8.3 后端代码

①verification/views.py代码如下:

import logging
import random

from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from django.views import View
from django_redis import get_redis_connection

from utils.captcha.captcha import captcha
from . import constants
from user.models import User
from utils.res_code import json_response, Code, error_map
from .forms import CheckImageForm


# 创建日志器
logger = logging.getLogger('django')

# 检验手机验证码
class SmsCodeView(View):
    """
    url: /sms_code/
    """

    # 校验手机号码
    def post(self, request):
        """
        生成短信验证码
        发送短信验证码
        保存在什么地方
        :param request:
        :return:
        """
        # 拿到checkImageForm里面的数据对象实例化给form
        form = CheckImageForm(request.POST, request=request)

        # 成功
        if form.is_valid():
            # 类似字典取值拿到mobile
            mobile = form.cleaned_data.get('mobile')
            # 随机生成4位数验证码,列表推导式
            sms_code = ''.join([random.choice('0123456789') for i in range(constants.SMS_CODE_LENGTH)])
            # 将生成的短信随机码打印出来
            logger.info('发送短信验证码【成功】:{} 【验证码】:{}'.format(mobile, sms_code))
            # print(sms_code)
            # 短信验证码缓存到Redis中,用Redis,设置时限
            # 创建短信验证码的发送记录的key
            sms_flag_key = 'sms_flag_{}'.format(mobile)
            # 创建短信验证码的值的发送记录的key
            sms_text_key = 'sms_text_{}'.format(mobile)
            # 保存验证码到Redis    alias是指哪一个库,之前在settings文件中配置的Redis缓存名字
            redis_conn = get_redis_connection(alias='verification')
            # 创建redis管道
            pl = redis_conn.pipeline()
            try:
                pl.setex(sms_flag_key, constants.SMS_CODE_INTERVAL, 1)
                pl.setex(sms_text_key, constants.SMS_CODE_EXPIRES*60, sms_code)
                pl.execute()
                return json_response(errmsg='短信验证码发送成功' )
            except Exception as e:
                logger.error('Redis执行异常: {}'.format(e))
                return json_response(errno=Code.UNKOWNERR, errmsg=error_map[Code.UNKOWNERR])

        # 失败
        else:
            # 将表单的报错信息拼接 可能不止一条
            err_msg_list = []
            # errors 是一个字典,通过values可以拿到所有值
            for item in form.errors.values():
                # 将拿到的item(值)添加到list中

                err_msg_list.append(item[0])

            # 这里加 的'/'是为了便于区分不同的报错信息
            err_msg_str = '/'.join(err_msg_list)
            print(err_msg_str)
            return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)

② verification/forms.py文件代码如下:

from django import forms
from django.core.validators import RegexValidator
from django_redis import get_redis_connection

from user.models import User

# 创建手机号码校验器
mobile_validator = RegexValidator(r'^1[3-9]\d{9}$', '手机号码格式不正确')


class CheckImageForm(forms.Form):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super().__init__(*args, **kwargs)

    # 这里的名字要和register.js中的json中传递回来的name一致
    mobile = forms.CharField(max_length=11, min_length=11, validators=[mobile_validator, ], error_messages={
        'min_length': '输入的手机号码长度有误',
        'max_length': '输入的手机号码长度有误',
        'required': '输入的手机号码不得为空',
    })

    # 这里的名字要和register.js中的json中传递回来的name一致
    captcha = forms.CharField(max_length=4, min_length=4, error_messages={
        'min_length': '输入的图片验证码长度有误',
        'max_length': '输入的图片验证码长度有误',
        'required': '图片验证码不得为空',
    })

    def clean(self):
        clean_data = super().clean()
        mobile = clean_data.get('mobile')
        captcha = clean_data.get('captcha')
        # 如果校验失败就会返回 none的结果
        # 如果前面的校验失败了的话就没有必要往下执行
        if mobile and captcha:
            # 拿到保存在session中的图形验证码的value
            image_code = self.request.session.get('image_code')
            # session中的图形验证码和request中发送过来的captcha不一样,返回'验证码错误'
            if image_code.upper() != captcha.upper():
                raise forms.ValidationError('图形验证码错误!')
            # 如果超过设定时间,图形验证码失效
            if not captcha:
                raise forms.ValidationError('图形验证码失效')
            # 判断短信验证码是否过期
            redis_conn = get_redis_connection(alias='verification')
            if redis_conn.get('sms_flag_{}'.format(mobile)):
                raise forms.ValidationError('获取短信验证码过于频繁')
            # 校验手机号码是否过期
            if User.objects.filter(mobile=mobile).count():
                raise forms.ValidationError('该手机号码已注册')
            return clean_data

4.8.verification/constants.py代码如下:

from django import forms
from django.core.validators import RegexValidator
from django_redis import get_redis_connection

from user.models import User

# 创建手机号码校验器
mobile_validator = RegexValidator(r'^1[3-9]\d{9}$', '手机号码格式不正确')


class CheckImageForm(forms.Form):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super().__init__(*args, **kwargs)

    # 这里的名字要和register.js中的json中传递回来的name一致
    mobile = forms.CharField(max_length=11, min_length=11, validators=[mobile_validator, ], error_messages={
        'min_length': '输入的手机号码长度有误',
        'max_length': '输入的手机号码长度有误',
        'required': '输入的手机号码不得为空',
    })

    # 这里的名字要和register.js中的json中传递回来的name一致
    captcha = forms.CharField(max_length=4, min_length=4, error_messages={
        'min_length': '输入的图片验证码长度有误',
        'max_length': '输入的图片验证码长度有误',
        'required': '图片验证码不得为空',
    })

    def clean(self):
        clean_data = super().clean()
        mobile = clean_data.get('mobile')
        captcha = clean_data.get('captcha')
        # 如果校验失败就会返回 none的结果
        # 如果前面的校验失败了的话就没有必要往下执行
        if mobile and captcha:
            # 拿到保存在session中的图形验证码的value
            image_code = self.request.session.get('image_code')
            # session中的图形验证码和request中发送过来的captcha不一样,返回'验证码错误'
            if image_code.upper() != captcha.upper():
                raise forms.ValidationError('图形验证码错误!')
            # 如果超过设定时间,图形验证码失效
            if not captcha:
                raise forms.ValidationError('图形验证码失效')
            # 判断短信验证码是否过期
            redis_conn = get_redis_connection(alias='verification')
            if redis_conn.get('sms_flag_{}'.format(mobile)):
                raise forms.ValidationError('获取短信验证码过于频繁')
            # 校验手机号码是否过期
            if User.objects.filter(mobile=mobile).count():
                raise forms.ValidationError('该手机号码已注册')
            return clean_data

    mobile = clean_data.get('mobile')
    captcha = clean_data.get('captcha')
    # 如果校验失败就会返回 none的结果
    # 如果前面的校验失败了的话就没有必要往下执行
    if mobile and captcha:
        # 拿到保存在session中的图形验证码的value
        image_code = self.request.session.get('image_code')
        # session中的图形验证码和request中发送过来的captcha不一样,返回'验证码错误'
        if image_code.upper() != captcha.upper():
            raise forms.ValidationError('图形验证码错误!')
        # 如果超过设定时间,图形验证码失效
        if not captcha:
            raise forms.ValidationError('图形验证码失效')
        # 判断短信验证码是否过期
        redis_conn = get_redis_connection(alias='verification')
        if redis_conn.get('sms_flag_{}'.format(mobile)):
            raise forms.ValidationError('获取短信验证码过于频繁')
        # 校验手机号码是否过期
        if User.objects.filter(mobile=mobile).count():
            raise forms.ValidationError('该手机号码已注册')
        return clean_data

4.8.verification/constants.py代码如下: 

```python
from django import forms
from django.core.validators import RegexValidator
from django_redis import get_redis_connection

from user.models import User

# 创建手机号码校验器
mobile_validator = RegexValidator(r'^1[3-9]\d{9}$', '手机号码格式不正确')


class CheckImageForm(forms.Form):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super().__init__(*args, **kwargs)

    # 这里的名字要和register.js中的json中传递回来的name一致
    mobile = forms.CharField(max_length=11, min_length=11, validators=[mobile_validator, ], error_messages={
        'min_length': '输入的手机号码长度有误',
        'max_length': '输入的手机号码长度有误',
        'required': '输入的手机号码不得为空',
    })

    # 这里的名字要和register.js中的json中传递回来的name一致
    captcha = forms.CharField(max_length=4, min_length=4, error_messages={
        'min_length': '输入的图片验证码长度有误',
        'max_length': '输入的图片验证码长度有误',
        'required': '图片验证码不得为空',
    })

    def clean(self):
        clean_data = super().clean()
        mobile = clean_data.get('mobile')
        captcha = clean_data.get('captcha')
        # 如果校验失败就会返回 none的结果
        # 如果前面的校验失败了的话就没有必要往下执行
        if mobile and captcha:
            # 拿到保存在session中的图形验证码的value
            image_code = self.request.session.get('image_code')
            # session中的图形验证码和request中发送过来的captcha不一样,返回'验证码错误'
            if image_code.upper() != captcha.upper():
                raise forms.ValidationError('图形验证码错误!')
            # 如果超过设定时间,图形验证码失效
            if not captcha:
                raise forms.ValidationError('图形验证码失效')
            # 判断短信验证码是否过期
            redis_conn = get_redis_connection(alias='verification')
            if redis_conn.get('sms_flag_{}'.format(mobile)):
                raise forms.ValidationError('获取短信验证码过于频繁')
            # 校验手机号码是否过期
            if User.objects.filter(mobile=mobile).count():
                raise forms.ValidationError('该手机号码已注册')
            return clean_data