Django 框架对数据库的操作

发表于2019-08-28,长度3062, 234个单词, 8分钟读完
Flag Counter

Django是🔥了多年的python web开发框架,在python界的火爆不输于Java界的Spring。与之类似的还有Flask,但是flask对python 3的支持度有点低,所以我还是选择使用Django。

温馨提示:Django中的D不发音,所以这个框架叫“粘钩”

Django与数据库的交互有点像JPA框架,严重的约定优于配置。国内搞Java开发的受阿里影响,基本都用mybatis,喜欢自己写灵活的SQL语句,而不喜欢依托规则。毕竟SQL几年前就学会了,可是要学比如JPA,还得特意去学其特性,烦!

模型

Django 应用创建好以后,数据模型都放在APP的models.py文件中。模型需要继承django.db.models.Model类,这样Django才能自动处理,为其生成表。比如:

from django.db import models


class MyMD(models.Model):
    id = models.IntegerField

上面这个模型只定义了主键字段id。下面是一个稍微复杂点的模型:

class MyMd(models.Model):
    id = models.IntegerField
    shop_id = models.CharField(max_length=8, default='')
    goods_id = models.CharField(max_length=30, default='')
    file = models.FileField(max_length=200, blank=False, null=False)
    created_at = models.DateTimeField('创建时间', auto_now_add=True, null=False, blank=False)
    created_by = models.CharField(max_length=50, default='')
    updated_at = models.DateTimeField('变更时间', auto_now=True, null=False, blank=False)
    updated_by = models.CharField(max_length=50, default='')
    last_updated_at = models.DateTimeField('时间戳', auto_now=True)

    def __str__(self):
        return self.image_name + self.file.name

    class Meta:
        db_table = "my_table"
        indexes = [
            models.Index(fields=['shop_id', 'goods_id'])
        ]

上面这个类加了更多字段,字段类型也丰富了,除了主键是整型以外,用的最多的当然是字符串models.CharField。models.CharField必须传入参数max_length。另外还有表示日期的models.DateTimeField类型字段,如果指定其为auto_now,则记录插入和保存的时候都会自动更新;如果指定为auto_now_add,则在插入的时候插入当前时间。这里还有一个字段是文件上传的models.FileField,当然它只记录文件或图片上传保存后的服务器路径。

这个模型最下面是一个Meta内部类,用于特殊配置,比如指定了这个模型生成的表名是my_table,否则会是app名加上模型名。另外还指定了一个联合索引,需要的话可以指定更多索引。

迁移

Django把根据模型生成数据映射文件的过程叫migrate,中文翻译过来是迁移,有点怪。 迁移很简单,直接使用命令(对,是命令,没有GUI操作):

python manage.py makemigrations my_app_name

这样会在my_app_name中生成migrate目录,里面就是映射文件。模型每次变更都会追加一个映射文件,所以第一次会是0001,第二次就是0002。

可以使用命令 python manage.py sqlmigrate my_app_name 0001 查看对应映射文件要执行的SQL语句。

映射文件并不只是用于生成SQL的,那样的话模型直接生成更好。前面提到的自动复制自动并不会写入SQL,而是在应用执行的时候,根据映射文件判断的。所以你在生成的SQL中看不到 CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) 这种语句。

最后就是执行上面看到的SQL了,当然不用复制出来去跑一下,而是执行:

python manage.py migrate

你会看到数据库生成了很多莫名其妙的表,这是Django各种插件迁移的结果。其实替你生成表并不是Django的特性,很多框架和中间件都会这样。

读写

正如上面提到,Django对数据库的操作是基于规则的。比如已经有一个模型的实例m:

m = MyMd(...)

调用m.save()即刻插入数据库,并且m会被附上插入后的主键(当然mybatis也是这样)。如果m更新了,也是调用m.save()。

Django中的模型增改就是这样简单,复杂和舒服的是读取规则。比如要查询全部记录:

all_ = MyMd.objects.all()

要查询某个字段是“XXX”的记录的ID和名字:

XXXset = MyMd.objects.filter(shop_id = 'XXX').values('id', 'name')

如果你熟悉Java8的流式操作,是不是有点熟悉?

Django中的数据读取都是延迟执行的,也就是需要拿数据进行操作了,数据库查询才会进行。比如有另一个模型BlackList,记录的是MyMd的黑名单,我们想查询不在名单中的全部XXX记录,这样需要先查出来黑名单的id集合,用它去过滤模型:

bl = BlackList.objects.all().values('id') # 全部id
all_ = MyMd.objects.filter(Q(shop_id = 'XXX') & ~Q(id__in=bl)).values('id', 'name')

这里有几个要点:使用了Q语法,使用了否定~,使用了与操作&。关于它们更多的信息可以自行去谷歌上百度一下。

如果只有上面两句语句,并不产生任何的数据查询,因为它们的结果没有被使用。当遍历all_的时候,上面两句会生成一个大SQL:

select id, name from my_table where shop_id = 'XXX' and id not in (select id from blacklist) a

可以通过all_.query查看生成的sql。也可以引入connection查看:

from django.db import connection
print('查询数据库', connection.queries)

这样会打印所有已经执行过的SQL。

生产中是不鼓励使用外键的,而Django对外键设计做了大量优化,实在有点可惜 ```

Written on August 28, 2019
分类: dev, 标签: python database
如果你喜欢,请赞赏! davelet