「比更大还更大」(Bigger than Bigger)
- 初级复习: 写一个留言板来复习Django基础知识
- 项目实战: 使用Django+xadmin搭建一个在线教育网站
最终成果: http://mxonline.mtianyan.cn
GitHub代码仓库:
https://github.com/mtianyan/Mxonline2
请开始你的表演是做一个项目的第一步
项目演示和课程介绍
Django是一个Python中Web开发的主流框架,被许多大型公司使用,如Google,豆瓣,YouTube,知乎,instagram:
创业公司喜欢的web框架。严格按照互联网公司开发流程,写出优雅简练的代码。
循序渐进,细致入微。独立完成完整项目。学习完课程,找份Python web开发工作不在话下。
教程线上已部署地址: http://mxonline.mtianyan.cn
系统介绍:
- 系统具有完整的用户登录注册以及找回密码功能,拥有完整个人中心。
- 个人中心: 修改头像,修改密码,修改邮箱,可以看到我的课程以及我的收藏。可以删除收藏,我的消息。
- 导航栏: 公开课,授课讲师,授课机构,全局搜索。
- 点击
公开课
–> 课程列表,排序-搜索。热门课程推荐,课程的分页。 - 点击
课程
–> 课程详情页中对课程进行收藏,取消收藏。富文本展示课程内容。 - 点击
开始学习
–> 课程的章节信息,课程的评论信息。课程资源的下载链接。 - 点击
授课讲师
–>授课讲师列表页,对讲师进行人气排序以及分页,右边有讲师排行榜。 - 点击
讲师的详情页面
–> 对讲师进行收藏和分享,以及讲师的全部课程。 - 导航栏: 授课机构有分页,排序筛选功能。
- 机构列表页右侧有快速提交我要学习的表单。
- 点击
机构
–> 左侧:机构首页,机构课程,机构介绍,机构讲师。 - 后台管理系统可以
切换主题
。左侧每一个功能都有列表显示, 增删改查,筛选功能。 - 课程列表页可以对不同字段进行排序。选择多条记录进行删除操作。
- 课程列表页:过滤器->选择字段范围等,搜索,导出csv,xml,json。
- 课程新增页面上传图片,富文本的编辑。时间选择,添加章节,添加课程资源。
- 日志记录:记录后台人员的操作
学完后还可以将本网站改造成电商网站
,在线旅游
等其他网站
开发环境搭建任务
windows下通过pycharm
和virtualenv
搭建开发环境
django基础知识回顾任务
照顾基础薄弱同学: 通过留言板功能回顾django基础知识。
数据库设计和xadmin搭建后台管理系统任务
通过业务分析设计django
的每个app
,设计app
下的model
。设计外键关系
,通过django的migrate
设计生成数据表。
然后将这些model
注册到xadmin
当中。为每个model配置搜索
,过滤字段
,以及列表页的显示字段
。配置xadmin的主题选择
功能。
系统功能模块实现任务
实现所有后台功能 & 面试中经常被提及的web开发知识。
几乎所有的django常用模块:
setting
配置url
配置view
书写model
设计form
和modelform
的使用templates
模板的使用django
常用的内置函数
web系统知识以及网络安全任务
防止一些攻击问题:
- sql注入
- xss攻击
- crsf攻击
这些攻击的原理以及防护措施
xadmin扩展知识
掌握更多可定制功能:
- 权限管理
- 权限配置
- 权限,用户,组之间的关系。
- xadmin常用插件
- 如何自定义xadmin插件
- xadmin的富文本编辑功能
- xadmin的excel导入功能。
还会用到一些开源的django开发库。
不管是想全面学习Django还是想做一个线上教育平台都可以满足要求。学习完Django,我们对于学习其他框架和通过Django搭建我们自己的系统,都会成为很简单的事情。
老话总是没错的,工欲善其事,苟…
使用Django+Xadmin打造在线教育平台
- 第二章:windows下搭建开发环境
教你安装pycharm,mysql,navicat,python相关环境。
教程仓库地址1: https://github.com/mtianyan/DjangoGetStarted
windows下搭建开发环境
2-1 pycharm,mysql,Navicat安装。
环境搭建:
- pycharm (我:PyCharm 2017.3.2)
- mysql for windows(mysql-installer-community-5.7.20)
- navicat for mysql(我:Navicat Premium)
- python2.7
提醒:记住自己设置的mysql密码
Mysql
百度”mysql for windows” 直接在百度软件中心下载即可
如果你的电脑跟我电脑一样空,推荐遵循我的:
- 点击接受协议
- 选择Custom选项。(如果默认选项,会发生必要条件缺失:如我电脑没有VS和py3.4)
- 下图页面点击
next
会显示我们不满足的条件,back
后点击绿色箭头移除。
- 所有条件都达成,点击
Execute
,等待安装完成。
均为绿色代表安装完成。
- 一直默认选择直到下图页面。设置密码,添加用户(可选)
注意:记住自己设置的mysql密码
之后全部默认下一步。直到安装完成
Finish
这时Navicat已经可以正常连接了。如果想让mysql
命令在cmd下可使用。
C:\Program Files\MySQL\MySQL Server 5.7\bin
(自行替换为自己的mysql.exe地址)加入环境变量中。
通过mysql -uroot -p
命令可以进行登入mysql控制台。
Navicat
安装指南:下一步下一步。
下载地址:http://www.navicat.com.cn/download/navicat-for-mysql
我的安装目录: C:\software\Navicat Premium 12
PyCharm 2017.3.2
pycharm官方下载链接:https://www.jetbrains.com/pycharm/download/#section=windows
我们要选择专业版(Professional)因为只有专业版才能够新建django项目,免费社区版不能。
为Pycharm添加解释器:
setting
- Project Interpreter
:
一直定位到 python.exe
点击确认。
Python2.7安装
推荐阅读:Python开发环境搭建指南(Anaconda2,3共存)
推荐选择进阶版本, 方便升级到3.6。
http://blog.mtianyan.cn/post/230a7ad6.html
2-2 virtualenv安装和配置
virtualenv介绍
每个应用可能需要各自拥有一套
独立
的Python运行环境。virtualenv就是用来为一个应用创建一套隔离
的Python运行环境。
virtualenv优点:
它是将全局Python解释器进行私有化复制。
如果不使用虚拟环境,默认的pip
安装都会安装到同一个目录(java是把自己需要的包放到自己项目目录),不同项目使用起来会产生问题
安装virtualenv
进入cmd,(确保自己的pip已经可用)
1 | pip install virtualenv |
默认使用virtualenv testvir
该命令,会将虚拟环境创建在我们当前用户目录。
注意:我的目录在桌面是我的cmder设置的、还请自行cd %homepath%
前往自己的目录
这样直接使用步骤有写过于繁琐。所以我们使用virtualenvwrapper
virtualenvwrapper安装
1 | pip install virtualenvwrapper-win |
- 创建虚拟环境
1 | mkvirtualenv DjangoTest |
会创建在C:\Users\mtian\Envs
当前用户目录下的Envs目录。
修改mkvirtualenv
创建的目录:新增环境变量WORKON_HOME
退出激活状态
1
deactivate
知道有哪些虚拟环境
1
workon
直接进入虚拟环境
1
workon DjangoTest
注意前面的(DjangoTest)
代表进入了虚拟环境。
执行workon
命令之后,执行pip install django==1.9.8
安装。
2-3 Pycharm和Navicat的简单使用
pycharm简单使用:
Setting -> reopen
取消默认打开上一次项目
新建项目并验证成功运行
- 如何新建django项目:
选择好自己的项目的解释器为我们新建的虚拟环境。
新建project
->djangotestProj
。别忘了为我们的虚拟环境安装Django
- 检查django环境是否安装好。
interpreter
- 点击导航栏的
run
可以直接运行我们的django项目
上图说明我们的django已经安装并且可以正常运行。
点击浏览器打开http://127.0.0.1:8000/
进行验证。
出现上画面代表我们大功告成
设置eclipse快捷键 - keymap
选择setting
搜索keymap
设置eclipse
快捷键
比如
ctrl + H
全局搜索
Run edit配置修改
点击上图中run edit
可对Django运行时的一些设置进行修改。
比如修改host为0.0.0.0
,然后就可以设置监听本机ip。然后点击run
进入cmd
下输入ipconfig
查询自己的ip
例如我的是
192.168.0.4
1 | 192.168.0.4:8000/ 来访问。 |
目录颜色不同的原因
可以看到不同的目录颜色不同。这是我们可以进行设置的,为了可以做到智能提示。
右键可以将template
目录unmark
可以看到上图目录是灰色的。但是我们右键mark
为source Root
目录,会变为蓝色。
这意味着我们在
import
时pycharm会根据设置智能提示。
如果不mark可能会出现很多我们在pycharm中报红色,
但是cmd确可以运行的情况。
navicat基本使用
新建连接
点击新建一个mysql的连接。
连接名自行设置,密码填自己安装mysql时设置的密码。
右键新建数据库
数据库名自行设置,
utf-8
utf_general_ci
注意:这里请与图中选择一致。否则保存中文可能出错
新建数据表
双击数据库testdjango
使他变绿,然后选中表,然后右键新建表。或使用右侧新建表
按钮
输入必要的字段然后使用ctrl + s
进行保存并输入表名。
增加数据
双击表,可以展示我们的数据,这时候我们可以自行修改值。
点击左下角可以新增更多行。并且状态栏会显示一些sql语句信息
设计表
右键设计表:我们可以添加字段
Sql语句查询
点击查询,新建查询。我们可以输入Sql语句进行查询。
表的复制粘贴与数据库传输。数据库导入导出。
Navicat支持我们把不同数据库的表之间的复制粘贴操作。
支持数据传输:点击工具数据传输
导出:在数据库上右键我们可以转储SQL文件
: 可以选择只转存结构。或连带数据一起。
导入:右键点击运行SQL文件。
对于表的操作:删除,清空等,在点击表的右键菜单里。
小项目不扫何以扫天下
使用Django+Xadmin打造在线教育平台
- 第三章:通过留言板功能回顾django基础知识
通过做一个小留言板,学习django基础知识
教程仓库地址1: https://github.com/mtianyan/DjangoGetStarted
通过留言版功能回顾django基础知识
教程中本章对应上传的仓库为: https://github.com/mtianyan/DjangoGetStarted
对应第一次commit:留言板仓库初始化。内容截止3-1一章结束。
- 将对于django目录结构,使用Django快速搭建可以提交的表单页面,models.py , urls.py, views.py。
- 从数据库中取出数据展示到html中:Django Template的配置。
- 即django的基础知识通过这个留言板项目进行一个全面细致的学习。
3-1 django目录结构
django目录:
projectname : 保存Django项目的urls,setting,uwsgi文件
如下图新建一个Django项目DjangoGetStarted
,使用我们上章节中已存在的虚拟环境DjangoTest
(里面已经装好了django)
django自动生成的目录
初始化完成后的目录如下:(如果不是,那么你们可能创建的不是django项目)
可以看到主目录DjangoGetStarted
与项目目录DjangoGetStarted
- DjangoGetStarted(文件夹):
- setting.py: 项目全局配置文件
- urls.py: 主要的urls配置入口
- wsgi.py: 是Django启动需要的文件。
- templates(文件夹): 放置html文件
- manage.py: 启动Django需要的主要文件。(主要的Django命令都通过manage.py运行)
还需要我们自己创建的目录
app是Django里一个一个应用的文件夹单位。
通过 Tools -> Run manage.py Task
创建app:
startapp message
可以看到当输入startapp message
之后,创建了message
应用。并存放在了:与项目目录同级目录。
新建static目录
使用static
目录来存放网站的静态文件:js,css,图片等。
新建log目录
使用log
目录来存放网站的日志文件
新建media目录
使用media
目录存放用户上传的图片等资源。
解决项目大了之后app过多问题
- 新建文件夹
apps
- 将
message
文件夹拖入apps
文件夹内:会自动生成__init__.py
文件表明这是一个包。使得apps文件夹可导入。
这时我们就会发现在导入我们的message的内容就得配置较长的路径。
每次前面都得加上
apps.
,这可烦死人啦。
解决方案奉上
将
apps
目录右键mark
成Source Root
(Mark 方法查看第一章pycharm简单使用:目录颜色不同的原因)
mark成功之后变蓝(变绿的话,只能摸摸头了,当然选择原谅),然后可以直接使用短路径进行import
Mark后Pycharm 不报错,Cmd下运行报错。
Mark后pycharm知道这是一个项目的Souce Root
路径了,但是cmd并不知道。
在项目目录下通过cmd命令行使用
python manage.py runserver
pycharm中mark只是pycharm自身可以进行识别短路径。
解决方案:
我们在setting文件中配置我们的
apps
路径:
图解读:我们需要在setting中向上图一样设置,程序就会接着报错。(换了一个错误了,滑稽脸)
1 | import sys |
上述代码为将apps拼接项目绝对路径后的路径插入当前系统的环境变量path中,这样就可以成功解决(个屁屁啊)。
成功性测试(测试已失败):
这个import放到manage.py文件是不行的 你把manage.py中这行删除 因为django整个的配置还没有启动好 import django的model是不行的,
插播:忘了失败吧,我偷学下面方法养你。
终极解决:将这个import
方法比如urls.py.等可以成功启动。或者自行删除该import。
红色警告:
1 | You have unapplied migrations; your app may not work properly |
是因为我们没有进行数据库models
进行初始化migrate
.
python manage.py migrate
我们之后会用到,现在不要做。
github仓库项目初始化第一次commit。
输入用户名密码,点击login。
选择左侧导航中
Git
设置你的git.exe的路径
点击Share project on GitHub
会弹出下图窗口
填写你的项目名称
,描述
。点击share
。
会弹窗让你选择需要上传的项目文件与commit信息。然后将项目上传至github。
我教程中上传的仓库为: https://github.com/mtianyan/DjangoGetStarted
对应第一次commit:留言板仓库初始化。内容截止3-1一章结束。
3-2 配置表单页面
上节教程(来学习本节前置条件):
- 对应第一次commit: 留言板仓库初始化。内容截止3-1一章结束。
github仓库地址:https://github.com/mtianyan/DjangoGetStarted
本节我们学习将表单页面配置进django项目中
必要的该说的,该了解的
前置条件:
你已经学习了前面教程。将项目的文件夹目录结构,setting配置等修改完毕与我保持一致。
本节通过Django快速的配置一个留言板页面来学习
Django从请求到响应的整个完整流程。为我们开发在线教育平台打下基础。
上图便是本节教程所要用到的静态页面: 前往Github下载:form.html
具体的业务:填写信息 -> 然后点击提交 ->数据被存储到数据库。
这个html
是一个单文件,里面已经包含了css
js
内容。
将html文件整合进项目操作步骤
将html
文件直接复制进templates
目录.
创建static目录下的css文件夹
和 static/js
- 在css中再新建一个
style.css
form.html
中点击<style>
标签左侧减号。将style内容收成一行。然后把这一行内容剪切粘贴到style.css
粘贴进去之后,将首尾两个
<style>
删除,shift + tab
可以将css格式化更整齐。在
form.html
新建<link>
来引入css。(文件里其实已经先加上了,学一种操作而已)
1 | <link rel="stylesheet" href="/static/css/style.css"> |
配置Django连接Mysql数据库
在setting.py
大概80行找到:
1 | DATABASES = { |
这是Django默认与自己项目根目录下的db.sqlite3
连接的设置。
我们的项目是与mysql
连接,所以我们要改成如下:
1 | DATABASES = { |
其中的name
应设置为我们中在Navicat中新建的数据库名字。名字一定要保持一致
这时要将我们之前建的表提前全部删除掉。
配置mysql驱动和seeting文件。
点击Tools 菜单下 Run manage.py Task
我们会发现报错了:
1 | raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e) |
由错误信息我们可以看出是因为没有安装数据库驱动模块
MySQLdb
cmd
下workon
进我们的虚拟环境目录。pip install mysql-python
然后会发现报错:
1 | _mysql.c(42) : fatal error C1083: Cannot open include file: 'config-win.h': |
前往地址 https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysql-python
下载MySQL_python_1.2.5_cp27_none_win_amd64.whl
到本地,放到桌面。
然后使用下面命令进行安装:注意是在虚拟环境下。
1 | cd Desktop |
这个时候我们再次点击Tools 菜单下 Run manage.py Task
会看到已经没有刚才的错误。
但是会有红框里的警告,面向强迫症解决方案是在setting.py
新增STATIC_ROOT = '/static/'
但其实现在还没有用到这个参数。后面用到我们再配置。(推荐自行克服强迫症)
输入下面命令来生成表:
1 | makemigrations |
这时我们去Navicat查看会发现为我们生成了很多表。
这些都是Django系统默认的内置数据表。
做完这些操作我们可以点击Run
来运行项目,
然后到http://127.0.0.1:8000/
来访问看是否运行成功。成功页面(It worked)
配置form页面展示出来:
DjangoGetStarted/urls.py
修改如下:
1 | urlpatterns = [ |
新增加url(r'^form/$', getform)
,^
是代表以form
为开头,$
代表以/
结尾的地址。
这里getform
是对于这个url
的相应处理的view
。我们先去创建一个.
message/views.py
添加如下代码:
1 | def getform(request): |
request
参数是一个django的http request
对象。(基础)
这里我们可以按住ctrl
+ 左键
跟踪到我们的render
函数里面。Alt + 左箭头
回来。
1 | def render(request, template_name,...): |
源代码解读:可以看到我们的render
需要一个request对象
和template_name
参数
注意:记性好的还记得我们提供的源文件是form.html
知识点:django内置了很多html页面,form会先从内置中寻找。所以我们得改。
因此我们需要右键如下图Refactor
修改from.html
为message_form
如果我们的项目在运行,ctrl + s
会自动重启我们的项目。
这时我们有了view,我们可以去配置完整的url了(前面已经配完整的检查一遍):
DjangoGetStarted/urls.py
1 | from message.views import getform |
这里我们不能加括号否则会变成方法的调用。
按住ctrl
+ render
跟踪到我们的url
函数里面查看源码如下:可以看到它除过一组正则表达式,还需要接收一个view对象。
1 | def url(regex, view,...): |
如果getform
加上括号会报错:
1 | TypeError: getform() takes exactly 1 argument (0 given) |
访问http://127.0.0.1:8000/
正常结果:Page not found
1 | Using the URLconf defined in DjangoGetStarted.urls, Django tried these URL patterns, in this order: |
是因为我们在url中加入了个人的配置^form/$
,它就不会采用默认配置了。
原因:(源码探究标记点)
这时访问:http://127.0.0.1:8000/form/
旧版本pycharm会报:TemplateDoesNotExist
错误。我的新版本pycharm并没有出现。
重要代码在DjangoGetStarted/settings.py
60行左右
1 | # 指明我们的templates目录路径 |
现在再次访问 http://127.0.0.1:8000/form/
页面出来了但是样式没有。static目录下的css文件提示没有找到。
Setting中静态文件的配置,这是因为我们setting中的
1 | STATIC_URL = '/static/' |
只说明了目录的名称。并没有指明查找的根路径。添加下面代码:
1 | STATIC_URL = '/static/' |
过程中看似不停的出错,其实是为了让大家更好记住该记住的。
项目配置流程图
我们刚才是以倒序:
- 把html文件放进来
- 通过简单的url配置来访问html。
- 发现找不到页面,所以我们设置setting中
DIRS
- 文件找到了又说找不到静态文件,我们设置了
STATICFILES_DIRS
这是我们的整体流程图,推荐新建一个项目再按照正向流程图来一遍。
后面我们的工作会围绕从migration生成数据表往下的内容
展开。
GitHub地址:https://github.com/mtianyan/DjangoGetStarted
本节结束对应于commit:
3-3 django orm介绍与model设计
上节教程完成后代码(来学习本节前置条件):
github仓库地址:https://github.com/mtianyan/DjangoGetStarted
- 对应commit: 留言板前端页面展示。本次内容截止教程3-2结束。
可能现在你还在通过手写sql语句来操作数据库,当我们有了orm,数据库操作变得很简单。这一小节我们来学习Django中的orm。
原生sql 与 orm
没有orm 的情况下message/views.py代码:
1 | import MySQLdb |
可不可以让数据库字段的查询和使用类的一个属性一样简单?没错登登登:orm上场了
1 | book:name |
Django的orm就是为了让我们不再写上面那样的语句,而是像使操作数据库像使用类和类属性一样。
创建我们的models
verbose_name:对象的人类可读的名称,单数:
1 verbose_name = "pizza"
1 | class Meta,内嵌于 UserMessage 这个类的定义中 |
message/models.py:
1 | # 继承于django.db.models.Model |
这时我们执行makemigrations messages
会发现并没有改动。
因为setting中我们没有注册我们的app: message
注意:新建的app都要在setting中注册
在setting中注册我们的app
DjangoGetStarted/settings.py 大概36行INSTALLED_APPS
:
1 | `INSTALLED_APPS` |
这时候我们重新运行Tools 菜单下 Run manage.py Task
会提示:
如果提示:
1 | SyntaxError: Non-ASCII character '\xe7' in file D:\CodeSpace\PythonProject\DjangoGetStarted\apps\message\models.py on line |
请注意可能你忘记在写过中文的地方加上:
1 | #coding: utf-8 |
注意必须加在第一或二行。
然后执行下面命令:
1 | makemigrations message |
1 | migrate message 生成数据表 |
前往Navicat验证:
可以看到我们的数据表已经创建成功。默认数据表名称为
app名称_类名转换为小写
自动生成的id作为主键。
Models讲解
除过普通的对应数据库的字段类型如CharField
,还有很多高级类型。如EmailField
提供email验证。
1 | models.ForeignKey # 外键 |
ctrl按住+左键点击
models
进入之后点击fields
拖到文件开始可以看到所有字段:
1 | __all__ = [str(x) for x in ( |
介绍字段参数
CharField
必须指明默认最大长度。null=True,blank=True
指明字段可以为空defalut = " "
指定默认值。
1 | name = models.CharField(max_length=20,null=True,blank=True, verbose_name=u"用户名") |
id是自动生成的,如果需要自定义主键,message/models.py中添加字段:
1 | object_id = models.CharField(primary_key=True, verbose_name="主键") |
此时点击Tools 菜单下 Run manage.py Task
输入makemigrations message
知识点:CharField必须指明最大长度
object_id改为:
1 | object_id = models.CharField(primary_key=True, max_length=50 ,verbose_name="主键") |
这时点击Tools 菜单下 Run manage.py Task
输入makemigrations message
1 | You are trying to add a non-nullable field 'object_id' to usermessage without a default; we can't do that (the database needs something to populate existing rows). |
根据提示信息,我们需要给object_id
添加默认值:
1 | object_id = models.CharField(primary_key=True, max_length=50,default="", verbose_name="主键") |
get新知识点:object_id必须有默认值
输入2
退出:然后输入makemigrations message
再输入下面命令生成数据表
1 | migrate message |
可以看到上图过程中会告诉我们做了哪些变化,如删除了默认系统生成的主键
id
,变更了name
。新增了我们的object_id
前往Navicat验证右键设计表:
可以看到object_id
已经成为我们的新主键。
介绍Meta信息:
Meta信息中我们可以指定常见的类型:
1 | db_table = "user_meassage" |
自定义后生成表,表名会与我们的保持一致。而不会前缀appname
如:message_
这里因为我们已经生成过了,就不要做验证改变表名了。
1 | ordering = '-object_id' |
ordering指定默认排序字段,如:就会以object_id倒序
1 | verbose_name_plural = u"用户留言信息" |
verbose_name_plural:复数信息,便于人阅读。否则会在后台显示用户留言信息s
已经学习完毕了orm
将数据表映射表。
github地址:https://github.com/mtianyan/DjangoGetStarted
此节结束对应github commit:
留言板数据库orm映射成表完成。内容截止教程3-3结束。
3-4 django model的增删改
github仓库地址:https://github.com/mtianyan/DjangoGetStarted
- 上小节完成代码对应commit: 留言板数据库orm映射成表完成。内容截止教程3-3结束。
在message/views.py
中:
1 | from .models import UserMessage |
将我们刚才创建的model,import进来。.
代表是与当前同级的目录。
按照下图所示添加一条测试数据。
然后再我们的getform
方法内部添加下面代码:
1 | def getform(request): |
调试过程:
点击上图小红框位置,打上断点。
点击Run -> debug后:在浏览器里打开:
http://127.0.0.1:8000/form/
- 弹出上图代表已进入断点。
此时鼠标左键点击:all_message.可以看到这是一个
{QuerySet}类型的对象,里面存放着[<UserMessage: UserMessage object>]
按
f6
使运行到下一步。此时下方的值窗口内可以看到message的值。说明我们成功取到了数据库的值。
filter取出指定要求值
1 | all_message = UserMessage.objects.filter(name=' mtianyan', address='西安') |
按照上面调试过程重新调试可以看到我们同样取出了值。
小练习:将名字改为与自己数据库存放值不同的。查看结果。
变成了空列表,说明一切正确。
将数据存入数据库
了解:django/db/models/base.py 源码中提供save方法
1 | def save(self, force_insert=False, force_update=False, using=None, |
getform方法中添加代码:
1 | # 存储部分 |
- 打上断点:如下图。
- 一直惦记f6单步调试,直到如下图:蓝色到
return
语句
可以在下方值窗口查看到值
Navicat进行验证
可以看到成功的添加了数据
mtianyan2
如何从html的提交中取到数据并保存进数据库
templates/message_form.html:
method是post。action就是指向我们在urls.py中配置的
/form/
前面必须加斜杠指根路径下form
里面的input会自动把值传递给后台:这时我们就可以在getform中取到刚才传递过来的值。
运行项目:然后输入需要填写的值。点击提交:出现403错误
1 | Forbidden (403) |
根据提示:我们的页面没有进行crsf的验证,这时django的安全机制,不允许任意form都往后台提交。
知识点:所以我们需要在html页面中加入csrf_token
1 | {% csrf_token %} |
原有那行删除掉。打上断点
刷新页面并提交。这时候在值窗口可以看到request对象下的POST中存放着我们提交的数据。内容如下
1 | <QueryDict: {u'message': [u'\u54c8\u54c8'], u'address': [ |
数据以dict:key-value 形式存储 key是由如下图html中的name所决定对应的。
数据库新增。
request.POST
中数据取出,存入user_message
对象
1 | # html表单部分 |
- 打断点在下图位置:
- 进入调试:点击点击method:是get请求。因为我们并没有按提交按钮,而是get这个网页
- 点击f8继续运行我们的项目 浏览器中填写表单内容点提交。
因为这次是表单提交,已经变成了post方式。按
f6
进行单步调试。
一直单步到如下图蓝色
这时候值浏览窗口可以看到
检查我们的user_message对象的属性是否已经全部添加进去,
使用f8 继续项目并前往Navicat验证
可以看到我们的数据库中已经新增,标志着我们已经成功存入数据。
删除数据。
对于查询到的数据做删除:
1 | # 方法2 :filter取出指定条件值,逗号代表and 必须同时满足两个条件才返回。 |
点击run并访问:http://127.0.0.1:8000/form/
进入Navicat进行验证。
可以看到我们的那条mtianyan + 西安的数据已经被删除。
至此:我们已经学会了新增,删除,查询。
本节结束github对应commit:
django model的增删改数据库。本次内容截止教程3-4。
3-5 django url templates配置
项目Github地址:https://github.com/mtianyan/DjangoGetStarted
本节开始对应对应Github的commit:django model的增删改数据库。本次内容截止教程3-4。
本节将介绍url的配置,以及如何将数据库数据填充回前台html页面。
情景:只允许用户修改mtianyan
,如果没有就添加,如果有就回填使用户可以修改。
取出数据
message/views.py中的getform方法中
1 | message = None |
这里注意把前几节写的删除掉
将数据回填至html中
修改return render
1 | return render(request, 'message_form.html',{ |
这里前面的”my_meassage”是我们可以自行命名的。会有一个
my_message
对象随着返回前端页面。
在前端页面中放入值。
为input系列标签添加value
: 使用my_message.name
取到我们传递过来的my_message
对象的属性值。
1 | <input id="name" type="text" name="name" |
请自行完成姓名,邮箱,联系地址三个
input
标签。
为textarea
标签添加值
1 | <textarea id="message" name="message" |
运行项目,访问:http://127.0.0.1:8000/form/
成功!!我们已经将后台数据库数据成功展示到前台。
template模板渲染中的一些用法。
在我们的template模板中也就是form.html中,不允许我们写Python的语法,
它提供了一套自己的内建标签。
常用的几种模板标签介绍:
if - else
官方提供模板如下:
个人实践:
满足if运行结果:
不满足if:如改为my_message.name == "mtianyan1"
运行结果:
ifequal
& ifnotequal
官方文档解释:ifequal a b
相当于f a == b
.ifnotequal
则相当于if a != b
个人实践:
结果为:未找到中文昵称
slice
官方文档解释:其实就是切片操作。从头开始切到第n个。
个人实践:
本来
mtianyan
与mtianyan1
是不同的,但是切片后前八位相同。
运行结果显示 :对应中文昵称:天涯明月笙
URl的别名设置技巧
DjangoGetStarted/urls.py:
为r'^form/$'
添加别名:
1 | url(r'^form/$', getform, name = "form_new") |
前往html中修改action地址为下面所示:
1 | <form action="{% url "form_new" %}" method="post" class="smart-green"> |
这时我们如果改动urls.py中的r'^form/$'
不需要再修改前端代码中值。
url先后顺序问题
注意url匹配规则中一定不要忘记/$
符号代表以form/
结束的才会有效。不会向后继续匹配。比如没有/$
1 | url(r'^form', getform, name="form_new") |
这时我们进入浏览器访问时输入http://127.0.0.1:8000/formemmm
都可以被响应。
特别是如果底下还配置有被这个规则包含的条目,会产生被写在更靠前的拦截住得不到正确处理的Bug。
上图我们是想要让formtest响应admin.site.urls。但是会被form提前拦截住。
所以我们一定要注意加上/$
符号。
至此我们完成了留言板项目:学习到了Django必备的基础知识。
下一章我们将开始我们的进阶学习:开发在线教育平台网站。
本章结束:
对应Commit: 留言板项目学习完成,本次内容截止教程3-5。完结,撒花。
项目Github地址:https://github.com/mtianyan/DjangoGetStarted
数据基础决定上层建筑
设计数据库是整个项目的第一项工作:
- 完成django的app设计 & 完成app中的models设计
Django1.9.8对应github: https://github.com/mtianyan/Mxonline2
Django2.0.1对应github: https://github.com/mtianyan/Mxonline3
完成django的app设计 & 完成app中的models设计
4-1 使用py3.6和django1.11开发系统前注意事项
直接通过Python3.6和django最新版本来开发我们的系统的一些注意事项。
原版本: Python 2.7 & django 1.9.8
现在版本:Python 3.6 & django 1.11
我个人使用: 3.5 + django2.0.1 & 2.7 + django 1.9.8
直接从3.6上手,开始工作,而不用做完2.7再转换。
代码几乎100%兼容2.7 & 3.6
虚拟环境问题
Python2.7 与 Python3.x共存并创建虚拟环境。
1 | mkvirtualenv -p C:\...\python.exe mxonline |
设计model的时候的__unicode__
方法
Python2.7 中:
1 | def __unicode__: |
Python 3.x中:
1 | def __str__(self): |
3.x下重载Unicode不会报错,但是会在后台显示有问题。
安装Python的mysql驱动时不能用之前的 MYSQL python
这个网址是windows下python包安装的居家必备良品,建议收藏。
https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysql-python
应该改用Mysqlclient
来替换我们的MySQl-python
接口是一样的。所以以后建议直接用Mysqlclient,因为它2, 3版本都有。
通过源码方式安装xadmin时。
Github 搜索
mxonline_resources
,将里面的Xadmin放进extras_apps中。
就不用官方的了。
django 2.0.1 的修复bug版可以使用我的:
https://github.com/mtianyan/xadmin_django2.0.1
也可以直接使用官方的新版,已经支持了Python3.6
Xadmin安装一定要安装依赖包
1 | django-crispy-forms=1.6.0 |
使用DjangoUeditor
官方的不支持Python3, 去mxonline_resource
目录下载兼容Python3的版本。
放入extras_apps
4-2 django-app 设计
数据库设计
根据app设计 models
数据表生成与修改
授课机构提供讲师录制课程,学员完成在线学习。
- 全局头部:用户消息 & 个人中心: 没有登录时,就是登录注册
- 对于公开课,授课讲师,授课机构进行搜索。
- 轮播图,课程,机构,页脚
- 公开课:分页公开课,右边热门推荐。
- 点进课程:课程详情页。详情: 后台富文本。右边是课程机构的介绍。收藏 或学习
- 章节信息 & 课程资源下载 & 评论
- 授课讲师: 授课讲师列表页, 讲师排行榜。分页。
- 点进讲师: 看到课程。
- 授课机构: 类别筛选,机构性质,所在地区 & 排序。用户提交表单,我要学习, 机构排名.
- 个人中心: 修改密码, 修改头像, 个人信息, 我的课程, 我的收藏, 我的消息。
app大致会有用户模块
,课程模块
,授课教师
与授课机构
。
多一个operation app 是因为数据库的需要。后面会讲。
4-3 新建项目
Python2.7 创建虚拟环境。
1 | mkvirtualenv mxonline2 |
安装django
1 | pip install django==1.9.8 |
注意Python2下此处必须用1.9.8
Python3.x 创建虚拟环境
如果你已经通过我的博文《Python开发环境搭建指南(Anaconda2,3共存)》
搭建了完美的共存环境使用下面命令创建虚拟环境
1 | mkvirtualenv -p D:\softEnvDown\Anaconda2\envs\py3\python.exe mxonline3 |
-p后面路径为自己的Python3的exe文件路径。
官方说明的最新稳定版为2.0.1(2018-01-08 19:37:06)
1 | workon mxonline3 |
至此我们的两个虚拟环境都已经准备好了。
新建Python2 下Project
为Mxonline2 配置环境 mxonline2
注意一直定位到Python.exe。
安装mysql驱动。
下载https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysql-python中
mysqlclient‑1.3.12‑cp34‑cp34m‑win_amd64.whl进行本地安装
1 | workon mxonline2 |
新建Python3 下Project
为Mxonline3 配置环境 mxonline3
注意一直定位到Python.exe。
安装mysql驱动。
1 | workon mxonline3 |
setting中配置
Mxonline2/settings.py:
Mxonline3/settings.py:
1 | DATABASES = { |
1 | DATABASES = { |
前往Navicat新建数据库
mxonline2 & mxonline3
进行数据库初始化makemigrations
点击Tools 菜单下 Run manage.py Task
1 | makemigrations |
2,3操作一致
点击 RUn edit
可以为2,3配置不同的port。比如2: 8002 & 3: 8003
2: 点击run运行: django1.9.8成功画面如下。
3: 点击run运行: django2.0.1成功画面如下。
这时我们的项目就新建成功。
此处对应commit:
项目初始化成功: 完成数据库Migration初始化。 对应4-3
4-4 自定义userprofile
点击Tools 菜单下 Run manage.py Task
1 | startapp users |
编写我们的model设计user表。
系统自动生成的user表如下:
- id: 主键, password 密码, last_login Django自动记录用户最后登录时间,。
- is_superuser 表明用户是否是超级用户(后台管理会用到)。
- username 用户名字段不要随便改动, email 邮箱,
- is_staff 表示是否是员工(后台管理会用到)。
- is_active 用户是否是激活状态, date_joined 注册时间。
我们需要扩展我们自己的需求字段。
个人中心页面中:
可以看到我们还需要的有:
- 昵称: nickname
- 生日: birthday
- 性别: gender
User表的自定义方法可以查看django官方文档。
我们既想保留原有字段,又想有新字段。
users/models.py(3把Unicode改为str)添加代码:
1 | from django.contrib.auth.models import AbstractUser |
点进AbstractUser
可以看到这个models里面就有我们默认表的那些字段。
因为Image字段需要用到pillow
所以需要安装该库
1 | pip install pillow |
注意:CharField必须有max_length, Imagefield实际也是charfield所以也要有max_length
setting设置INSTALLED_APPS & AUTH_USER_MODEL。
- INSTALLED_APPS注册app
1 | 'users' |
- 重载AUTH_USER_MODEL
1 | # 此处重载是为了使我们的UserProfile生效 |
点击Tools 菜单下 Run manage.py Task
1 | makemigrations users |
上图中可以看到数据库做出的改动。输入: yes
进入Navicat进行验证
如上图可以看到我们的表已经生成成功。
附加Python3下不同与报错:
将Unicode方法改为str方法
1 | # 重载__str__方法,打印实例会打印username,username为继承自AbstractUser |
报错:
1 | django.db.migrations.exceptions.InconsistentMigrationHistory: Migration |
解决方案:
1 | 删除数据库中 除了auth_user的其他表 |
然后执行命令:
1 | makemigrations |
共11张表,同期django1.9.8会产生13张表
我推测是因为在django2.0版本中。我们如果自定义了userProfile并且在setting中进行了设置。那么auth_user将不再拥有多的表。
下次不要再初始化时执行makemigrations & migrate。当我们设计userProfile完成之后再执行。
本小节完成对应commit:
完成USerProfile models书写。makemigrations & migrate 建表成功。对应4-4
4-5 user modesl.py设计
循环引用:
设计app时每个app都有model
如图:我们在user中定义usercourse记录用户学习的课程。会有两个外键:user和course。
我们就会import Courses.models
如果用户对课程的评论:会放在 Courses.models
当中。评论我们需要保存相应的用户。
我们就会import User.models
循环import会出错。a与b相互调用,造成等待。
解决循环引用: 分层设计
目前已有app:users courses organization
另外一个app高于这些app的层级。operation
.上一层app可以import下层的app。
上节中: 自定义userprofile
覆盖默认user
表
user中还需要添加的(前提是这些功能比较独立):
- EmailVerifyRecord - 邮箱验证码
- PageBanner - 轮播图
观察轮播图:
- 图片 2. 点击图片地址 3. 轮播图序号(控制前后)
users/models.py中添加代码:
1 | from datetime import datetime |
从上往下: 第一块区域import官方包,第二块import第三方。(PEP8)
如下图: 我们一共创建了三个数据表: Structure可以查看到
与用户相关的评论啊,点赞啊。学习的课程啊并没有放进来,因为那些独立性不高。
容易产生循环引用。我们把那些放到operation中。
本小节完成,对应commit:
Usermodel添加邮箱验证码,首页轮播图。对应4-5
4-6 course models.py编写
点击 Tools 菜单下 Run manage.py Task
1 | startapp courses |
course中需要那些表:
- 课程本身需要一张表
点进去之后点击开始学习。
课程基本信息需要一张表, 章节表与课程表存在(一个课程对应多个章节)
- 章节表中:章节的名称。 章节与视频(一个章节对应多个视频)
结构: 课程本身–(一对多)>章节-(一对多)->视频信息
资源下载放在课程里面的。一个课程对应多个资源
共四张表:课程本身–(一对多)>章节-(一对多)->视频信息 & 资源表
courses/models.py:
1 | from datetime import datetime |
下面来编写章节 & 视频 & 课程资源:courses/models.py
一对多, 多对一都可以使用django的外键来完成。
1 | # 章节 |
通过Structure可以看到我们刚才设计的四张表
本小节完毕, 对应commit:
设计完成课程app中四张数据表: 课程,章节,视频,资源。对应4-6
4-7 organization modesl.py设计
新建课程机构app:
点击Tools 菜单下 Run manage.py Task
1 | startapp organization |
课程是属于机构的, 机构有机构类别,城市等字段。讲师实体。
我要学习的提交表单会与用户关联,存放在机构。
其中课程数,学习人数可以动态统计。机构地址,机构经典课程。
机构讲师,机构课程可以通过外键获取到, 不保存到机构中。
讲师大概所需要的字段如图所示。
organization/models.py 代码如下:
1 | # encoding : utf-8 |
可以看到我们一共创建了三张表:分别是城市,课程机构,讲师。
本小节对应commit:
课程机构app:城市,机构,讲师表书写完毕。对应4-7
4-8 operation models.py设计
分析需要那些表:
- 用户可以提交我要学习的个人需求。
- 学员的课程评论信息
- 收藏:可以收藏公开课, 授课讲师, 授课机构, 用户消息提醒。
- 个人中心:我的课程说明用户和课程之间的学习关系也需要保存。
新建操作app:
点击Tools 菜单下 Run manage.py Task
1 | startapp operation |
operation/models.py添加代码:
1 | # encoding: utf-8 |
至此:我们的五张operation下的数据表models设计完成
setting中配置添加app
本小节对应commit:
operation下的models设计,用户: 课程&消息&收藏&评论&我要学习.并在setting中进行了注册。对应4-8
4-9 数据表生成以及apps目录建立
学习如何通过刚才设计的models生成数据库对应的表
点击Tools 菜单下 Run manage.py Task
:
Python2与Python3不同:
Python2下可能会报一些noASCII
错误:
只需要在对应你写了中文的第一行加上:
1 | # encoding: utf-8 |
Python3(django2.0.1)会报错:
1 | org = models.ForeignKey(CourseOrg, verbose_name=u"所属机构") |
这是因为在2.0.1中,外键关系必须指明删除时的操作。
比如:出租车都归属于出租车公司。如果出租车公司倒闭了,那这些汽车该怎么处理。
必须自己指明: 我觉得可以直接进行级联删除。
django提供了:
CASCADE
PROTECT
SET_NULL
SET_DEFAULT
等选项。我选择了CASCADE
删除。
将(dajngo 2.0.1)项目中所有的外键修改为如下面代码所示:
也就是添加了
on_delete=models.CASCADE
使其级联删除。
1 | org = models.ForeignKey(CourseOrg, on_delete=models.CASCADE, verbose_name=u"所属机构") |
makemirgration & migrate生成表
1 | makemirgration |
上图为makemirgration过程中输出的信息。可以看到我们做出的改动
此时我们查看app目录中migrations文件夹可以看到产生的新文件。
operation/migrations/0001_initial.py:
可以看到里面也是Python的语法。他会帮我们生成数据表。
以后每次migrations
时都会生成新的initial文件。这是很重要的变动文件,不能随意删除。
打开Navicat可以看到django的数据库中有它默认的django_migrations表
双击django_migrations表可以看到我们migration的记录。
会记录哪个app下的哪个initial.py已经运行了。
进入Navicat进行成功性验证:
可以看到我们的表已经生成成功,命名规则为: app名称 + 我们的类名变成小写
把我们的四个app放到一个文件夹下。
- 新建Python的package: apps
- 把四个app都拖进apps中去。
去掉searchfor
的勾选。拖进去之后会报错,说找不到那些import的模块了。
解决方案:右键Mark
为sourceRoot
。根目录下找不到的,会去apps下搜索。
但是这时候cmd下还是会报错。
解决方案(图来源于我的DjangoGetStarted
教程):
同理,插入第0是希望它先搜索我们app下东西:
成功性验证
可以看到Django已经可以正常run成功了。
第四章总结
- 我们设计了app
- 设计了user models.py
- 循环引用
得出我们需要创建一个更高层次的app。分层设计,operation在更高层。
- Courses models.py
- organization models.py
- operation models.py
通过makemigrations 生成表的变动 & migrate
每个app下的migration目录的用途,和数据库中django_migration
将所有app放到同一个目录之下。
本章结束对应commit:
数据表全部生成,migration目录&表django_migration。将app放到apps目录。对应4-9.
第四章结束!撒花。
总之,我们要拿来。我们要或使用,或存放,或毁灭。 - 鲁迅
既然有django提供的强大admin,我们便要拿来用喽。
- 使用xadmin,通过adminx,将已有model注册进后台。快速搭建可用的后台系统。
xadmin快速搭建可用的后台系统
django admin介绍
上一章我们进行了需求分析和数据库设计。本章我们来快速搭建一个可用的后台管理系统。
后台管理系统特点:
- 权限管理
- 少前端样式。(样式一般不是很看重),
- 快速开发
django的后台管理系统是一套智能的管理系统。
django的杀手锏之一就是admin管理系统。
admin在项目新建时就已经为我们生成好了。
Django的admin也是一个app,在我们新建项目时就创建好了。
而且会自动在url中配置好了链接。
访问:http://127.0.0.1:8000/admin/
可以看到admin的登录窗口。
Django是不会自动生成admin的用户的,需要我们自己去命令生成。
createsuperuser
点击Tools 菜单下 Run manage.py Task
1 | createsuperuser |
输入自己的用户名密码。
报错:
1 | django.db.utils.DataError: (1406, "Data too long for column 'gender' at row 1") |
gender中female是6位。而我们最大长度只有5.
修改后
1 | makemigrations users |
然后重新createsuperuser
使用自己定义的用户名密码可以登进系统。
默认是用户名 + 密码。后面会讲到如何实现用户名 或 邮箱和密码登录。
修改setting中对应语言,时区,以及数据库写入时间。
修改
1 | # 语言改为中文 |
点击运行可以看到如下图被换成汉语的效果:
注意: django 2.0.1 并不会看到汉化后的默认页面。只有admin被汉化了。
组对应数据表: auth_group
在Django的admin中可以把上章的表都注册进来。对于表进行任意的增删改查。
默认其实会把user也注册进来的,但是因为我们通过userProfile覆盖了user。所以没有显示。
注册UserProfile进来
users/admin.py:
1 | # encoding: utf-8 |
可以看到我们的用户信息就注册进来了。
USERS
是用户所在表名称。
进入页面可以看到Django为我们把每个不同类型的字段生成了不同的前端样式。
Django会自动帮我们把密码加密,而且不能反解。单向性。
如果出现错误, 可能是initial
文件在我们拖入apps时路径被改变。之后我们添加了环境变量, 前面再加上apps就会报错。
这时把initial.py
中路径进行修改。
错误2:
1 | 新增用户信息提示: |
解决方案1: 不用解决,之后换Xadmin就好了。
解决方案2: 在setting的databases中添加以下代码取消外键检查
1 | DATABASES = { |
实验成功为了不影响后面,把options删除
本小节结束对应commit:
admin中添加管理器&注册。时区,语言,utc(False).数据库中选项参数。female的长度修改, createsuperuser.对应5-1
xadmin的安装
一套基于admin, 比admin更强大的系统。
- 通过pip安装
1 | pip install xadmin |
Python3 & Django2.0.1安装官方适配Django2.0的包
1 | pip install git+git://github.com/sshwsfc/xadmin.git@django2 |
xadmin可以把我们的后台做的很强大,可扩展。
可以看到它同时下载了很多其他依赖包。
注册Xadmin 与 crispy-forms
Mxonline2/settings.py的INSTALLED_APPS中
1 | 'xadmin', |
然后把urls中默认admin指向Xadmin
1 | # 导入x admin,替换admin |
Python3 Django2.0.1 的url的配置中
1 | path('xadmin/', xadmin.site.urls), |
注意:Django 2.0.1中不需要加r
也不需要加^
将我们原来写的user/admin.py中代码注释掉。
此时直接运行项目会报错,因为我们Xadmin的默认数据表并没有migarte
1 | ProgrammingError: (1146, "Table 'mxonline2.xadmin_usersettings' doesn't exist") |
点击Tools 菜单下 Run manage.py Task
1 | makemigrations |
可以看到已经被应用成功。
前往Navicat进行验证。
可以看到新增的表。
Xadmin的后台采用的是bootstrap。
后面我们会介绍如何制作插件
源码安装:
github: https://github.com/sshwsfc/xadmin
下载或git clone
将源码下载到本地。
解压后将Xadmin文件夹复制到我们的项目中。
Python3版本源码安装:与url配置不同
1 | git clone -b django2 https://github.com/sshwsfc/xadmin.git |
其余操作一样。
新建extra_apps,并在setting中注册地址
新建new package: extra_apps
使用该目录存放我们的第三方插件,将Xadmin移入。
右键mark为SourceRoot, 但是这时候cmd下回报错。
所以在setting.py中加入。
1 | sys.path.insert(0,os.path.join(BASE_DIR, 'extra_apps')) |
因为我们的source目录已经有Xadmin了,就不会再去系统环境中找了。这时候卸载我们的Xadmin。
1 | workon mxonline2 |
但是他的依赖包我们还需要,所以只需要卸载Xadmin。此时我们运行会报错
1 | from future.utils import iteritems |
安装必要的包:
1 | pip install future |
此时又可以成功运行了
日志记录:后台管理人员做的操作都会生成一条记录。
源码安装优点:
- xadmin新特性
- 对于源码进行自己的修改。
本小节结束对应commit:
Xadmin的安装与源码安装,配置setting中extra_apps. 对应5-2
users app 的model注册
遗留问题: django2.0.1使用xadmin时。如验证码等带dateTimefield区域出错。
xadmin/widgets.py
1 | input_html = [ht for ht in super(AdminSplitDateTime, self).render( |
此时可以看到已经运行正常
真正开始
Xadmin是基于Django的admin来开发的,所以Xadmin也继承了许多admin的用法。
- 比如: models的注册。
UserProfile已经被自动注册进去了,我们从验证码开始注册。
我们需要新建一个adminx.py
文件,Xadmin会自动搜寻这种命名的文件。
新建py文件的初始化模板
新建users/adminx.py:
1 | # encoding: utf-8 |
可以看到这时候访问已经有邮箱验证码了。
邮箱验证码这几个字就是我们代码中Meta中verbose_name定义的:
1 | class Meta: |
verbose_name_plural
是verbose_name
的复数形式。
字段的verbose_name会直接显示在后台。sendtype
和sendtime
没有设置所以直接显示了英文。
可以看到我们添加验证码成功。注意:上节版本中我们进行了: makemigaration & migrate。
但是它是pip安装的Xadmin的数据表生成。我们卸载之后,源码安装需要重新运行进行数据迁移。(django需要通过app文件夹下的init文件来记录表的更改记录,pip的都卸了,所以就没法找到了)
会报错:
Xadmin_log不存在错误。只需要运行这两条命令即可。
解决后台部分英文显示
全部models中字段自行添加verbose_name
这里就不贴出来了,自行检查都加上(没写出的请自行修改全部加上verbose_name)。
解决EmailVerifyRecord object显示
全部(没写出的请自行修改)model,py2:重载
__unicode
py3:重载__str__
1 | # 重载Unicode方法使后台不再直接显示object |
上面代码是python的自身基础语法。
配置显示列
users/adminx.py的管理器中设置list_display:
1 | # 创建admin的管理类,这里不再是继承admin,而是继承object |
list_display可以使用列表或元祖,建议使用列表。否则元组只有一个元素,忘记加逗号就会报错。
选择框的生成是因为我们加上了choices
配置搜索searchfield
users/adminx.py的管理器中EmailVerifyRecordAdmin添加
1 | # 配置搜索字段,不做时间搜索 |
再添加一条数据验证搜索功能
xadmin导出csv中文乱码解决
将
charset=utf-8
改为charset=gbk
xadmin导出xml报错
1 | TypeError at /xadmin/users/emailverifyrecord/ |
io.StringIO这个库新版本的python3直接往这个库中加入了一些新的内容,使得该库在Python2.7中较为混乱。
将StringIo变为BytesIO
通过时间筛选字段。
users/adminx.py的管理器中EmailVerifyRecordAdmin添加
1 | # 配置筛选字段 |
Django的admin, Xadmin和其他系统区别
不像php等其他语言是一个功能模块一个功能设计的。
Django是对于每张表增删改查的管理器,我们可以在增删改成的基础上加上我们自己的后台逻辑。
因此某种程度可以说他是不依赖于具体业务的。不管啥系统后台都是由表组成。
不依赖于后台逻辑,又可以加上逻辑。
user/models的注册
users/adminx.py中
1 | # 创建banner的管理类 |
1 | # 将model与admin管理器进行关联注册 |
此时后台页面。
可以自行测试轮播图是否可以新建成功。
本小节结束对应commit:
usersmodels三张表注册进xadmin, 配置搜索过滤展示字段,修复xadmin导出xml错误,导出csv乱码,Unicode重载。对应5-3
py3(django2.0.1):
usersmodels三张表注册进xadmin, 配置搜索过滤展示字段,修复xadmin导出csv乱码,修复django2.0.1的indexError, str重载。对应5-3
剩余app model注册
courses注册
新建courses/adminx.py:
1 | # encoding: utf-8 |
注意:对应后台显示英文的字段自行检查
verbosename
,自行加上。
注意: py2下重载__unicode__
方法,py3下重载__str__
方法
如(注意缩进):
1 | def __str__(self): |
int
类型后台会生成如下图区间取值:
可以看到有外键关系的会有一个小符号。
注册机构app的adminx
新建organization/adminx.py
:
1 | # encoding: utf-8 |
注意:对应后台显示英文的字段自行检查verbosename,自行加上。
注意: py2下重载__unicode__
方法,py3下重载__str__
方法
如(注意缩进):
1 | def __str__(self): |
如果注册后没有显示: 重新登录,或重启项目
operation app注册xadmin
新建operation/adminx.py
1 | # encoding: utf-8 |
注意:对应后台显示英文的字段自行检查
verbosename
,自行加上。
注意: py2下重载__unicode__
方法,py3下重载__str__
方法
如(注意缩进):
1 | def __str__(self): |
成功性验证:
本小节结束对于commit:
5:4 注册完成operation,机构,课程app。注意:自行重载str/unicode。补全verbosename。
xadmin全局配置
将全局配置修改:
- 如左上角:django Xadmin。下面的我的公司
- 主题修改,app名称汉化,菜单收叠。
使用Xadmin的主题功能。
把全站的配置放在users\adminx.py中:
1 | from xadmin import views |
解决django1.9(python2)下Xadmin主题不生效问题。
https://my.oschina.net/u/2396236/blog/1083251
- 安装requests
1 | pip install requests |
- /xadmin/plugins/themes.py 引入requests
1 | import requests |
- 修改block_top_navmenu方法:
1 | def block_top_navmenu(self, context, nodes): |
修改django admin 和下面的我的公司收起菜单
1 | # x admin 全局配置参数信息设置 |
apps.py配置app的显示名称
每个app下执行同样操作:
1 | # encoding: utf-8 |
注意自行找猫画虎为每个app添加中文名
新建app时并没有引用apps的配置
在app下的init.py中添加:
1 | default_app_config = "operation.apps.OperationConfig" |
注意对应关系。
注意为每个都添加对应的default_app_config
最终大功告成:
自定义导航菜单顺序
1 | class GlobalSetting(object): |
最终成型菜单:
日志记录的使用
日志记录会记录下我们进行过什么操作。
通过点击动作,进入当时修改的某条信息
第五章完结对应commit:
5-5 (第五章完结),配置了页头页脚信息,修改了菜单的顺序,配置apps中文名,修复Python2下,xadmin主题不生效问题。 完结撒花。
矛盾的普遍性是指矛盾存在于一切事物的发展过程之中,矛盾存在于一切事物发展过程的始终。
我想只要是个系统,就少不了登录注册。这是我们需要首先处理的主要矛盾。
- 完成登录 注册 找回密码 激活 验证码集成
完成登录 注册 找回密码 激活 验证码集成
6-1 首页和登录页面的配置
用户访问我们的根目录,我们需要把html文件返回给用户。因此我们第一步把html文件放入template目录。
在html中找到首页的html。拷贝到我们的template目录
新建static目录
用来存放css, js等静态文件
配置处理静态文件的url。
Django2.0.1版本下:
Mxonline3/urls.py:
1 | from django.views.generic import TemplateView |
Django1.9.8版本下:
Mxonline2/urls.py:
1 | from django.views.generic import TemplateView |
此时运行访问就可以访问到我们的index页面,不过会没有样式。
设置static文件
1 | # 说明静态文件放在哪个目录 |
修改index页面中前端样式的引用地址
使用ctrl+f查找出所有../
,全部替换为/static/
然后点击运行,刷新页面可以看到我们的页面已经显示正常了。
拷贝登录页面到template
使用ctrl+f查找出所有../
,全部替换为/static/
将css,js,图片全部替换完。
url配置跳转登录页面
Mxonline2/urls.py:
1 | # 登录页面跳转url |
Mxonline3/urls.py:
1 | # TemplateView.as_view会将template转换为view |
在index页面,ctrl+f找到登录。将a标签中地址替换为login的url。
取消注释后,将login.html改为/login/
点击左侧减号收起。然后使用<!--
与-->
将个人中心暂时注释。
可以看到登录注册,点击登录。
根路径下的所有url都不需要斜杠
此时我们的首页已经可以成功显示,通过首页点击登录也可以成功跳转登录页面
本小节完成对应commit:
6-1: 完成首页与登录页面配置,设置了STATICFILES_DIRS。注意:dirs是一个元组,不要少逗号。删除了前面上传头像等直接传到根目录的目录。
6-2 用户登录-1
配置url之前我们要书写好对应处理的view
Django的view实际就是一个函数,接收request请求对象,处理后返回response对象。
users/views.py:
1 | # encoding: utf-8 |
django1.9.8/urls.py
1 | from users.views import user_login |
django2.0.1/urls.py:
1 | from users.views import login |
在两行返回语句的位置打上断点:
点击debug,进入首页后点击登录。可以看到
说明确实是通过get请求请求页面的。
通过值浏览器窗口可以看到这是一个<WSGIRequest: GET '/login/'>
对象
path:
是指向的地址。
f8
继续运行。跳转到登录页面。
6-3 用户登录2
html form基础知识
templates/login.html:
可以看到form表单中有input。点击提交会把值提交到后台。我们需要修改action让它指向我们的后台相应地址。
input中的name值会被传递到后台。回组成键值对形式。
submit类型的input
只保留post这里的断点。输入用户名密码。查看debug情况
403禁止访问
错误: html页面内必须加上crsf token
才能传值到后台。
我会随机的给前端发一串符号,你必须把这串符号带回来,我才允许你post。
from表单之前写上crsf token
此时我们查看前端页面:
可以看到html中登录下面有一个隐藏着的值:crsf token, 不会显示。
此时点击登录跳转到pass位置。
可以看到request中的POST中是一个queryset的对象。我们可以把它当成一个字典来用。
来取到前端的数据
1 | if request.method == "POST": |
取到用户名和密码我们就要开始进行验证登录。使用Django自带的auth
方法。
1 | from django.contrib.auth import authenticate, login |
authenticate调用只需要传入用户名和密码。成功会返回user对象,失败返回null
html中通过
设置成如果登录显示个人中心那段,未登录显示登录注册
打上断点
点击debug后可以看到
我们成功的取到了值。
Django默认我们使用用户名和密码来登录
成功的登录user值如下
但是继续执行报错:
1 | login() takes exactly 1 argument (2 given) |
这时因为我们处理登录的自定义函数也叫login。就直接调用了自身,而不是调用Django提供的login。所以我们一定不要把自定义view函数命名与Django提供的冲突
解决方案:将我们的login改为
def user_login(request):
并且前往urls.py中将login也一并改了
此时运行可以看到我们的个人中心已经出来了。
改造为使用邮箱用户名均可。Setting中重载变量
自定义authenticate方法
1 | from django.contrib.auth.backends import ModelBackend |
Mxonline2/settings.py
1 | # 设置邮箱和用户名均可登录 |
使用xadmin的退出,注销当前用户的退出。
此时我们可以通过邮箱和用户名都可以完成登录。
用户提示:return页面时提供它的错误信息
1 | return render(request, "login.html", {"msg":"用户名或密码错误! "}) |
Html中如何取到这个值;
login html中这段是用来做错误提示的。
1 | <div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div> |
验证:
本小节结束对应commit:
6-2&3 完成了用户登录,登录验证自定义:邮箱用户名均可。错误信息返回前端。设置登录显示个人中心判断,注意不要把自定义方法写成login。
6-4 用form实现登录-1
上面我们的用户登录的方法是基于函数来做的。本节我们做一个基于类方法的版本。
要求对类的继承有了解。
基础教程中基本上都是基于函数来做的,其实更推荐基于类来做。基于类可以带来不少好处
1 | # 基于类实现需要继承的view |
继承的view中的方法。
django1.9.8 urls中的配置:
1 | # 换用类实现 |
Django2.0.1 urls配置:
1 | # 基于类方法实现登录,这里是调用它的方法 |
6-5 form字段验证
验证最大长度,是否为空等一系列。
users下新建forms文件。
1 | # encoding: utf-8 |
定义好forms之后我们来使用它做验证。
1 | def post(self, request): |
完善错误提示
比如:既然表单都验证失败了,就不用显示密码出错了
1 | # 仅当用户真的密码出错时 |
forms中的名称username和password必须和html中的一致。毕竟他是使用的request.POST
而request是从前面传过来的。
实例化LoginView
时已经对于我们的字段进行了验证。
打上断点:
debug
后f6
运行到
此时可以看到errors(ErrorDict)
中的错误
将form传回前端:
前端中取值:
给这个class加上errorput会显示红色外框。
注意:写在class里面
将forms错误信息显示出来
1 | <div class="error btns login-form-tips" id="jsLoginTips"> |
- 写了一个类继承Django的view,然后写了get post方法(get/post的if是Django替我们完成的)
- 在url中调用Loginview的as_view方法需要加上括号,进行调用。
- Django的form进行表单验证并把error值传到前台。
- is_valid方法,验证表单
本小节完毕对应commit:
6-4 & 5 登录换用类继承view实现,使用Django form进行表单验证并把错误信息提示到前台。
6-6 session和cookie自动登录机制
我们本节来讲session和cookie
User1如何实现登录的。
cookie的存储
cookie是浏览器支持的一种本地存储方式。以dict,键值对方式存储。
1 | {"sessionkey": "123"} |
浏览器会自动对于它进行解析。
http请求是一种无状态的请求
用户向服务器发起的两次请求之间是没有状态的。也就是服务器并不知道这是同一个用户发的。
做到记住用户:
浏览器a在向服务器发起请求,服务器会自动给浏览器a回复一个id,浏览器a把id放到cookie当中,在下一次请求时带上这个cookie里的id值向浏览器请求,服务器就知道你是哪个浏览器发过来的了。
有状态请求(cookie)
服务器a
发回来的id
会放到服务器a
的域之下。不能跨域访问cookie。
使用浏览器随便打开一个网页,然后f12
打开。
比如我使用的Chrome
浏览器
会找到存储在浏览器本地的cookie值
点击clear all
清空所有的cookie
f5
刷新页面,会发现又把这些cookie值进来。
如果将用户名和密码直接保存在cookie,可以实现最垃圾最简略版本的自动登录。
解决cookie放在本地不安全的问题(session)
用户在第一次请求后,浏览器回复的id既可以是用户的user id。
也可以一段任意的字符串,我们把它叫做session id
根据用户名和密码,服务器会采用自己的规则生成session id
。这个session id
保存在本地cookie。浏览器请求服务器会携带。
- 输入用户名 & 密码
- 调用 login(), 后端程序会根据用户名密码生成session id。保存在数据库中。
- 用户登录之后,需要通过这个
session id
取出这些基本信息。
Django的默认表中的session
表就记录了用户登录时,后端我们Django为用户生成的sessionid
。
可以看到session key value
和过期时间。
我们可以清空这张表的数据。运行项目进行登录。
可以看到我们刚刚生成的session id。
此时通过f12
查看浏览器在本地存储的session id
。可以看到如下图和我们数据库中的一致。
session_key 发到浏览器叫做session id
通过session id 用户访问任何一个页面都会携带,服务器就会认识。
Setting.py中,
这个app会拦截我们每次的request请求,在request
中找到session id,然后去数据表中进行查询。
然后通过session key
去找到session data
。此时直接为我们取出了user。
在服务器返回浏览器的response
中也会直接加上session id
cookie是浏览器本地存储机制,存在域名之下,存储不安全。
服务器在返回id时通过规则生成一串字符,并设置了过期时间。存储在服务器端(数据库)
6-7 用户注册
拷贝注册页面进入template目录
书写我们对应要处理的view(RegisterView)
users/views.py
1 | # 注册功能的view |
配置对应的url
Django1.9.8 url配置如下:
1 | from users.views import RegisterView |
Django2.0.1 url配置如下
1 | from users.views import RegisterView |
修改index页面中注册url
此时访问首页发现可以成功跳转到注册页面
修改静态文件中static目录引用
关键步骤load staticfile
然后修改路径为一个相对于static的相对路径
他会自动根据setting中配置,为我们加上前缀
如果我们把目录在setting中改到mystatic。url中会自动添加指定的前缀
可以看到可以访问成功。
将目前的三个html中的静态文件全部修改目录
枯燥但是要有耐心。
这时候访问三个页面,查看样式是否完好。
验证码库实现验证码
https://github.com/mbi/django-simple-captcha
安装配置
1 | workon mxonline3 |
Add
captcha
to theINSTALLED_APPS
in yoursettings.py
Add an entry to your
urls.py
:
django1.9.8如下:
1 | from django.conf.urls import url, include |
django2.0.1如下;
1 | # 验证码url |
1 | makemigrations |
进入数据库查看生成的表
将验证码展示到页面
users/forms.py:
定义我们的register form:
1 | # 引入验证码field |
users/views.py
在我们的registerform中实例化并传送到前端:
1 | # form表单验证 & 验证码 |
前端获取验证码值
找到上图验证码部分。修改为下图
Forms中的field会生成不同的框。
我们只有label但是前端可以查看到input框等,也就是Registerform会为我们生成输入框+验证码。
隐藏的字符串的框会被带到后台,由Django为我们进行验证。验证该验证码是否保存过。
可以看得我们数据库中将这个hashkey进行了保存。这个key与验证码内容对应。
后台会把验证码值 和 hashkey进行联合查询。
编写register view的后台逻辑(RegisterView)
users/views.py的RegisterView中添加post方法:
1 | def post(self, request): |
修改form表单提交方式与提交到哪个url
前端的form提交加上对应的crsf token
刷新验证码是前端帮我们完成的:
1 | //刷新验证码 |
获取前端页面值并封装成一个user_profile对象,保存到数据库。
1 | from django.contrib.auth.hashers import make_password |
发送邮件实现
setting中配置;
1 | # 发送邮件的setting设置 |
新建package后新建文件。
apps:utils/email_send.py
:
1 | # encoding: utf-8 |
上图为qq邮箱开启smtp服务
点击生成授权码
def post中加上发送邮件
users/views.py
:
1 | # 发送邮件 |
点击注册提交,因为我们没有return。一直在转圈圈。
但是数据库中已经添加了字段。
可以看到我们的邮件已经被发送到邮箱中。
如果注册成功返回login页面:不成功,返回register页面并报错。
完善错误提示。
找猫画虎:将login中的错误提示搬运到register中来。
- register_form的报错信息。
- 邮箱 & 密码 form验证
完善用户值回填逻辑
如果传回的有值则,显示传回来值。
密码也做同样操作
修改默认的激活状态为false
post方法中
1 | # 默认激活状态为false |
书写处理激活的view。
1 | # 激活用户的view |
配置用户激活的url并通过url提取到变量:
django1.9.8:
1 | # 激活用户url |
django2.0.1:
1 | # 激活用户url |
这里通过?p
将后面.*
代表全部提取的正则,符合的内容传入参数active_code中/$
代表以/$
为结尾
其他细节根据自己需要进行优化。
注册功能制作完毕。对应commit:
注册功能实现完毕,流程:注册,发邮件,激活,登录。对应6-6,7,8,9,10
6-8 找回密码
这个6-8对应对应6-11,6-12
拷入forgetpassword页面
书写处理忘记密码的view
users/views.py
1 | # 用户忘记密码的处理view |
django2.0.1 urls中配置:
1 | # 忘记密码 |
Django1.9.8 urls中配置:
1 | # 忘记密码 |
login html中忘记密码
配置忘记密码页面中静态文件。
1 | load static |
定义一个给forget的form
users/forms.py:
1 | # 注册验证码实现 |
添加验证码
1 | # 用户忘记密码的处理view |
html中加上验证码
post中逻辑
1 | # post方法实现 |
邮箱重置密码邮件发送
1 | elif send_type == "forget": |
前端页面添加错误信息
已经重复很多遍这个操作了。
上述三图进行改正,不一一列举
书写重置密码view
1 | # 重置密码的view |
配置重置密码url
1 | # Django1.9.8: |
拷贝进来password reset页面
添加一个隐藏的input框,以便于我们知道到底是哪个用户在重置密码
配置html中三大变化加url配置。
reset
的url
需要我们传参进来,但是modify
的不需要。
所以url配置和view都得分开。
创建改变密码的forms:
1 | # 重置密码form实现 |
书写改变密码的view;
1 | # 改变密码的view |
配置modify的url。
django2.0.1:
1 | # 修改密码url; 用于passwordreset页面提交表单 |
django1.9.8:
1 | # 修改密码url; 用于passwordreset页面提交表单 |
建议自行走一遍注册,登录,忘记密码。重置密码。
错误的激活链接,错误的重置链接。值回填,form报错
更多: 重置密码链接是否被点击过,过期时间。
对应commit忘记密码重置功能实现完毕,并进行了必要的测试。对应6-11,6-12
在绝望中寻找希望,人生终将辉煌 - 新东方
作为一个正经的教育网站,我们更是拥有正规的机构合作: 比如来自火星的星星优培。
- 完成授课机构的功能实现。
完成授课机构的功能实现。
7-1 django templates模板继承1
- 机构可以筛选类别
- 机构可以根据所在地区进行分类
右侧我要学习功能: form表单提交
右下:授课机构排名
页面头部与底部为全局头和全局底部。
Django template 共用头部底部机制
将head和foot放在两个html中,然后在写其他需要这两个部分的页面时include进来。
Django也是支持include机制的。
include的问题
include的进来的死页面,这时候该怎么办?
解决这种问题:进行模板的继承机制。定义一个父类的框架,子类可以替换其中一部分block,子类只需要重写自己需要改变的block。
template中新建base.html
将课程机构列表页。orglist拷贝进template目录
将orglist内容替换base内容。
将div收起来
loadstaticfiles & 修改静态文件路径为static
这个步骤做过太多遍了,自行完成。耐心就行了。
定义父模板: 包含head & footer
title应该是可以被子页面替换的所以要包起来。
css有共用的部分,也有可以被子页面替换的部分。
js同理
面包屑是需要被各个页面自己替换的。
将正文内容包起来;
此时base页面就制作好了
7-2 开始orglist编写
第一步:清空所有内容
- 继承base页面
- 覆盖父类的title
- 书写课程机构view
organization/views.py
1 | # encoding: utf-8 |
- Django2.0.1配置课程机构首页url
1 | # 课程机构首页url |
- Django1.9.8配置url:
1 | # 课程机构首页url |
修改面包屑
- base中只保留首页
- org中重写block custom_bread
block之间没有先后顺序。
将base中block content拿到orglist重写
- 然后将base中block中间section删除掉
orglist开始loadstaticfiles
ctrl+d
快速删除
页面的继承关系使得变量也可以直接用
比如user中的form数据传递到register文件当中.如果register继承的是base页面。
base页面当中也是可以用这些数据的。参数的向上传递
每个request对象都会传递到html中来,如果继承了base,request也会向上传递到base。
base中就可以加入我们的逻辑: 用户是否登录等。
小节结束对应commit:
完成Django templates的继承关系了解,机构列表展示页。对应7-1 & 2
7-3 课程机构列表页数据展示1
确定由后台传过来的动态数据:
授课机构列表本身, 授课机构的排名,所在地区(后台取出所有地区), 机构类别写成静态,因为一般不怎么变动。
在xadmin中添加城市信息,课程信息。
添加城市
添加机构。
插播知识点:
这里指定的路径是一个相对路径
setting中要配置我们把文件存放在哪个根目录之下
1 | # 设置我们上传文件的路径 |
在项目根目录创建media文件夹
在后台上传图片
修改机构信息中封面图为logo
自行添加十个课程机构
models中添加机构类别
organization/models.py:
1 | class CourseOrg(models.Model): |
修改了models之后做数据库的变动:
1 | makemigrations organization |
完成之后打开Navicat进行验证:
可以看到新增了。
完善我们的view
将列表里的静态数据变成后台获取的动态数据
organization/views.py
1 | from .models import CourseOrg, CityDict |
7-4 课程机构列表页数据展示2
前去html中进行数据填充
可以看到所有城市是通过a标签,当前选中城市为active。
之后把下面的写死的城市删除掉。
这时就是我们在后台添加的数据了
可以看到每个课程机构都是一个dl
同理使用for循环。
如何将imageField转换为图片地址
数据库中img存放的是字符串:相对路径
上图这种取法会取出一个相对地址。
将setting中配置的mediaurl放在前面可以补全地址。
设置media处理器
注册之后,mediaurl将可以在html中使用
图片还是没有显示。因为url中没有处理图片相应路径的url
Django1.9.8 urls.py:
1 | from django.views.static import serve |
Django2.0.1 urls.py:
1 | from django.views.static import serve |
完善xadmin的adminx为机构添加分类索引字段
organization/adminx.py
1 | # 机构课程信息管理器 |
去除加载小圈圈
static/css/style.css中scrollLoading置为空:
1 | .scrollLoading { |
完成后台数据添加,列表页数据展示。对应7-3&7-4
7-5 列表分页功能
github搜索django-pure-pagination
1 | pip install django-pure-pagination |
install app中添加:
1 | 'pure_pagination', |
可设置参数;
1 | PAGINATION_SETTINGS = { |
PAGE_RANGE_DISPLAYED
是总共会显示多少个page。(包括省略号,包括两边和中间)MARGIN_PAGES_DISPLAYED
是旁边会显示多少个。SHOW_FIRST_PAGE_WHEN_INVALID
:当输入页数不合法是否要跳到第一页
官方实例;
1 | from django.shortcuts import render_to_response |
我们对照着的实现:
1 | from pure_pagination import Paginator, EmptyPage, PageNotAnInteger |
对于html中分页进行配置
不再是objects,而是objectlist
使用默认的render
自定义html的样式
本小节完成对应commit:
7-3, 4 & 5:完成列表数据展示列表分页功能:使用pure_pagination
7-6 分类筛选功能
当用户点击某一个city时对应加上参数city的id
在后台处理这个city
获取传入的参数进行进一步筛选。
将city_id传回html,使得可以知道哪个是选中的。
因为city.id是后端传回来的值是一个int。所以我们要做类型转换。
当city_id为空的时候,显示全部。
后台处理类别
1 | # 类别筛选 |
返回前台类别值以active
1 | return render(request, "org-list.html", { |
对于类别做同样的ifequal判断
如上图所示进行城市与分类的联动:
当选择全部类别的时候,就只通过当前城市id。
当选择全部城市的时候,就只通过当前目录id。
当两者都选的时候使用&连接。
刚才统计机构数目过早,应该移到后面后面已经筛选过后,
1 | # 总共有多少家机构使用count进行统计 |
课程机构排名
1 | # 热门机构,如果不加负号会是有小到大。 |
循环时内置变量forloop.counter取当前循环到第几次
待完成:点击排名机构的连接
课程机构排序。
学习人数,课程数
organization/models.py
CourseOrg
1 | # 当学生点击学习课程,找到所属机构,学习人数加1 |
1 | makemigrations organization |
前端页面学习人数,添加参数sort
1 | # 进行排序 |
添加选择效果
7-7 modelform 提交我要学习咨询1
对应表userask
form
会对字段先做验证,然后保存到数据库中。
可以看到我们的forms和我们的model中有很多内容是一样的。我们如何让代码重复利用呢?
使用modelform解决这个问题。
1 | # encoding: utf-8 |
include的机制配置应用自己的url
django1.9.8 创建organization/urls.py:
1 | # encoding: utf-8 |
django1.9.8 Mxonline2/urls.py:
删掉orglist,新增如下
1 | # 课程机构app的url配置 |
django2.0.1: 新建organization/urls.py
1 | # encoding: utf-8 |
django2.0.1: urls.py中:
删掉org_list,新增include
1 | # 课程机构app的url配置 |
使用命名空间防止重复
解决Django2.0.1报错:
1 | 'Specifying a namespace in include() without providing an app_name ' |
在自己的app下的urls中写上appname
1 | # encoding: utf-8 |
html中使用命名空间方式:
使用modelform做提交
比较合理的操作是异步的,不会对整个页面进行刷新。
如果有错误,显示错误。一种ajax的异步操作。
因此我们此时不能直接render一个页面回来。
应该是给前端返回json数据,而不是页面
HttpResponse
类指明给用户返回哪种类型数据
1 | from django.http import HttpResponse |
配置一个modelform的view
1 | # 用户添加我要学习 |
配置相应的url
1 | # 添加我要学习 |
7-8 modelform 提交我要学习
分析ajax请求
1 | <script> |
监听这个button,用户如果点击了button。我们来向这个url进行post请求。
将我们的表单进行序列化。
form表单添加crsf_token
如果后台返回的状态值为success,那么我们将弹出提交成功。
失败,就会在错误提示框中写入。
手机号码正则表达式验证:
organization/forms.py
1 | # 手机号的正则表达式验证 |
本小节完毕对应提交commit:
7-7&8 使用modelform完成表单我要学习的异步提交
7-9 机构详情
- 机构首页
- 机构课程
- 机构介绍
- 机构讲师
登录xadmin添加基础的必要数据。添加课程与讲师。
课程中应该有一个外键指向它是哪个机构的。
courses/models.py
Django1.9.8中:
1 | from organization.models import CourseOrg |
django2.0.1下;
1 | course_org = models.ForeignKey(CourseOrg,on_delete=models.CASCADE, verbose_name=u"所属机构",null=True,blank=True) |
新增外键字段应该null=true,blank=true。
因为历史数据中没有这个外键字段
1 | makemigration course |
将前端给我们的org相关的四个页面拷进template
新建org_base页面
将org_home页面内容拿过去。
- loadstaticfiles -> 改css文件路径->改js文件路径->改图片路径
- 改已经实现的url。->将子页面继承后需要改得地方使用block包裹。
将home页面清空
完成替换之后添加访问的view以及URl
organization/views.py:
1 | class OrgHomeView(View): |
Django2.0.1下url
1 | # home页面,取纯数字 |
django1.9.8下url:
1 | # home页面,取纯数字 |
html中使用for循环遍历:
如上图可以直接通过外键字段再找到外键对象的字段
templates/org-list.html
配置里面跳转到详情页的url
1 | return render(request, 'org-detail-homepage.html',{ |
将course_org
也return回来,就可以把网页里这部分值替换掉
templates/org_base.html
数值会随继承链向上传递。
为讲师增加头像字段
organization/models.py
1 | image = models.ImageField( |
然后
1 | makemgration organization |
使用for循环填充数据
同理可得,我们把机构课程页不同的部分拿出来即可
配置访问的view和url
organization/urls.py:
Django 2.0.1:
1 | # 访问课程 |
Django 1.9.8:
1 | # 访问课程 |
organization/views.py
1 | class OrgCourseView(View): |
base页面的left链接修改
这里能直接用到course_org.id。是因为子页面render时都向上传递了course_org对象
使用for循环,填充内容。
左侧active修改
因为现在没有值能判断当前是哪个页面。所以在orghomeview中传值回来current page
写两个view
1 | class OrgDescView(View): |
加两个url
Django2.0.1下:
1 | # 访问机构描述 |
Django1.9.8下:
1 | # 访问机构描述 |
修改base页面相关跳转链接,注意点:加上course_org.id
重载我们的pagepath,使得面包屑动态显示
7-10 课程机构收藏功能
书写收藏的后台逻辑:
url配置
django2.0.1下:
1 | # 机构收藏 |
django1.9.8下;
1 | # 机构收藏 |
配套的view;
1 | class AddFavView(View): |
相关处理收藏的jQuery代码写在org base Html
添加返回页面的收藏值。
1 | # 向前端传值说明用户是否收藏 |
前台 org_base.html中
同理添加剩下的几个页面的。
第七章完结撒花。对应commit:
7-9&10&11&12将机构详情展示完毕,为课程机构添加了收藏功能。修复了index未登录状态下爆炸的错误
三年六班,三年六班,李子明,李子明同学,你妈妈拿了两罐旺仔牛奶给你
上帝说得有课程! 得有详情 !点了开始学习,得有章节!章节得有视频。
课程还得能评论下,还得有相关课程。于是这章出现了。
课程相关功能实现
8-1 课程列表
拷贝课程列表页到template目录
创建课程相关的urls.py
Mxonline2/urls.py中声明包含到course的url中:
1 | # 课程app的url配置 |
django2.0.1版本:
1 | # 课程app的url配置 |
书写处理列表展示相关的view
courses/views.py
1 | class CourseListView(View): |
courses/urls.py
1 | # encoding: utf-8 |
django2.0.1版本:
1 | # encoding: utf-8 |
此时访问没有样式。我们开始对于course list html进行工作
可以观察到它和orglist一样可以有共同的头尾。所以继承base页面
xadmin中添加一些课程。
然后在view中返回课程数据
1 | class CourseListView(View): |
保留一个div
通过外键字段取外键表中字段
分页功能
拷贝代码:
1 | from pure_pagination import Paginator, EmptyPage, PageNotAnInteger |
改动完成:
1 | class CourseListView(View): |
在html中使用时注意object_list
此时的all_course已经不是一个queryset,而是一个purepage对象。
对于页码进行修改
直接把orglist中的那段拿过来就行了。自行替换变量名称
此时已经好了。
排序功能
将之前的sort逻辑拷贝过来:
1 | # 进行排序 |
修改完成:
1 | # 进行排序 |
应放在分页之前。让分页处理所有筛选过的数据
return render时添加
1 | "sort":sort, |
用来判断激活状态。
修改a标签参数
1 | # 热门课程推荐 |
修改html中
for循环填充内容
这里的degree我们在数据库中填写的是字母。如何显示为中文。
- 个人猜测: template if
get_degree_display degree是字段名。专门用于choice字段显示
本小节完成对应commit:
8-1完成课程列表页展示,分页,热门课程。
8-2 课程详情页1
拷贝course_detail进入template目录
可以看出这个页面也是继承base页面的。将course_list的页面框架拿过来
替换面包屑。
配置url访问
django2.0.1:
1 | # 课程详情页 |
书写对应访问的view
1 | 课程详情处理view |
尝试访问:
在列表展示页放入详情的url。
有参数类型的把参数也传进来
进行数据填充:先取出当前的课程
1 | # 此处的id为表默认为我们添加的值。 |
html中取出数据:
课程的章节数如何实现?
models.py中自定义方法
1 | def get_zj_nums(self): |
添加课程类别字段
1 | category = models.CharField(max_length=20, default=u"", verbose_name=u"课程类别") |
1 | makemigrations |
operation中专门有张表是做用户学习记录的。
UserCourse查询有哪些学生学习了这门课
1 | # 获取学习这门课程的用户 |
链式调用取出数据
添加一些用户课程进行验证
可以看到已经大功告成
课程详情的view中添加clicknums+1
1 | # 增加课程点击数 |
8-3 课程详情页2
tab_cont1 中填充我们自己的内容。
教师数自定义函数
1 | def get_teacher_nums: |
不用自定义函数的方法如下
课程是否相关
定义课程的tag ,如果tag相同,那么是相关课程。
courses/models.py:
1 | tag = models.CharField(max_length=15, verbose_name=u"课程标签", default=u"") |
更改数据库后必然。此处略。
1 | tag = course.tag |
return render加上:
1 | "relate_courses":relate_courses, |
收藏功能:
将block js写到页面底部。
1 | <script type="text/javascript"> |
刷新后又不见了的问题,从view中传递has_fav的参数。前台进行判断。
1 | # 是否收藏课程 |
return render
1 | "has_fav_course":has_fav_course, |
html中使用;
8-2&3完成课程详情页展示,课程详情页机构,相关推荐课程。收藏课程,收藏机构。
8-4 课程章节信息
章节信息,评论信息。
course comments 和 course video放入 template
它也有head和foot继承我们的base页面
配置相应的url:
1 | # 处理课程章节信息页面的view |
1 | # 课程章节信息页 |
用户点击开始学习链接修改
为课程添加章节以及视频。
8-5 章节视频信息
为video表添加视频对应的url信息。
1 | url = models.CharField(max_length=200, default="http://blog.mtianyan.cn/" ,verbose_name=u"访问地址") |
将章节信息填充进页面
通过课程可以找到章节:course.lesson_set
video的时长添加
1 | # 使用分钟做后台记录(存储最小单位)前台转换 |
资源下载功能:
后台自行上传点文件
1 | all_resources = CourseResource.objects.filter(course=course) |
或者前端直接:
创建课程与讲师之间的外键关联
1 | teacher = models.ForeignKey(Teacher,verbose_name=u"讲师", null=True, blank=True) |
前往课程,设置讲师。
注意:不要在Unicode方法里使用外键字段很容易报错。
增加课程需知字段和老师告诉你学什么?
1 | you_need_know = models.CharField(max_length=300, default=u"一颗勤学的心是本课程必要前提",verbose_name=u"课程须知") |
将这两个字段显示到页面。
对应commit:
8-4&5完成课程章节信息,课程资源,课程老师信息。
8-6 课程评论页面
配置课程评论的url和view
1 | class CommentsView(View): |
1 | # 课程章节信息页 |
course video中跳转到评论链接
发表评论功能
ajax操作。如果发布成功就会刷新页面。
新建view用于添加评论:
1 | # ajax方式添加评论 |
添加配套的url:
1 | # 添加课程评论,已经把参数放到post当中了 |
js代码
1 | <script type="text/javascript"> |
后端已经将all_comments传过来了。然后for循环输出。
本小节完毕对应commit:
1 | 8-6完成课程评论功能,添加评论并展示到页面。 |
8-7 相关课程推荐:
学过该课程的还学过
CourseInfoView
添加
1 | # 选出学了这门课的学生关系 |
重点: 两个下划线代表我传进来的是一个list,你进行遍历。
comments也做同样处理
用户未登录,不要让他能点进view
如果使用的是方法型编程可以使用装饰器loginrequired
而我们使用的是类。所以要继承。
1 | from django.contrib.auth.mixins import LoginRequiredMixin |
8-7完成相关课程推荐功能,取出相关课程去除本身。课程评论login require鉴权添加。登录页面重定向回登录前浏览页面
8-8 课程播放页面
将视频播放页面拷贝到template目录
使用开源库video js
添加访问的url和view;
1 | # 课程视频播放页 |
1 | # 播放视频的view |
title 与面包屑的修改
对应commit:
第八章公开课模块全部完成,完结撒花。
师者,所以传道受业解惑也 - 韩愈
上帝说得课程得有老师来讲! 得有老师的详情 !
老师应该可以收藏分享,我得看到老师是哪个机构,它讲了啥课。于是这章出现了。
授课讲师列表页。列表页右侧是讲师排行榜。列表页可以进行排序
点击课程讲师进入课程讲师的详情页: 分享 & 点击收藏 右边是讲师所属课程机构
下面是讲师的课程详情。
讲师相关功能实现
9-1 讲师列表页
teacherlist 和 teacher detail 一起放到template目录之下
继承base页面
书写view与配置url
1 | # 讲师列表 |
添加讲师的年龄字段
1 | age = models.IntegerField(default=18, verbose_name=u"年龄") |
分页仿照orglist 注意object_list
view
1 | # 课程讲师列表页 |
排序 & 讲师排行榜
1 | sort = request.GET.get("sort", "") |
1 | "sort":sort |
将sort return到前端。实现active
排行榜讲师
1 | # 排行榜讲师 |
forloop.count 取出当前是第几次循环
9-2 讲师详情页
配置url和view
列表页中配置入口
1 | # 教师详情页面 |
1 | # 访问机构讲师 |
第九章完结
不以个人为中心, 但我们得有个个人中心。
做一做全局导航,做一做个人中心,做一做全局搜索。
洒洒水啦。
全局导航&个人中心&全局搜索
配置全局导航
让index页面也继承base页面
base页面的导航栏配置
但是现在我们不知道当前是哪一个页面,因为后端没有传值过来
后台的每个view中添加current nav字段。然后向上传递到base页面
为了满足前台有current view的值,我们写的每个view都得加上这个字段。
小技巧:根据request的地址中的前几位来判断在哪一个区域之下
request.path
修改url中
1 | # 访问机构讲师 |
全局搜索功能开发
搜索跳到列表展示
courselist后加参数keywords
搜索的代码放在deco-common js中
课程的搜索功能:
1 | # 搜索功能 |
1 | "search_keywords":search_keywords, |
个人中心信息展示
将用户中心相关的六个页面,全部拷贝进template
新建usercenter base页面
配置url
1 | # user app的url配置 |
1 | # encoding: utf-8 |
1 | # 用户个人信息view |
user app 下新建url
django自带的filter
request.user.mobile|default_if_none:’’
修改密码和修改头像
新建url 和 view
小技巧:
django的xadmin和admin当中,实际上是可以对form定义为文件的时候,是可以自动对上传的文件做保存的。
使用form的一个字段定义一个文件类型。把字段取出来就是内存中的文件。
赋值到user.image 就完成图片的一个存储。
users/forms.py:
1 | # 用于文件上传,修改头像 |
实例化时,传进来是post 和 文件类型的request 存放地址
上传文件时通过一个form完成的。
必须指明enctype,才能把文件类型传递到后台
url 和view
1 | # 用户头像上传 |
1 | # 用户上传图片的view:用于修改头像 |
这里的name必须和form中的一样
所有验证通过的字段放在cleaned data
1 | # 用户上传图片的view:用于修改头像 |
修改密码功能:
1 | # 在个人中心修改用户密码 |
必须与我们的form中定义的一致
实现的js代码在deco-user.js
js文件中的url就一定不能用template的模板语言了
修改邮箱提交form表单
有两个接口需要完成。点击获取验证码时,后台需要向用户新邮箱发送验证码。
邮箱如果出错,会返回错误信息。
输入了邮箱和验证码,验证是否匹配。
获取验证码接口:
配置url:
1 | # 专用于发送验证码的 |
新增邮箱验证码model类型
1 | SEND_CHOICES = ( |
发送邮箱验证码view
1 | class SendEmailCodeView(LoginRequiredMixin, View): |
发送邮箱验证码的功能放在deco-user.js中
1 | elif send_type == "update_email": |
修改邮箱的view
1 | # 修改邮箱的view: |
修改邮箱的url
1 | url(r'^update_email/$', UpdateEmailView.as_view(), name="update_email"), |
为userInfo view增加post方法。使用modelform完成直接提交
1 | def post(self,request): |
user_info_form
1 | # 用于个人中心修改个人信息 |
配置url和view
1 | # 用户中心我的课程 |
usercenter base中添加链接
1 | # 个人中心页我的课程 |
配置url和view
1 | # 我收藏的课程机构 |
1 | class MyFavOrgView(LoginRequiredMixin, View): |
1 | # 我收藏的授课讲师 |
url
1 | # 我收藏的课程机构 |
配置view和url
1 | # 我收藏的课程 |
1 | # 我收藏的课程 |
取消收藏
templates/usercenter-fav-course.html
取消收藏的代码在base页面中。三个js。
我的消息页面
配置url和view
1 | # 我收藏的课程 |
1 | # 我的消息 |
注册时发生欢迎消息
1 | # 写入欢迎注册消息 |
页面顶部小喇叭
所有页面都要读取一个共同的变量:未读消息的数量。我们需要向request中注入这个变量
所有页面都有request.user对象。所以我们在userprofile中自定义方法,
1 | # 获取用户未读消息的数量 |
泰山不让土壤,故能成其大;河海不择细流,故能就其深。
细节决定成败,大体的网站已经做好了。
佛曰:给我把细节再搞一搞
首页,全局功能细节和404以及500页面配置
登出和点击数以及收藏数完善
退出登录
Python3+django2.0.1下
1 | class LogoutView(View): |
1 | # 退出功能url |
点击数加1
CourseInfoView学习人数加1
1 | course.students += 1 |
TeacherDetailView:
1 | teacher.click_nums +=1 |
OrgHomeView
1 | course_org.click_nums +=1 |
收藏数统计
1 | exist_records.delete() |
1 | user_fav.save() |
注意负数的处理:
修改消息已读
1 | # 用户进入个人中心消息页面,清空未读消息记录 |
改进取出未读数字
1 | return UserMessage.objects.filter(has_read=False, user=self.id).count() |
首页功能开发
轮播图
公开课
授课机构
新建view
1 | ## 首页view |
为课程添加字段: isbanner
1 | is_banner = models.BooleanField(default=False, verbose_name=u"是否轮播") |
使用django自带模板标签add
添加2
为courseOrg
添加字段tag
1 | tag = models.CharField(max_length=10, default= u"国内名校",verbose_name=u"机构标签") |
template内建标签被五整除
配置全局404和500
Mxonline3/urls.py
1 | # 全局404页面配置 |
users/views.py
1 | # 404对应处理view |
Debug = True 404是不起作用的
1 | ALLOWED_HOSTS = ['*'] |
在debug为false情况下。
我们在访问media的时候配置过用serve来取
告诉它访问media的时候去哪个路径下找
debug为True
会自动前往STATICFILES——DIRS取文件的
一旦debug改为false,django就不会代管你的静态文件,
一般静态文件通过第三方http服务器代理转发。
nignx 和 Apache都会自动代理这些静态文件
方法1:我们自己url响应我们的static
1 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') |
常见web攻击及防范
sql注入攻击与防范
sql注入的危害
对于用户的输入进行合法性判断。
1 | class LoginUnsafeView(View): |
参数中加入sql语句拼接字符串使之为真。
使用django的orm,它已经对这些做了处理
xss攻击
xss跨站脚本攻击(Cross Site Scripting)的危害
正常流程
当传入iPhone6时,这个字符会被显示到页面中。
将这段代码改成js代码
黑客拿到你的cookie信息。然后伪装成用户。
Xss攻击防范
crsf攻击与防护
crsf跨站请求伪造(Cross-site request forgery)的危害
用户并没有向a请求,而是访问了b。b要求用户访问a的。
原因:用户向a的每次请求都会带上session id
图片中插入。
提交form表单必须添加crsf token
攻击网站无法生成crsf token
xadmin的进阶开发
static目录问题。
xadmin的static文件在自己的目录下所以我们找不到
1- url下的static路由配置注释掉
2- setting中的StaticRoot注释掉
1 | from xadmin.plugins.auth import UserAdmin |
点进UserProfileAdmin中可以看官方是怎么实现布局的,我们可以重载
get_form_layout
1 | 自定义的显示方式 |
get_user_model 获取用户: User =
django权限赋值方式:赋给用户。没一个表的增删改查。
auth_permission 组权限
自定义icon
model_icon = ‘fa fa-group’
xadmin/static/xadmin/vendor/font-awesome
官网下载然后替换css和font
默认排序
adminx中
1 | ordering = ['-click_nums'] |
两个字段是冲突的。
下拉框搜索:
1 | relfield_style = 'fk-ajax' |
当有外键指向他,会以ajax方式加载
数据量过大时很有用
inlines添加数据。
在一个页面直接完成章节。
1 | # 课程直接添加章节 |
没法完成在章节中再嵌套视频
但是可以有多个inline。在添加课程时添加课程资源
一张表分两个model来管理
1 | class BannerCourse(Course): |
设置proxy = true会具有model的功能,但不会生成表
1 |
|
admin中重载queryset方法。
1 | # 过滤列表中的数据 |
xamdin其他常见功能:
可以在列表上快速修改内容、
1 | list_editable = [ 'degree','desc',] |
自定义函数作为列
1 | def get_zj_nums(self): |
显示自定义的html代码
1 | def go_to(self): |
xadmin的工具: refresh
xadmin/plugins/refresh.py
列表页定时刷新的工具
1 | refresh_times = [3,5] |
字段联动
应用场景: 新增一门课程之后,courseorg的课程数+1
1 | def save_models(self): |
不用在课程里面添加章节数。
xadmin自行探究
- local 语言包
- migration 数据表的记录
- plugins 每一个后台页面都是一个plugin 插件机制
- static文件。js css
- template xadmin自己用到的html文件
- 对django admin的封装
下载源码包
添加插件使得可以识别ueditor field
记得在init中注册。
关闭自动转义。
导入excel
- 如何注入导入excel代码到菜单
- 如何只在课程列表显示
- 如何接收文件对文件进行处理
adminx 文件中的变量覆盖插件内部变量。
只需要重载block_top_toolbar
,就会把这个放到页面里面
默认使用bootstrap
init
确定是否启用插件,
1 | def post(self, request, *args , **kwargs): |
adminx中重写post方法。上传文件会被放在request.FILES
中。
中间pass步骤不管做什么事情,都要最后return父类的
1 | return super(CourseAdmin, self).post(request, args, kwargs) |
把项目部署上线
nginx + uwsgi(Python) Tomcat(java)
端口转发。负载均衡。
静态文件交给nginx转发。静态文件代理
1 | sudo apt-get install nginx |
1 | sudo apt-get install mysql-server |
1 | mysql -u root -p |
修改bind address
为0.0.0.0
是为了让win进行连接。真正部署尽量127.0.0.1
1 | sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf |
然后Navicat中连接会报错
你不用localhost(127)。使用本机ip都是不行的。
*.*
里面是可以指定某一张表。root用户名 IP地址
通过该IP地址过来的root用户,通过密码才可以访问所有表。
‘%’表示所有ip可以访问。
mysql下运行
1 | GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'tp131861' WITH GRANT OPTION; |
- 安装pip
1 | wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate |
1 | pip install virtualenv |
sudo ln -s /mnt/Mxonline2/uc_nginx.conf /etc/nginx/conf.d/
```
将setting中 static路径指明。然后将staticDIRS注释掉
项目根目录创建conf文件夹。然后创建uwsgi.ini.
uwsgi -i /mnt/Mxonline2/conf/uwsgi.ini &