PythonDjango,尤其是Django,是建立更有效的应用程序的必要条件,用更少的代码,并连接到一个高度可扩展的数据库。我在这里要和大家讨论的是,如何使用SQL和Python ,减少构建和支持现代应用程序时的日常摩擦,以抽象出复杂性,使我们的工作和生活更轻松一些。
在不深入了解的情况下,我们可以假设:
- SQL是为SQL数据库优化的
- Python 没有针对SQL数据库进行优化
这个练习直接支持《理解数据库》电子书和我新的Linode LIVE!教育系列,使用Python 和Pythonic工具来执行原始SQL的命令,而不需要实际编写SQL。我使用的是Django的数据建模,但其语法与Python 中的SQLAlchemy包非常相似。
让我们开始吧!
这里有一个Django数据模型的例子:
class BlogArticle(models.Model):
user = models.ForeignKey(User, default=1, on_delete=models.SET_DEFAULT)
title = models.CharField(max_length=120)
slug = models.SlugField(blank=True, null=True)
content = models.TextField(blank=True, null=True)
publish_timestamp = models.DateTimeField(
auto_now_add=False,
auto_now=False,
blank=True,
null=True,
)
让我们假设这个模型生活在一个名为 Articles
(Django应用程序本质上是构成Django项目整体的组件)。
所以现在我们有两个名字可以使用:
Articles
(应用名称)BlogArticle
(模型名称)
结合起来,这些就转化为SQL表的名称:
articles_blog_article
Django为我们施展了这个魔法。
如果我们使用的是MySQL外壳,我们会看到:
mysql> SHOW TABLES;
+------------------------------+
| Tables_in_cfe_django_blog_db |
+------------------------------+
| articles_blog_article |
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+------------------------------+
11 rows in set (0.01 sec)
现在让我们看看我们的Django模型中的列:
mysql> DESCRIBE articles_blog_article;
+------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| title | varchar(120) | NO | | NULL | |
| slug | varchar(50) | YES | MUL | NULL | |
| content | longtext | YES | | NULL | |
| publish_timestamp | datetime(6) | YES | | NULL | |
| user_id | int | NO | MUL | NULL | |
+------------------------+--------------+------+-----+---------+----------------+
12 rows in set (0.00 sec)
除了写一个基本的配置之外,Django在这个MySQL数据库中为我们做了所有的SQL工作。在这一点上,Django并没有做任何非常令人印象深刻的事情。事实上,这个练习的大部分内容并不是为了突出Django或Python 作为SQL的替代品,而是为了提醒你抽象工作的事实。
Python 的崛起和摩擦的代价
让我们来谈谈阻力最小的路径,以及为什么我认为Python 是利用SQL的最佳方式之一。
编写、阅读、运行和运送Python ,都非常容易。将 "Python" 改为几乎任何其他的编程范式,几乎不可能做出同样的声明。JavaScript也是一个竞争者,但它也一直被混淆为Java。我知道这些都是概括性的,并不总是真实的,但这是开发你的应用程序时常见的问题。
我相信这些概括往往是真实的,因为Python's English-like syntax for doing things.
让我们比较一下SQL语句和Python 、Django语句:
- SQL:
SELECT * from articles_blog_article;
- Python 和Django:
items = BlogArticle.objects.all()
两条语句产生的数据完全相同。然而,Python 语句返回一个Python 对象的列表(items
),几乎任何有经验的Python 开发人员都可以使用。原始的SQL结果在被用于Python 应用程序之前需要进行转换。
字段描述
如果我们仔细看一下这个SQL字段描述:
+------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+----------------+
| title | varchar(120) | NO | | NULL | |
+------------------------+--------------+------+-----+---------+----------------+
与这个Django字段描述相比:
title = models.CharField(max_length=120)
- 哪一个的摩擦力更大?
- 哪一个更容易理解发生了什么事?
其中提供的信息刚好够用?
如果你不是一个编码员,看到 varchar(120)
你会怎么想?我敢肯定,你至少可以猜到什么 max_length=120 means
.最酷的是,它们的意思完全一样:把这个字段限制在120个字符以内。
向数据库添加数据
有了Django:
BlogArticle.objects.create(
title="Hello World",
content="Coming Soon",
slug="hello-world",
publish_timestamp=None,
)
有了SQL:
INSERT INTO `articles_blog_article` (`user_id`, `title`, `slug`, `content`, `publish_timestamp`)
VALUES (1, 'Hello World', 'hello-world', 'Coming Soon', NULL);
说到简单和清晰,我认为上面的赢家肯定是Django和Python 。 title = "Hello World"
是比在SQL中弄清楚同等的列(字段)值是怎么回事更容易。不要搞错了,当你知道自己在做什么的时候,在SQL中的这种写法是非常有效的。
用Django添加多行
:
items = [
BlogArticle(title='Hello Again 0', slug='hello-again-0', content="Coming Soon"),
BlogArticle(title='Hello Again 1', slug='hello-again-1', content="Coming Soon"),
BlogArticle(title='Hello Again 2', slug='hello-again-2', content="Coming Soon"),
BlogArticle(title='Hello Again 3', slug='hello-again-3', content="Coming Soon"),
BlogArticle(title='Hello Again 4', slug='hello-again-4', content="Coming Soon"),
]
BlogArticle.objects.bulk_create(items)
有了SQL:
INSERT INTO `articles_blog_article` (`user_id`, `title`, `slug`, `content`, `publish_timestamp`)
VALUES (1, 'Hello Again 0', 'hello-again-0', 'Coming Soon', NULL),
(1, 'Hello Again 1', 'hello-again-1', 'Coming Soon', NULL),
(1, 'Hello Again 2', 'hello-again-2', 'Coming Soon', NULL),
(1, 'Hello Again 3', 'hello-again-3', 'Coming Soon', NULL),
(1, 'Hello Again 4', 'hello-again-4', 'Coming Soon', NULL);
同样,Python 的代码更具有可读性,而SQL代码则能更深入地了解实际数据。而且,Python ,又一次用上面的Django代码为我们写了这段SQL代码。很整洁,是吧?
我之所以潜心研究这个侧面比较,不是为了挑选,哪个是利用SQL数据库的最佳方式,而是为了突出Python,帮助减少直接学习编写原始SQL的开销。
有几个Python 包,它们基本上是为你写原始SQL,下面是其中的几个:
- Django
- 熊猫
- SQLAlchemy
- 极点
- 邓小平
- Vaex
- Python的内置CSV模块
- 龟兔赛跑 ORM
- Pony ORM
- 淘宝客
Django做重活
对象关系映射包(通常被称为ORM)是Python ,它是如何利用SQL数据库的秘方。我认为ORM是一个中间人,它可以帮助在任何特定的编程语言的本地语法中移动数据。
在本练习的早些时候,我们开始看到这一点是如何转化为Django的,但现在让我们扩展一下。
假设我们的数据库里有数据,我们可以写一个这样的命令:
my_post = BlogArticle.objects.first()
这个语句将查询我们的数据库,提取数据,将其加载到一个Python Class
,然后将其分配给变量 my_post
.
从这里,我们现在可以做这样的事情:
# using a django-managed python shell
# via python manage.py shell
>>> print(my_post.title)
Hello World
在这种情况下,我们使用点符号来访问 title
中声明的字段。 BlogPost
上一节中的Django模型。这个字段对应于我们SQL数据库表中的一个列 articles_blog_article
.
多亏了ORM,我们可以做到这一点:
>>> my_post.title = "some other title"
在这个Python shell会话的例子中, my_post.title
现在将 始终 是 "some other title"
.然而,底层的SQL数据库仍然将这些完全相同的数据识别为 Hello World
.数据库将保留原始数据,直到Python 最终提交(又称 .save()
)这个数据变化到数据库中。如果Python ,它将永远不会在数据库中被更新。这就是ORM的神奇之处。我们可以 使用 和 变化 而不影响实际存储的数据。当我们想改变数据库中实际发生的情况时,我们运行:
>>> my_post.title = "some other title again"
>>> my_post.save()
运行后 .save()
我们的数据库,对于这个特定的行,将更新列的标题,以完全匹配上面写成的Python 字符串。请不要忘记,在 .save()
方法是专门用于在Django模型中向数据库进行提交的。 .save()
对一个人来说,实际上并不意味着什么Python Class
而不首先继承一个形式的Django模型类。
用Django、MySQL和Linode进行建设
这是Django如何为你做繁重工作的众多例子之一。如果你对在Linode上部署Django应用程序的现代方法以及管理的MySQL数据库、用于CI/CD的GitHub Actions、Terraform ,以及Ansible ,请查看以下内容,现在Linode上可以看到:
- 在Linode上观看我们的理解数据库视频教程系列直播
- 下载《了解数据库》:扩展版电子书
为了帮助你开始,Coding for Entrepreneurs GitHub有一个代码库,与该系列的每个步骤相配套。祝你好运,一定要通过Twitter@JustinMitchel让我知道事情进展如何。
注释