使用python和Django完成博客数据库的迁移方法


Posted in Python onJanuary 05, 2018

上一讲完成了基本博客的配置和项目工程的生成。这次开始将博客一些基本的操作主要是数据库方面学习。

1.设计博客数据库表结构

博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个地方就是数据库。我们把写好的文章永久地保存在数据库里,当用户访问我们的博客时,Django 就去数据库里把这些数据取出来展现给用户。

博客的文章应该含有标题、正文、作者、发表时间等数据。一个更加现代化的博客文章还希望它有分类、标签、评论等。为了更好地存储这些数据,我们需要合理地组织数据库的表结构。

我们的博客初级版本主要包含博客文章,文章会有分类以及标签。一篇文章只能有一个分类,但可以打上很多标签。

文章 id 标题 正文 发表时间 分类 标签
1 title 1 text 1 2016-12-23 Django Django 学习
2 title 2 text 2 2016-12-24 Django Django 学习
3 title 3 text 3 2016-12-26 Python Python 学习

其中文章 ID 是一个数字,唯一对应着一篇文章。当然还可以有更多的列以存储更多相关数据,这只是一个最基本的示例。

数据库表设计成这样其实已经可以了,但是稍微分析一下我们就会发现一个问题,这 3 篇文章的分类和标签都是相同的,这会产生很多重复数据,当数据量很大时就浪费了存储空间。

不同的文章可能它们对应的分类或者标签是相同的,所以我们把分类和标签提取出来,做成单独的数据库表,再把文章和分类、标签关联起来。下面分别是分类和标签的数据库表:

分类 id 分类名

1 Django
2 Python

标签 id 标签名

1 Django 学习
2 Python 学习

接下来就是编写我们的数据库模型:

以上是自然语言描述的表格,数据库也和编程语言一样,有它自己的一套规定的语法来生成上述的表结构,这样我们才能把数据存进去。一般来说这时候我们应该先去学习数据库创建表格的语法,再回来写我们的 Django 博客代码了。但是 Django 告诉我们不用这么麻烦,它已经帮我们做了一些事情。Django 把那一套数据库的语法转换成了 Python 的语法形式,我们只要写 Python 代码就可以了,Django 会把 Python 代码翻译成对应的数据库操作语言。用更加专业一点的说法,就是 Django 为我们提供了一套 ORM(Object Relational Mapping)系统。

例如我们的分类数据库表,Django 只要求我们这样写:

blog/models.py
from django.db import models
class Category(models.Model):
 name = models.CharField(max_length=100)

给出完整的代码:

from django.db import models
from django.contrib.auth.models import User
from django.utils.six import python_2_unicode_compatible

# python_2_unicode_compatible 装饰器用于兼容 Python2
@python_2_unicode_compatible
class Category(models.Model):
 """
 Django 要求模型必须继承 models.Model 类。
 Category 只需要一个简单的分类名 name 就可以了。
 CharField 指定了分类名 name 的数据类型,CharField 是字符型,
 CharField 的 max_length 参数指定其最大长度,超过这个长度的分类名就不能被存入数据库。
 当然 Django 还为我们提供了多种其它的数据类型,如日期时间类型 DateTimeField、整数类型 IntegerField 等等。
 Django 内置的全部类型可查看文档:
 https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types
 """
 name = models.CharField(max_length=100)
 def __str__(self):
 return self.name

@python_2_unicode_compatible
class Tag(models.Model):
 """
 标签 Tag 也比较简单,和 Category 一样。
 再次强调一定要继承 models.Model 类!
 """
 name = models.CharField(max_length=100)
 def __str__(self):
 return self.name

@python_2_unicode_compatible
class Post(models.Model):
 """
 文章的数据库表稍微复杂一点,主要是涉及的字段更多。
 """
 # 文章标题
 title = models.CharField(max_length=70)
 # 文章正文,我们使用了 TextField。
 # 存储比较短的字符串可以使用 CharField,但对于文章的正文来说可能会是一大段文本,因此使用 TextField 来存储大段文本。
 body = models.TextField()
 # 这两个列分别表示文章的创建时间和最后一次修改时间,存储时间的字段用 DateTimeField 类型。
 created_time = models.DateTimeField()
 modified_time = models.DateTimeField()
 # 文章摘要,可以没有文章摘要,但默认情况下 CharField 要求我们必须存入数据,否则就会报错。
 # 指定 CharField 的 blank=True 参数值后就可以允许空值了。
 excerpt = models.CharField(max_length=200, blank=True)
 # 这是分类与标签,分类与标签的模型我们已经定义在上面。
 # 我们在这里把文章对应的数据库表和分类、标签对应的数据库表关联了起来,但是关联形式稍微有点不同。
 # 我们规定一篇文章只能对应一个分类,但是一个分类下可以有多篇文章,所以我们使用的是 ForeignKey,即一对多的关联关系。
 # 而对于标签来说,一篇文章可以有多个标签,同一个标签下也可能有多篇文章,所以我们使用 ManyToManyField,表明这是多对多的关联关系。
 # 同时我们规定文章可以没有标签,因此为标签 tags 指定了 blank=True。
 # 如果你对 ForeignKey、ManyToManyField 不了解,请看教程中的解释,亦可参考官方文档:
 # https://docs.djangoproject.com/en/1.10/topics/db/models/#relationships
 category = models.ForeignKey(Category)
 tags = models.ManyToManyField(Tag, blank=True)
 # 文章作者,这里 User 是从 django.contrib.auth.models 导入的。
 # django.contrib.auth 是 Django 内置的应用,专门用于处理网站用户的注册、登录等流程,User 是 Django 为我们已经写好的用户模型。
 # 这里我们通过 ForeignKey 把文章和 User 关联了起来。
 # 因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关系,和 Category 类似。
 author = models.ForeignKey(User)
 def __str__(self):
 return self.title

Category 就是一个标准的 Python 类,它继承了 models.Model 类,类名为 Category 。Category 类有一个属性 name,它是 models.CharField 的一个实例。

这样,Django 就可以把这个类翻译成数据库的操作语言,在数据库里创建一个名为 category 的表格,这个表格的一个列名为 name。还有一个列 id,Django 则会自动创建。可以看出从 Python 代码翻译成数据库语言时其规则就是一个 Python 类对应一个数据库表格,类名即表名,类的属性对应着表格的列,属性名即列名。

我们需要 3 个表格:文章(Post)、分类(Category)以及标签(Tag),下面就来分别编写它们对应的 Python 类。模型的代码通常写在相关应用的 models.py 文件里。

二:迁移数据库

为了让 Django 完成翻译,创建好这些数据库表,我们再一次请出我的工程管理助手 manage.py。激活虚拟环境,切换到 manage.py 文件所在的目录下,分别运行 python manage.py makemigrations 和 python manage.py migrate 命令:

使用python和Django完成博客数据库的迁移方法

注意:如果代码中含有中文注释,且你使用的是 Python 2 开发环境的话,会得到一个编码错误。因此请在含有中文注释的文件最开始处加入编码声明:# coding: utf-8。

当我们执行了 python manage.py makemigrations 后,Django 在 blog 应用的 migrations\ 目录下生成了一个 0001_initial.py 文件,这个文件是 Django 用来记录我们对模型做了哪些修改的文件。目前来说,我们在 models.py 文件里创建了 3 个模型类,Django 把这些变化记录在了 0001_initial.py 里。

不过此时还只是告诉了 Django 我们做了哪些改变,为了让 Django 真正地为我们创建数据库表,接下来又执行了 python manage.py migrate 命令。Django 通过检测应用中 migrations\ 目录下的文件,得知我们对数据库做了哪些操作,然后它把这些操作翻译成数据库操作语言,从而把这些操作作用于真正的数据库。

你可以看到命令的输出除了 Applying blog.0001_initial... OK 外,Django 还对其它文件做了操作。这是因为除了我们自己建立的 blog 应用外,Django 自身还内置了很多应用,这些应用本身也是需要存储数据的。可以在 settings.py 的 INSTALLED_APP 设置里看到这些应用,当然我们目前不必关心这些。

blogproject/settings.py
INSTALLED_APPS = [
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'blog',
]

对于了解数据库语言的人,你可以运行下面的命令看看 Django 究竟为我们做了什么:

python manage.py sqlmigrate blog 0001

你将看到输出了经 Django 翻译后的数据库表创建语句,这有助于你理解 Django ORM 的工作机制。

使用python和Django完成博客数据库的迁移方法

三:选择数据库版本

没有安装任何的数据库软件,Django 就帮我们迁移了数据库。这是因为我们使用了 Python 内置的 SQLite3 数据库。

SQLite3 是一个十分轻巧的数据库,它仅有一个文件。你可以看一到项目根目录下多出了一个 db.sqlite3 的文件,这就是 SQLite3 数据库文件,Django 博客的数据都会保存在这个数据库文件里。

Django 在 settings.py 里为我们做了一些默认的数据库配置:

blogproject/settings.py
## 其它配置选项...
DATABASES = {
 'default': {
 'ENGINE': 'django.db.backends.sqlite3',
 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
 }
}
## 其它配置选项...

可以看到默认的数据库引擎就是使用的 SQLite3。

当然一些人倾向于使用 MySQL 等大型数据库,至于 Django 如何配置 MySQL 这里就不赘述了,你可以自行使用搜索引擎或者查阅 Django 的官方文档解决。对于一个小型博客而言,SQLite3 数据库足以胜任。

四:用Django方式操作数据库

数据库最主要的操作就是往里面存入数据、从中取出数据、修改已保存的数据和删除不再需要的数据。和创建数据库表一样,Django 为这些操作提供了一整套方法,从而把我们从数据库语言中解放出来。我们不用学习如何利用数据库语言去完成这些操作,只要简单地调用几个 Python 函数就可以满足我们的需求。

存数据

先在命令行中来探索一下这些函数,感受一下如何用 Django 的方式来操作数据库。在 manage.py 所在目录下运行 python manage.py shell 命令:

创建一个分类和一个标签:

使用python和Django完成博客数据库的迁移方法

首先导入 3 个之前写好的模型类,然后实例化了一个 Category 类和一个 Tag 类,为他们的属性 name 赋了值。为了让 Django 把这些数据保存进数据库,调用实例的 save 方法即可。

再创建一篇文章试试,但创建文章之前,我们需要先创建一个 User,用于指定文章的作者。创建 User 的命令 Django 已经帮我们写好了,依然是通过 manage.py 来运行。首先按住 Ctrl + c 退出命令交互栏(一次退不出就连续多按几次),运行 python manage.py createsuperuser 命令并根据提示创建用户:

使用python和Django完成博客数据库的迁移方法

(密码过于简单会通不过)

再次运行 python manage.py shell 进入 Python 命令交互栏,开始创建文章:

使用python和Django完成博客数据库的迁移方法

由于我们重启了 shell,因此需要重新导入了 CategoryTagPost 以及 User。我们还导入了一个 Django 提供的辅助模块 timezone,这是因为我们需要调用它的 now() 方法为 created_timemodified_time 指定时间,容易理解 now 方法返回当前时间。然后我们根据用户名和分类名,通过 get 方法取出了存在数据库中的 UserCategory(取数据的方法将在下面介绍)。接着我们为文章指定了 titlebodycreated_timemodified_time值,并把它和前面创建的 Category 以及 User 关联了起来。允许为空 excerpttags 我们就没有为它们指定值了。

注意:我们这里使用 get 方法根据 Categoryname 属性的值获取分类的一条记录。Category.objects.get(name='category test') 的含义是从数据库中取出 name 的值为 category test 的分类记录。确保数据库中只有一条值为 category test 的记录,否则 get 方法将返回一个 MultipleObjectsReturned 异常。如果你不小心已经存了多条记录,请删掉多余的记录。如何删除数据请看下文。

取数据

数据已经存入数据库了,现在要把它们取出来看看:

使用python和Django完成博客数据库的迁移方法

objects 是我们的模型管理器,它为我们提供一系列从数据库中取数据方法,这里我们使用了 all 方法,表示我们要把对应的数据全部取出来。可以看到 all 方法都返回了数据,这些数据应该是我们之前存进去的,但是显示的字符串有点奇怪,无法看出究竟是不是我们之前存入的数据。为了让显示出来的数据更加人性化一点,我们为 3 个模型分别增加一个 __str__ 方法:

from django.db import models
from django.contrib.auth.models import User
from django.utils.six import python_2_unicode_compatible

# python_2_unicode_compatible 装饰器用于兼容 Python2
@python_2_unicode_compatible
class Category(models.Model):
 """
 Django 要求模型必须继承 models.Model 类。
 Category 只需要一个简单的分类名 name 就可以了。
 CharField 指定了分类名 name 的数据类型,CharField 是字符型,
 CharField 的 max_length 参数指定其最大长度,超过这个长度的分类名就不能被存入数据库。
 当然 Django 还为我们提供了多种其它的数据类型,如日期时间类型 DateTimeField、整数类型 IntegerField 等等。
 Django 内置的全部类型可查看文档:
 https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types
 """
 name = models.CharField(max_length=100)
 def __str__(self):
 return self.name

@python_2_unicode_compatible
class Tag(models.Model):
 """
 标签 Tag 也比较简单,和 Category 一样。
 再次强调一定要继承 models.Model 类!
 """
 name = models.CharField(max_length=100)
 def __str__(self):
 return self.name

@python_2_unicode_compatible
class Post(models.Model):
 """
 文章的数据库表稍微复杂一点,主要是涉及的字段更多。
 """
 # 文章标题
 title = models.CharField(max_length=70)
 # 文章正文,我们使用了 TextField。
 # 存储比较短的字符串可以使用 CharField,但对于文章的正文来说可能会是一大段文本,因此使用 TextField 来存储大段文本。
 body = models.TextField()
 # 这两个列分别表示文章的创建时间和最后一次修改时间,存储时间的字段用 DateTimeField 类型。
 created_time = models.DateTimeField()
 modified_time = models.DateTimeField()
 # 文章摘要,可以没有文章摘要,但默认情况下 CharField 要求我们必须存入数据,否则就会报错。
 # 指定 CharField 的 blank=True 参数值后就可以允许空值了。
 excerpt = models.CharField(max_length=200, blank=True)
 # 这是分类与标签,分类与标签的模型我们已经定义在上面。
 # 我们在这里把文章对应的数据库表和分类、标签对应的数据库表关联了起来,但是关联形式稍微有点不同。
 # 我们规定一篇文章只能对应一个分类,但是一个分类下可以有多篇文章,所以我们使用的是 ForeignKey,即一对多的关联关系。
 # 而对于标签来说,一篇文章可以有多个标签,同一个标签下也可能有多篇文章,所以我们使用 ManyToManyField,表明这是多对多的关联关系。
 # 同时我们规定文章可以没有标签,因此为标签 tags 指定了 blank=True。
 # 如果你对 ForeignKey、ManyToManyField 不了解,请看教程中的解释,亦可参考官方文档:
 # https://docs.djangoproject.com/en/1.10/topics/db/models/#relationships
 category = models.ForeignKey(Category)
 tags = models.ManyToManyField(Tag, blank=True)
 # 文章作者,这里 User 是从 django.contrib.auth.models 导入的。
 # django.contrib.auth 是 Django 内置的应用,专门用于处理网站用户的注册、登录等流程,User 是 Django 为我们已经写好的用户模型。
 # 这里我们通过 ForeignKey 把文章和 User 关联了起来。
 # 因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关系,和 Category 类似。
 author = models.ForeignKey(User)
 def __str__(self):
 return self.title

定义好 __str__ 方法后,解释器显示的内容将会是 __str__ 方法返回的内容。这里 Category 返回分类名 nameTag 返回标签名,而 Post 返回它的 title

python_2_unicode_compatible 装饰器用于兼容 Python2。如果你使用的 Python3 开发环境,去掉这个装饰器不会有任何影响。如果你使用的 Python2 开发环境,而又不想使用这个装饰器,则将 __str__ 方法改为 __unicode__ 方法即可。

先按 Ctrl + c 退出 Shell,再重新运行 python manage.py shell 进入 Shell。

使用python和Django完成博客数据库的迁移方法

改数据:

使用python和Django完成博客数据库的迁移方法

首先通过 get 方法根据分类名 name 获取值为 category test 到分类,修改它的 name 属性为新的值 category test new,然后调用 save 方法把修改保存到数据库,之后可以看到数据库返回的数据已经是修改后的值了。TagPost的修改也一样。

删除数据:

使用python和Django完成博客数据库的迁移方法

先根据标题 title 的值从数据库中取出 Post,保存在变量 p 中,然后调用它的delete 方法,最后看到 Post.objects.all() 返回了一个空的 QuerySet(类似于一个列表),表明数据库中已经没有 Post,Post 已经被删除了。

这就是 Django 对数据库增、删、改、查的操作。除了上述演示的方法外,Django 还为我们提供了大量其它的方法,这些方法有一部分会在教程中使用,用到时我会讲解它们的用法。但以后你开发自己的项目时,你就需要通过阅读 Django 的官方文档 来了解有哪些方法可用以及如何使用它们。

以上这篇使用python和Django完成博客数据库的迁移方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中去空格函数的用法
Aug 21 Python
使用SAE部署Python运行环境的教程
May 05 Python
两个命令把 Vim 打造成 Python IDE的方法
Mar 20 Python
Python cookbook(数据结构与算法)将多个映射合并为单个映射的方法
Apr 19 Python
对python3 urllib包与http包的使用详解
May 10 Python
Python模拟浏览器上传文件脚本的方法(Multipart/form-data格式)
Oct 22 Python
Python提取转移文件夹内所有.jpg文件并查看每一帧的方法
Jun 27 Python
python读写配置文件操作示例
Jul 03 Python
Python全栈之列表数据类型详解
Oct 01 Python
Python如何存储数据到json文件
Mar 09 Python
高考要来啦!用Python爬取历年高考数据并分析
Jun 03 Python
如何在python中实现ECDSA你知道吗
Nov 23 Python
Python3多线程爬虫实例讲解代码
Jan 05 #Python
python编写微信远程控制电脑的程序
Jan 05 #Python
使用python爬虫实现网络股票信息爬取的demo
Jan 05 #Python
简单实现python收发邮件功能
Jan 05 #Python
5款非常棒的Python工具
Jan 05 #Python
Python基于列表模拟堆栈和队列功能示例
Jan 05 #Python
Django 2.0版本的新特性抢先看!
Jan 05 #Python
You might like
PHP curl伪造IP地址和header信息代码实例
2015/04/27 PHP
PHP整合PayPal支付
2015/06/11 PHP
php判断当前操作系统类型
2015/10/28 PHP
php实现计算百度地图坐标之间距离的方法
2016/05/05 PHP
php实现的http请求封装示例
2016/11/08 PHP
php和C#的yield迭代器实现方法对比分析
2019/07/17 PHP
Javascript 生成指定范围数值随机数
2009/01/09 Javascript
基于ajax实现文件上传并显示进度条
2015/08/03 Javascript
javascript针对不确定函数的执行方法
2015/12/16 Javascript
基于JavaScript实现智能右键菜单
2016/03/02 Javascript
第一次动手实现bootstrap table分页效果
2016/09/22 Javascript
开源免费天气预报接口API及全国所有地区代码(国家气象局提供)
2016/12/26 Javascript
JS常用的几种数组遍历方式以及性能分析对比实例详解
2018/04/11 Javascript
解决bootstrap-select 动态加载数据不显示的问题
2018/08/10 Javascript
JQuery扩展对象方法操作示例
2018/08/21 jQuery
使用jquery模拟a标签的click事件无法实现跳转的解决
2018/12/04 jQuery
JavaScript代码调试方法实例小结
2019/01/05 Javascript
JavaScript交换变量常用4种方法解析
2020/09/02 Javascript
解决VUE项目使用Element-ui 下拉组件的验证失效问题
2020/11/07 Javascript
编写同时兼容Python2.x与Python3.x版本的代码的几个示例
2015/03/30 Python
python机器学习理论与实战(四)逻辑回归
2018/01/19 Python
python 自定义异常和异常捕捉的方法
2018/10/18 Python
Python读取系统文件夹内所有文件并统计数量的方法
2018/10/23 Python
如何用python写一个简单的词法分析器
2018/12/18 Python
Python使用post及get方式提交数据的实例
2019/01/24 Python
pandas将多个dataframe以多个sheet的形式保存到一个excel文件中
2019/10/10 Python
Python ATM功能实现代码实例
2020/03/19 Python
在python下实现word2vec词向量训练与加载实例
2020/06/09 Python
利用HTML5画出一个坦克的形状具体实现代码
2013/06/20 HTML / CSS
应届生妇产科护士求职信
2013/10/27 职场文书
工商管理专业应届生求职信
2013/11/04 职场文书
家长对孩子的感言
2014/03/10 职场文书
篮球比赛口号
2014/06/10 职场文书
坎儿井导游词
2015/02/09 职场文书
2015暑期爱心支教策划书
2015/07/14 职场文书
MySQL中的布尔值,怎么存储false或true
2021/06/04 MySQL