Commit 0640abb2 authored by jackfrued's avatar jackfrued

更新了Linux、数据库和Django部分的文档

parent 87de54a0
This diff is collapsed.
......@@ -498,7 +498,7 @@ python3
```Python
>>> from pymongo import MongoClient
>>> client = MongoClient('mongodb://120.77.222.217:27017')
>>> client = MongoClient('mongodb://127.0.0.1:27017')
>>> db = client.school
>>> for student in db.students.find():
... print('学号:', student['stuid'])
......
This diff is collapsed.
## 深入模型
在上一个章节中,我们提到了Django是基于MVC架构的Web框架,MVC架构追求的是“模型”和“视图”的解耦合。所谓“模型”说得更直白一些就是数据,所以通常也被称作“数据模型”。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在很长一段时间都是持久化的首选方案,下面我们以MySQL为例来说明如何使用关系型数据库来实现持久化操作。
在上一个章节中,我们提到了Django是基于MVC架构的Web框架,MVC架构追求的是“模型”和“视图”的解耦合。所谓“模型”说得更直白一些就是数据(的表示),所以通常也被称作“数据模型”。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在过去和当下都是持久化的首选方案,下面我们以MySQL为例来说明如何使用关系型数据库来实现持久化操作。
### 配置关系型数据库MySQL
......@@ -9,7 +9,7 @@
1. 修改项目的settings.py文件,首先将我们之前创建的应用hrs添加已安装的项目中,然后配置MySQL作为持久化方案。
```Shell
(venv)$ cd oa/settings.py
(venv)$ vim oa/settings.py
```
```Python
......@@ -29,7 +29,7 @@
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'oa',
'HOST': 'localhost',
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '123456',
......@@ -50,13 +50,13 @@
NAME属性代表数据库的名称,如果使用SQLite它对应着一个文件,在这种情况下NAME的属性值应该是一个绝对路径;使用其他关系型数据库,则要配置对应的HOST(主机)、PORT(端口)、USER(用户名)、PASSWORD(口令)等属性。
2. 安装MySQL客户端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb。
2. 安装Python操作MySQL的依赖库,Python 3中通常使用PyMySQL,Python 2中通常用MySQLdb。
```Shell
(venv)$ pip install pymysql
```
如果使用Python 3需要修改**项目**的`__init__.py`文件并加入如下所示的代码,这段代码的作用是将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具而询问你:“Did you install mysqlclient? ”(你安装了mysqlclient吗?)。
如果使用Python 3需要修改**项目目录**下的`__init__.py`文件并加入如下所示的代码,这段代码的作用是将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具而询问你:“Did you install mysqlclient? ”(你安装了mysqlclient吗?)。
```Python
import pymysql
......@@ -64,15 +64,15 @@
pymysql.install_as_MySQLdb()
```
3. 运行manage.py并指定migrate参数实现数据库迁移,为应用程序创建对应的数据表,当然在此之前需要**先启动MySQL数据库服务器并创建名为oa的数据库**,在MySQL中创建数据库的语句如下所示。
3. 如果之前没有为应用程序创建数据库,那么现在是时候创建名为oa的数据库了。在MySQL中创建数据库的SQL语句如下所示:
```SQL
drop database if exists oa;
create database oa default charset utf8;
```
4. Django框架本身有自带的数据模型,我们稍后会用到这些模型,为此我们先做一次迁移操作。所谓迁移,就是根据模型自动生成关系数据库中的二维表,命令如下所示:
```Shell
(venv)$ cd ..
(venv)$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
......@@ -93,7 +93,7 @@
Applying sessions.0001_initial... OK
```
4. 可以看到,Django帮助我们创建了10张表,这些都是使用Django框架需要的东西,稍后我们就会用到这些表。除此之外,我们还应该为我们自己的应用创建数据模型。如果要在hrs应用中实现对部门和员工的管理,我们可以创建如下所示的数据模型
5. 接下来,我们为自己的应用创建数据模型。如果要在hrs应用中实现对部门和员工的管理,我们可以先创建部门和员工数据模型,代码如下所示
```Shell
(venv)$ vim hrs/models.py
......@@ -120,11 +120,11 @@
no = models.IntegerField(primary_key=True, db_column='eno', verbose_name='员工编号')
name = models.CharField(max_length=20, db_column='ename', verbose_name='员工姓名')
job = models.CharField(max_length=10, verbose_name='职位')
# 自参照完整性多对一外键关联
mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='主管编号')
# 多对一外键关联(自参照)
mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='主管')
sal = models.DecimalField(max_digits=7, decimal_places=2, verbose_name='月薪')
comm = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True, verbose_name='补贴')
# 多对一外键关联
# 多对一外键关联(参照部门模型)
dept = models.ForeignKey(Dept, db_column='dno', on_delete=models.PROTECT, verbose_name='所在部门')
class Meta:
......@@ -132,7 +132,7 @@
```
> 说明:上面定义模型时使用了字段类及其属性,其中IntegerField对应数据库中的integer类型,CharField对应数据库的varchar类型,DecimalField对应数据库的decimal类型,ForeignKey用来建立多对一外键关联。字段属性primary_key用于设置主键,max_length用来设置字段的最大长度,db_column用来设置数据库中与字段对应的列,verbose_name则设置了Django后台管理系统中该字段显示的名称。如果对这些东西感到很困惑也不要紧,文末提供了字段类、字段属性、元数据选项等设置的相关说明,不清楚的读者可以稍后查看对应的参考指南。
5. 通过模型创建数据表。
6. 再次执行迁移操作,先通过模型生成迁移文件,再执行迁移创建二维表。
```Shell
(venv)$ python manage.py makemigrations hrs
......@@ -151,7 +151,9 @@
![](./res/er-graph.png)
### 在后台管理模型
### 利用Django后台管理模型
Django框架有自带的后台管理系统来实现对模型的管理。虽然实际应用中,这个后台可能并不能满足我们的需求,但是在学习Django框架时,我们暂时可以利用Django自带的后台管理系统来管理我们的模型,同时也可以了解一个项目的后台管理系统到底需要哪些功能。
1. 创建超级管理员账号。
......@@ -201,23 +203,23 @@
4. 对模型进行CRUD操作。
可以在管理员平台对模型进行C(新增)R(查看)U(更新)D(删除)操作,如下图所示。
可以在管理员平台对模型进行C(新增)、R(查看)、U(更新)、D(删除)操作,如下图所示。
添加新的部门。
- 添加新的部门。
![](./res/admin-model-create.png)
查看所有部门。
- 查看所有部门。
![](./res/admin-model-read.png)
更新和删除部门。
- 更新和删除部门。
![](./res/admin-model-delete-and-update.png)
5. 注册模型管理类。
再次修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。
可能大家已经注意到了,刚才在后台查看部门信息的时候,显示的部门信息并不直观,为此我们再修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。
```Python
from django.contrib import admin
......@@ -267,17 +269,10 @@
# 此处省略上面的代码
mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='直接主管')
# 此处省略下面的代码
# 此处省略上面的代码
def __str__(self):
return self.name
# 此处省略下面的代码
```
修改代码后刷新查看Emp模型的页面,效果如下图所示。
......@@ -301,6 +296,7 @@ Type "help", "copyright", "credits" or "license" for more information.
```Shell
>>> from hrs.models import Dept, Emp
>>>
>>> dept = Dept(40, '研发2部', '深圳')
>>> dept.save()
```
......@@ -314,14 +310,14 @@ Type "help", "copyright", "credits" or "license" for more information.
#### 查询
查询所有对象。
1. 查询所有对象。
```Shell
>>> Dept.objects.all()
<QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>, <Dept: 研发3>]>
```
过滤数据。
2. 过滤数据。
```Shell
>>> Dept.objects.filter(name='研发3部') # 查询部门名称为“研发3部”的部门
......@@ -337,7 +333,7 @@ Type "help", "copyright", "credits" or "license" for more information.
<QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>]>
```
查询单个对象。
3. 查询单个对象。
```Shell
>>> Dept.objects.get(pk=10)
......@@ -353,7 +349,7 @@ Type "help", "copyright", "credits" or "license" for more information.
<Dept: 研发1>
```
排序数据。
4. 排序数据。
```Shell
>>> Dept.objects.order_by('no') # 查询所有部门按部门编号升序排列
......@@ -363,7 +359,7 @@ Type "help", "copyright", "credits" or "license" for more information.
<QuerySet [<Dept: 研发3>, <Dept: 运维1>, <Dept: 销售1>, <Dept: 研发1>]>
```
切片数据
5. 数据切片(分页查询)
```Shell
>>> Dept.objects.order_by('no')[0:2] # 按部门编号排序查询1~2部门
......@@ -373,7 +369,7 @@ Type "help", "copyright", "credits" or "license" for more information.
<QuerySet [<Dept: 运维1>, <Dept: 研发3>]>
```
高级查询。
6. 高级查询。
```Shell
>>> Emp.objects.filter(dept__no=10) # 根据部门编号查询该部门的员工
......
This diff is collapsed.
This diff is collapsed.
......@@ -12,13 +12,13 @@
2. 隐藏域(隐式表单域)。在提交表单的时候,可以通过在表单中设置隐藏域向服务器发送额外的数据。例如:`<input type="hidden" name="sessionid" value="123456">`
3. Cookie。Cookie是保存在浏览器临时文件中的数据,每次请求时,请求头中会携带本站点的cookie到服务器,那么只要将sessionid写入cookie,下次请求时服务器只要读取请求头中的cookie就能够获得这个sessionid,如下图所示:
3. 本地存储。现在的浏览器都支持多种本地存储方案,包括:cookie、localStorage、sessionStorage、IndexedDB等。在这些方案中,cookie是历史最为悠久也是被诟病得最多的一种方案,也是我们接下来首先为大家讲解的一种方案。简单的说,cookie是一种以键值对方式保存在浏览器临时文件中的数据,每次请求时,请求头中会携带本站点的cookie到服务器,那么只要将sessionid写入cookie,下次请求时服务器只要读取请求头中的cookie就能够获得这个sessionid,如下图所示。
![](./res/sessionid_from_cookie.png)
需要说明的是,在HTML5时代要想在浏览器中保存数据,除了使用cookie之外,还可以使用新的本地存储API,包括localStorage、sessionStorage、IndexedDB等,如下图所示。
在HTML5时代要,除了cookie,还可以使用新的本地存储API来保存数据,就是刚才提到的localStorage、sessionStorage、IndexedDB等技术,如下图所示。
![](./res/cookie_xstorage_indexeddb.png)
![](./res/cookie_xstorage_indexeddb.png)
### Django框架对session的支持
......@@ -57,7 +57,7 @@ def login(request: HttpRequest):
user = User.objects.filter(username=username, password=password).first()
if user:
# 登录成功后将用户编号和用户名保存在session中
request.session['no'] = user.no
request.session['userid'] = user.no
request.session['username'] = user.username
return redirect('/')
else:
......@@ -67,11 +67,11 @@ def login(request: HttpRequest):
return render(request, 'login.html', {'hint': hint})
```
上面的代码中,我们设定了登录成功后会在session中保存用户的编号(`no`)和用户名(`username`),页面会重定向到首页。接下来我们可以稍微对首页的代码进行调整,在页面的右上角显示出登录用户的用户名。我们将这段代码单独写成了一个名为header.html的HTML文件,首页中可以通过在`<body>`标签中添加`{% include 'header.html' %}`来包含这个页面,代码如下所示。
上面的代码中,我们设定了登录成功后会在session中保存用户的编号(`userid`)和用户名(`username`),页面会重定向到首页。接下来我们可以稍微对首页的代码进行调整,在页面的右上角显示出登录用户的用户名。我们将这段代码单独写成了一个名为header.html的HTML文件,首页中可以通过在`<body>`标签中添加`{% include 'header.html' %}`来包含这个页面,代码如下所示。
```HTML
<div class="user">
{% if request.session.no %}
{% if request.session.userid %}
<span>{{ request.session.username }}</span>
<a href="/vote/logout">注销</a>
{% else %}
......@@ -81,7 +81,7 @@ def login(request: HttpRequest):
</div>
```
如果用户没有登录,页面会显示登录和注册的超链接;而用户登录成功后,页面上会显示用户名和注销的链接,注销链接对应的视图函数如下所示。
如果用户没有登录,页面会显示登录和注册的超链接;而用户登录成功后,页面上会显示用户名和注销的链接,注销链接对应的视图函数如下所示,URL的映射与之前讲过的类似,不再赘述
```Python
def logout(request):
......
......@@ -9,7 +9,7 @@ def praise_or_criticize(request: HttpRequest):
try:
tno = int(request.GET.get('tno', '0'))
teacher = Teacher.objects.get(no=tno)
if request.path.startswith('/vote/praise'):
if request.path.startswith('/praise'):
teacher.good_count += 1
else:
teacher.bad_count += 1
......@@ -30,20 +30,15 @@ def praise_or_criticize(request: HttpRequest):
$('.comment > a').on('click', (evt) => {
evt.preventDefault()
let a = $(evt.target)
$.ajax({
url: a.attr('href'),
type: 'get',
dataType: 'json',
success: (json) => {
$.getJSON(a.attr('href'), (json) => {
if (json.code == 200) {
let span = a.next()
span.text(parseInt(span.text()) + 1)
} else if (json.code == 401) {
location.href = '/vote/login/?backurl=' + location.href
location.href = '/login/?backurl=' + location.href
} else {
alert(json.message)
}
}
})
})
})
......
## 日志和调试
在项目开发阶段,显示足够的调试信息以辅助开发人员调试代码还是非常必要的.
项目开发阶段,显示足够的调试信息以辅助开发人员调试代码还是非常必要的;项目上线以后,将系统运行时出现的警告、错误等信息记录下来以备相关人员了解系统运行状况并维护代码也是很有必要的。要做好这两件事件,我们需要为Django项目配置日志。
### 配置日志
Django的日志配置基本可以参照官方文档再结合项目实际需求来进行,这些内容基本上可以从官方文档上复制下来,然后进行局部的调整即可,下面给出一些参考配置。
```Python
LOGGING = {
'version': 1,
# 是否禁用已经存在的日志器
'disable_existing_loggers': False,
# 日志格式化器
'formatters': {
'simple': {
'format': '%(asctime)s %(module)s.%(funcName)s: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
'verbose': {
'format': '%(asctime)s %(levelname)s [%(process)d-%(threadName)s] '
'%(module)s.%(funcName)s line %(lineno)d: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
}
},
# 日志过滤器
'filters': {
# 只有在Django配置文件中DEBUG值为True时才起作用
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
# 日志处理器
'handlers': {
# 输出到控制台
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'filters': ['require_debug_true'],
'formatter': 'simple',
},
# 输出到文件(每周切割一次)
'file1': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'access.log',
'when': 'W0',
'backupCount': 12,
'formatter': 'simple',
'level': 'INFO',
},
# 输出到文件(每天切割一次)
'file2': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'error.log',
'when': 'D',
'backupCount': 31,
'formatter': 'verbose',
'level': 'WARNING',
},
},
# 日志器记录器
'loggers': {
'django': {
# 需要使用的日志处理器
'handlers': ['console', 'file1', 'file2'],
# 是否向上传播日志信息
'propagate': True,
# 日志级别(不一定是最终的日志级别)
'level': 'DEBUG',
},
}
}
```
大家可能已经注意到了,上面日志配置中的formatters是**日志格式化器**,它代表了如何格式化输出日志,其中格式占位符分别表示:
1. %(name)s - 记录器的名称
2. %(levelno)s - 数字形式的日志记录级别
3. %(levelname)s - 日志记录级别的文本名称
4. %(filename)s - 执行日志记录调用的源文件的文件名称
5. %(pathname)s - 执行日志记录调用的源文件的路径名称
6. %(funcName)s - 执行日志记录调用的函数名称
7. %(module)s - 执行日志记录调用的模块名称
8. %(lineno)s - 执行日志记录调用的行号
9. %(created)s - 执行日志记录的时间
10. %(asctime)s - 日期和时间
11. %(msecs)s - 毫秒部分
12. %(thread)d - 线程ID(整数)
13. %(threadName)s - 线程名称
14. %(process)d - 进程ID (整数)
日志配置中的handlers用来指定**日志处理器**,简单的说就是指定将日志输出到控制台还是文件又或者是网络上的服务器,可用的处理器包括:
1. logging.StreamHandler(stream=None) - 可以向类似与sys.stdout或者sys.stderr的任何文件对象输出信息
2. logging.FileHandler(filename, mode='a', encoding=None, delay=False) - 将日志消息写入文件
3. logging.handlers.DatagramHandler(host, port) - 使用UDP协议,将日志信息发送到指定主机和端口的网络主机上
4. logging.handlers.HTTPHandler(host, url) - 使用HTTP的GET或POST方法将日志消息上传到一台HTTP 服务器
5. logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False) - 将日志消息写入文件,如果文件的大小超出maxBytes指定的值,那么将重新生成一个文件来记录日志
6. logging.handlers.SocketHandler(host, port) - 使用TCP协议,将日志信息发送到指定主机和端口的网络主机上
7. logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0) - 将日志输出到指定的邮件地址
8. logging.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True) - 将日志输出到内存指定的缓冲区中
上面每个日志处理器都指定了一个名为“level”的属性,它代表了日志的级别,不同的日志级别反映出日志中记录信息的严重性。Python中定义了六个级别的日志,按照从低到高的顺序依次是:NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL。
最后配置的**日志记录器**是用来真正输出日志的,Django框架提供了如下所示的内置记录器:
1. django - 在Django层次结构中的所有消息记录器
2. django.request - 与请求处理相关的日志消息。5xx响应被视为错误消息;4xx响应被视为为警告消息
3. django.server - 与通过runserver调用的服务器所接收的请求相关的日志消息。5xx响应被视为错误消息;4xx响应被记录为警告消息;其他一切都被记录为INFO
4. django.template - 与模板渲染相关的日志消息
5. django.db.backends - 有与数据库交互产生的日志消息,如果希望显示ORM框架执行的SQL语句,就可以使用该日志记录器。
日志记录器中配置的日志级别有可能不是最终的日志级别,因为还要参考日志处理器中配置的日志级别,取二者中级别较高者作为最终的日志级别。
### 配置Django-Debug-Toolbar
Django-Debug-Toolbar是辅助Django项目开发的神器,只要配置了它,就可以很方便的查看到以下内容,这些信息对了解项目的运行状况以及优化Web应用性能都是至关重要的。
| 项目 | 说明 |
| ----------- | --------------------------------- |
| Versions | Django的版本 |
| Time | 显示视图耗费的时间 |
| Settings | 配置文件中设置的值 |
| Headers | HTTP请求头和响应头的信息 |
| Request | 和请求相关的各种变量及其信息 |
| StaticFiles | 静态文件加载情况 |
| Templates | 模板的相关信息 |
| Cache | 缓存的使用情况 |
| Signals | Django内置的信号信息 |
| Logging | 被记录的日志信息 |
| SQL | 向数据库发送的SQL语句及其执行时间 |
1. 安装Django-Debug-Toolbar。
```Shell
pip install django-debug-toolbar
```
2. 配置 - 修改settings.py。
```Python
INSTALLED_APPS = [
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
DEBUG_TOOLBAR_CONFIG = {
# 引入jQuery库
'JQUERY_URL': 'https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js',
# 工具栏是否折叠
'SHOW_COLLAPSED': True,
# 是否显示工具栏
'SHOW_TOOLBAR_CALLBACK': lambda x: True,
}
```
3. 配置 - 修改urls.py。
```Python
if settings.DEBUG:
import debug_toolbar
urlpatterns.insert(0, path('__debug__/', include(debug_toolbar.urls)))
```
4. 使用 - 在页面右侧可以看到一个调试工具栏,上面包括了如前所述的调试信息,包括:执行时间、项目设置、请求头、SQL、静态资源、模板、缓存、信号等,查看起来非常的方便。
\ No newline at end of file
......@@ -182,7 +182,7 @@ ipython最直观的优点:
resp = requests.get('http://api.tianapi.com/allnews/?key=请使用自己申请的Key&col=7&num=50')
```
> 说明:上面使用了天行数据提供的数据接口,需要的话可以自行去[天行数据](<https://www.tianapi.com/>)的网站注册开通。
> 说明:上面使用了天行数据提供的数据接口,需要的话可以自行去[天行数据](<https://www.tianapi.com/>)的网站注册开通,调用接口的时候要填写注册成功后系统分配给你的key
3. 使用反序列化将JSON字符串解析为字典并获取新闻列表。
......@@ -225,5 +225,5 @@ ipython最直观的优点:
)
```
> 说明:上面的代码使用了[螺丝帽](<https://luosimao.com/>)的短信网关,利用短信网关发送短信是需要支付费用的,但是一般的平台都会提供免费测试的短信,但是发送短信必须遵守平台的规则,违规的短信是无法发送的。上面发短信时使用的短信模板(“发现一条您可能感兴趣的新闻 - ###,详情点击https://news.china.com/### 查看。”)和短信签名(“【Python小课】”)需要登录螺丝帽管理平台进行配置,如果不清楚如何配置,这些平台都是有客服的哟
> 说明:上面的代码使用了[螺丝帽](<https://luosimao.com/>)提供的短信网关服务,利用短信网关发送短信是需要支付费用的,但是一般的平台都会提供若干条免费的测试短信。发送短信必须遵守平台的规则,违规的短信是无法发送的。上面发短信时使用的短信模板(“发现一条您可能感兴趣的新闻 - ###,详情点击https://news.china.com/### 查看。”)和短信签名(“【Python小课】”)需要登录螺丝帽管理平台进行配置,如果不清楚如何配置,可以联系平台的客服人员进行咨询
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment