Django 迁移、操作数据库的方法


Posted in Python onAugust 02, 2019

文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库

我们已经编写了博客数据库模型的代码,但那还只是 Python 代码而已,django 还没有把它翻译成数据库语言,因此实际上这些数据库表还没有真正的在数据库中创建。

迁移数据库

为了让 django 完成翻译,创建好这些数据库表,我们再一次请出我的工程管理助手 manage.py。切换到 manage.py 文件所在的目录(项目根目录)下,分别运行 pipenv run python manage.py makemigrations pipenv run python manage.py migrate 命令:

> pipenv run python manage.py makemigrations
Migrations for 'blog':
 blog\migrations\0001_initial.py
  - Create model Category
  - Create model Tag
  - Create model Post
  
> pipenv run python manage.py migrate
Operations to perform:
 Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
 Applying contenttypes.0001_initial... OK
 Applying auth.0001_initial... OK
 Applying admin.0001_initial... OK
 Applying admin.0002_logentry_remove_auto_add... OK
 Applying admin.0003_logentry_add_action_flag_choices... OK
 Applying contenttypes.0002_remove_content_type_name... OK
 Applying auth.0002_alter_permission_name_max_length... OK
 Applying auth.0003_alter_user_email_max_length... OK
 Applying auth.0004_alter_user_username_opts... OK
 Applying auth.0005_alter_user_last_login_null... OK
 Applying auth.0006_require_contenttypes_0002... OK
 Applying auth.0007_alter_validators_add_error_messages... OK
 Applying auth.0008_alter_user_username_max_length... OK
 Applying auth.0009_alter_user_last_name_max_length... OK
 Applying auth.0010_alter_group_name_max_length... OK
 Applying auth.0011_update_proxy_permissions... OK
 Applying blog.0001_initial... OK
 Applying sessions.0001_initial... OK

当我们执行了 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 究竟为我们做了什么:

> pipenv run python manage.py sqlmigrate blog 0001

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

选择数据库版本

我们没有安装任何的数据库软件,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 的方式操作数据库

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

存数据

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

> pipenv run python manage.py shell
Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

这打开了一个交互式命令行。

首先我们来创建一个分类和一个标签:

>>> from blog.models import Category, Tag, Post
>>> c = Category(name='category test')
>>> c.save()
>>> t = Tag(name='tag test')
>>> t.save()

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

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

> pipenv run python manage.py createsuperuser
Username (leave blank to use 'zmrenwu@163.com'): myuser
Email address: a@aa.com
Password:
Password (again):
Superuser created successfully.

运行 python manage.py createsuperuser 开始创建用户,之后会提示你输入用户名、邮箱、密码和确认密码,按照提示输入即可。 注意一点的是密码输入过程中不会有任何字符显示,不要误以为你的键盘出问题了,正常输入即可。 最后出现 Superuser created successfully. 说明用户创建成功了。

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

>>> from blog.models import Category, Tag, Post
>>> from django.utils import timezone
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='myuser')
>>> c = Category.objects.get(name='category test')
>>> p = Post(title='title test', body='body test', created_time=timezone.now(), modified_time=timezone.now(), category=c, author=user)
>>> p.save()

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

注意:

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

取数据

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

>>> Category.objects.all()
<QuerySet [<Category: Category object>]>
>>> Tag.objects.all()
<QuerySet [<Tag: Tag object>]>
>>> Post.objects.all()
<QuerySet [<Post: Post object>]>
>>>

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

blog/models.py
class Category(models.Model):
  ...
  def __str__(self):
    return self.name
class Tag(models.Model):
  ...
  def __str__(self):
    return self.name
class Post(models.Model):
  ...
  def __str__(self):
    return self.title

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

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

>>> from blog.models import Category, Tag, Post
>>> Category.objects.all()
<QuerySet [<Category: category test>]>
>>> Tag.objects.all()
<QuerySet [<Tag: tag test>]>
>>> Post.objects.all()
<QuerySet [<Post: title test>]>
>>> Post.objects.get(title='title test')
<Post: title test>

可以看到返回的是我们之前存入的数据。

此外我们在创建文章时提到了通过 get 方法来获取数据,这里 all 方法和 get 方法的区别是: all 方法返回全部数据,是一个类似于列表的数据结构(QuerySet);而 get 返回一条记录数据,如有多条记录或者没有记录, get 方法均会抛出相应异常。

改数据

尝试修改数据:

>>> c = Category.objects.get(name='category test')
>>> c.name = 'category test new'
>>> c.save()
>>> Category.objects.all()
<QuerySet [<Category: test category new>]>

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

删数据

删除掉数据:

>>> p = Post.objects.get(title='title test')
>>> p
<Post: title test>
>>> p.delete()
(1, {'blog.Post_tags': 0, 'blog.Post': 1})
>>> Post.objects.all()
<QuerySet []>

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

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

总结

以上所述是小编给大家介绍的Django 迁移、操作数据库的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
Python中用Ctrl+C终止多线程程序的问题解决
Mar 30 Python
有关wxpython pyqt内存占用问题分析
Jun 09 Python
Python中返回字典键的值的values()方法使用
May 22 Python
浅谈python 四种数值类型(int,long,float,complex)
Jun 08 Python
Python利用Beautiful Soup模块搜索内容详解
Mar 29 Python
Django数据库操作的实例(增删改查)
Sep 04 Python
python语言线程标准库threading.local解读总结
Nov 10 Python
ansible动态Inventory主机清单配置遇到的坑
Jan 19 Python
python修改linux中文件(文件夹)的权限属性操作
Mar 05 Python
Anaconda+VSCode配置tensorflow开发环境的教程详解
Mar 30 Python
Python打印特殊符号及对应编码解析
May 07 Python
keras自定义损失函数并且模型加载的写法介绍
Jun 15 Python
Django用户认证系统 组与权限解析
Aug 02 #Python
python3中eval函数用法使用简介
Aug 02 #Python
Django用户认证系统 Web请求中的认证解析
Aug 02 #Python
Django用户认证系统 User对象解析
Aug 02 #Python
浅谈python3中input输入的使用
Aug 02 #Python
Pycharm连接远程服务器并实现远程调试的实现
Aug 02 #Python
在PyCharm的 Terminal(终端)切换Python版本的方法
Aug 02 #Python
You might like
日本因肺炎疫情影响,这几部动漫推延播放!
2020/03/03 日漫
PHP读写文件的方法(生成HTML)
2006/11/27 PHP
用ADODB来让PHP操作ACCESS数据库的方法
2006/12/31 PHP
php下pdo的mysql事务处理用法实例
2014/12/27 PHP
浅谈PHP的数据库接口和技术
2016/12/09 PHP
ThinkPHP 模板substr的截取字符串函数详解
2017/01/09 PHP
js 判断脚本加载完毕的代码
2011/07/13 Javascript
JQuery报错Uncaught TypeError: Illegal invocation的处理方法
2015/03/13 Javascript
从重置input file标签中看jQuery的 .val() 和 .attr(“value”) 区别
2016/06/12 Javascript
AngularJS 路由和模板实例及路由地址简化方法(必看)
2016/06/24 Javascript
jquery广告无缝轮播实例
2017/01/05 Javascript
vue中如何引入jQuery和Bootstrap
2017/04/10 jQuery
jQuery扩展_动力节点Java学院整理
2017/07/05 jQuery
微信小程序tabBar用法实例详解
2017/12/04 Javascript
Vue-router 切换组件页面时进入进出动画方法
2018/09/01 Javascript
如何使用VuePress搭建一个类型element ui文档
2019/02/14 Javascript
vue中实现Monaco Editor自定义提示功能
2019/07/05 Javascript
解决layer.open弹出框不能获取input框的值为空的问题
2019/09/10 Javascript
如何在微信小程序中使用骨架屏的步骤
2020/06/12 Javascript
简单的Python2.7编程初学经验总结
2015/04/01 Python
python利用matplotlib库绘制饼图的方法示例
2016/12/18 Python
python导出hive数据表的schema实例代码
2018/01/22 Python
基于Python pip用国内镜像下载的方法
2018/06/12 Python
Django处理多用户类型的方法介绍
2019/05/18 Python
pyqt5 实现多窗口跳转的方法
2019/06/19 Python
python全栈知识点总结
2019/07/01 Python
python创建属于自己的单词词库 便于背单词
2019/07/30 Python
Django+zTree构建组织架构树的方法
2019/08/21 Python
python字符串格式化方式解析
2019/10/19 Python
python的launcher用法知识点总结
2020/08/07 Python
利用canvas实现图片下载功能来实现浏览器兼容问题
2019/05/31 HTML / CSS
美术专业个人自我评价
2014/01/18 职场文书
中班上学期幼儿评语
2014/04/30 职场文书
祖国在我心中演讲稿200字
2014/08/28 职场文书
python如何获取网络数据
2021/04/11 Python
关于MySQL中的 like操作符详情
2021/11/17 MySQL