美文网首页
Django 数据库操作

Django 数据库操作

作者: 李霖弢 | 来源:发表于2021-11-30 14:16 被阅读0次

通过ORM(Object-Relation Mapping,对象关系映射),把一个类对应到一个表,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段,开发人员只需像操作对象一样从数据库操作数据。

注意,自定义的mysql_client.pyredis_client.py中应使用单例模式,保证同一进程下只有一个数据库实例。

数据库配置

settings.pyDATABASES中可以配置数据库,默认使用SQLite3。
如使用MySQL,则需要安装驱动模块:mysqlclient,这是MySQLdb的分支,用于兼容Python3,安装时也会附带MySQLdb包。Django进一步封装了该驱动模块以实现内置的ORM模型(见django.db.backends.mysql.baseDatabaseWrapper类),也可以创建其同名子类并进一步自定义封装:

# Linux
pip3 install mysqlclient -i https://pypi.tuna.tsinghua.edu.cn/simple
# Windows
pip3 install mysqlclient @ file:///D:/ChinaDuanCai/DRF/static/mysqlclient-2.0.3-cp37-cp37m-win_amd64.whl 

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 也可以自定义为封装后的模块路径
        'NAME': 'drf',  # 连接的数据库  #一定要存在的数据库名
        'HOST': '127.0.0.1',  # mysql的ip地址
        'PORT': 3306,  # mysql的端口
        'USER': 'root',  # mysql的用户名
        'PASSWORD': 'vl1qaz@WSX'  # mysql的密码
    }
}

关于为什么不用Pymysql,以及通过连接池解决并发问题,详见https://www.jianshu.com/p/e51dc4f6356a

设计模型

# models.py
from django.db import models
from DRF.settings import MEDIA_ROOT

class Type(models.Model):
    id = models.AutoField(primary_key=True, unique=True)
    name = models.CharField(verbose_name="类型名", max_length=10) # verbose_name用于自带的admin页面中显示名称

def cb():
    return None

def upload_to(instance, filename):
    return "/".join([MEDIA_ROOT, "upload", filename])

class Blog(models.Model):
    sex_choices = (
        (0, '男'),
        (1, '女'),
    )

    # 自增int + 主健 + 唯一值,model中无主键列时会自动创建该id字段
    id = models.AutoField(primary_key=True, unique=True)

    # 建立索引
    code = models.CharField(max_length=64, null=True, verbose_name="编号", db_index=True)

    name = models.CharField(verbose_name="姓名", max_length=32)
    age = models.IntegerField("年龄")
    sex = models.SmallIntegerField('性别', choices=sex_choices, default=0)
    has_hair = models.BooleanField()
    phone = models.CharField("手机号", max_length=14, null=True)  # 该字段可以为null

    # 超过254个字符应使用TextField,对应MySQL中的longtext
    description = models.TextField("简介", blank=True)  # 可以为空字符串

    register_date = models.DateField('注册日期', auto_now_add=True)  # 首次添加时使用当前时间
    update_time = models.DateTimeField('注册时间', auto_now=True)  # 每次保存时使用当前时间
    is_delete = models.BooleanField(default=False)
    type_id = models.ForeignKey(  # 外键
        Type,  # 主表
        # related_name="blog_set",  # 默认即为 子表的小写名称_set
        on_delete=models.SET(cb),  # 主表对应行删除时的子表行为
        # on_delete=models.CASCADE,
        null=True)

    # models.FileField 用于接收上传的文件,其在数据库里只是一个路径字符串
    # upload_to用于指定MEDIA_ROOT下的相对路径,也可以传入一个方法用于自定义保存逻辑。
    # upload_file = models.FileField(upload_to="upload/") #指向 settings.MEDIA_ROOT 下的 upload 子目录
    # upload_file = models.FileField(upload_to="upload/%Y/%m/%d")
    upload_file = models.FileField(upload_to=upload_to, default="")

    # 自定义实例print输出内容
    def __str__(self):
        return f"name={self.name}"

    class Meta:
        db_table = 'blog'  # 通过db_table自定义数据表名

应用模型

  • python manage.py makemigrations
    查找所有(或指定)app中可用模型(model),创建迁移脚本(XXXX_initial.py)
python manage.py makemigrations
python manage.py makemigrations app_name
  • python manage.py migrate
    检查django_migrations表,并运行其中不存在的_initial.py脚本创建/修改表结构,并在django_migrations表中插入一条记录。
python manage.py migrate
python manage.py migrate app_name

QuerySet

查询集,也称查询结果集,表示ORM从数据库中获取的对象(model实例)集合。
可迭代,可转为列表,也可以直接索引或切片,支持链式调用:

items = Blog.objects.all()
# 迭代
for item in items: 
    print(item)
# 转为列表
[item for item in items]
list(items)
# 索引、切片
items[0]
items[1:3]
model 实例对象
  • 实例对象来源
    对QuerySet调用getfirstlast、索引等方法,或直接索引取出
    将QuerySet先转为列表,再从中取出
    model类.objects.create方法 或 直接实例化model类 创建
  • 更新
    修改属性并调用save方法即可
    注意,不能直接修改QuerySet中的成员,应先将其重新赋值:
query_set = Blog.objects.all()
query_set[0].name="new" # 无效
query_set.first().name="new" # 无效
new_item = query_set[0]
new_item.name="new" 
new_item.save()
  • 删除
    直接调用delete方法即可
query_set = Blog.objects.all()
query_set[0].delete()
  • 转化为dict
    可以直接调用__dict__属性,但会包含一些冗余字段。
    更好的方法是使用django自带的model_to_dict(instance, fields=None, exclude=None)方法,其中,fields指定需要哪些字段,exclude指定排除哪些字段,exclude比fields优先更级高。
from django.forms.models import model_to_dict
model_to_dict(Blog.objects.first(), exclude=['create_time', 'update_time'])
惰性查询

创建查询集本身不进行数据库访问,仅读取其值时才真正运行:

query_set = Book.objects.all() # 不会查询数据库
print(query_set) # 查询数据库
缓存机制

每个查询集都包含一个缓存来最小化对数据库的访问。
对原本的查询集执行filter等操作会创建一个新的查询集,拥有独立的缓存空间。
首次对整个查询集求值时,发生数据库访问,其查询结果会存入缓存,后续对查询集的取值直接从缓存中读取。

  • 直接print(query_set)无需获知其完整细节,不会进入缓存,但可以读取缓存
  • 对查询集索引切片只是读取查询集中部分项,不会进入缓存,但可以读取缓存
  • 布尔判断
    直接对查询集进行如下布尔判断,会触发整个查询集求值
bool(query_set)
if query_set:
    ...

可使用exist方法节省性能,此时本质上只查了一条看是否存在,因此不会加入缓存:

if query_set.exists():# SELECT (1) AS `a` FROM `app01_book` LIMIT 1
    ...
  • 迭代操作
    如下,普通的迭代操作都会触发缓存:
[book for book in query_set]
bool(query_set)
any_obj in query_set
list(query_set)

当数据量过大时,为防止占用过多内存,可用iterator迭代器。迭代器也会在第一次被调用时完成完整的查询,只是不会进入缓存:

objs = Book.objects.all().iterator()
for obj in objs:
    print(obj.name)
# 强调:再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in books:
  print(obj.name)
  • only 与 defer
    only方法指定加入缓存的字段,其他字段都不加入缓存
    defer方法指定不加入缓存的字段,其他字段都加入缓存
res = Book.objects.only('name')

for obj in res:
    print(obj.name) # .name不走数据库 .其他字段会重新走数据库查询
objects 与 链式方法

models中的类均继承models.Model,也继承了其 objects 属性用于管理模型。
其具有如下链式方法,如返回值是QuerySet则可以继续调用:

  • .all()
    查询所有结果,返回 QuerySet 实例
  • .filter(**kwargs)
    筛选符合条件的内容,返回 QuerySet 实例。
    当无符合条件者时返回<QuerySet []>,该值转为布尔值时为False(同空列表)
  • .exclude(**kwargs)
    排除符合条件的内容,返回 QuerySet 实例。
  • .distinct()
    去重,返回 QuerySet 实例
  • .order_by(*args)
    排序,返回 QuerySet 实例。传入字段以-开头则为倒序。
  • .reverse()
    倒序,返回 QuerySet 实例
  • .count()
    总数,返回 int
  • .first() / .last()
    返回第一个 / 最后一个模型对象实例,无值则返回None
  • .get(**kwargs)
    返回一个符合条件的模型对象实例,如无符合条件或有多个符合条件,都会报错
  • .exists()
    如果 QuerySet 包含数据,就返回 True,否则返回 False。
  • .values(*args)
    返回一个可迭代的字典序列,支持继续调用QuerySet方法。
    默认包含所有字段,也可指定仅包含部分字段
  • .values_list(*args)
    类似values,返回不带key的元组序列
    如有参数 flat=True 则返回不带key的列表序列
  • .aggregate()
    实现SQL聚合函数,返回一个dict。
    字典key若未指定则默认为字段__聚合函数名
def db_retrieve2(request):
    vvs = Blog.objects.all().aggregate(Avg('age'), Sum('age'), count=Count('*'),
                                       max_age=Max('age'), min_age=Min('age'))
    return JsonResponse(vvs, json_dumps_params={'ensure_ascii': False})
  • .annotate()
    实现SQL的GROUP BY功能,返回 QuerySet 实例
    如下,前置的values用于指定group by的目标字段,默认为主键字段。
vvs = Blog.objects.values("age").annotate(Min('id'))
# SELECT `blog`.`age`, MIN(`blog`.`id`) AS `id__max` FROM `blog` GROUP BY `blog`.`age`
  • .extra(select=None, where=None, params=None,
    tables=None, order_by=None, select_params=None)
    用于其他ORM语句满足不了时的复杂语句,注意防范SQL注入
## select提供简单数据
# SELECT age, (age > 18) as is_adult FROM myapp_person ORDER BY is_adult;
Person.objects.all().extra(select={'is_adult': "age > 18"}, order_by=['is_adult'])  # 加在select后面

## where提供查询条件
# SELECT * FROM myapp_person WHERE first||last ILIKE 'jeffrey%';
Person.objects.all().extra(where=["first||last ILIKE 'jeffrey%'"])  # 加一个where条件

## table连接其它表
# SELECT * FROM myapp_book, myapp_person WHERE last = author_last
Book.objects.all().extra(table=['myapp_person'], where=['last = author_last']) # 加from后面

## params添参数
# !! 错误的方式 !!
first_name = 'Joe'  # 如果first_name中有SQL特定字符就会出现漏洞
Person.objects.all().extra(where=["first = '%s'" % first_name])
# 正确方式
Person.objects.all().extra(where=["first = '%s'"], params=[first_name])
双下划线过滤参数

__exact 精确等于 (缺省值)
__iexact 精确等于 忽略大小写
__contains 包含(sqlite中,等同于icontains)
__icontains 包含 忽略大小写
__regex 正则
__iregex 正则 忽略大小写
__gt 大于
__gte 大于等于
__lt 小于
__lte 小于等于
__in 存在于一个 list 内
__startswith 以...开头
__istartswith 以...开头 忽略大小写
__endswith 以...结尾
__iendswith 以...结尾,忽略大小写
__range 在...范围内
__year 日期字段的年份
__month 日期字段的月份
__day 日期字段的日
__isnull 取 True 或 False,对应 IS NULL 和 IS NOT NULL 的 SQL 查询

Q 查询

使用符号&表示与(或直接隔开),|表示或,~表示非,将多个Q对象进行组合,合并成一个新的Q对象。
Q对象和普通关键词参数共同使用时,应放在普通关键词参数前面

from django.db.models imports Q
News.objects.get(
    ~Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    is_delete=1)
)
F 查询

用于引用该model下的另一个字段

from django.db.models imports F
News.objects.filter(aaa__gt=F('bbb')*2)
跨表查询优化
  • 外键关联查询
    select_related(外键字段1, 外键字段2),底层为JOIN
    prefetch_related(外键字段1, 外键字段2),底层为子查询
    将当前表和外键关联表一次性都查出来,以免多次查询。通过使用双下划线“__”连接字段名还能实现指定的递归查询:
res = Book.objects.select_related('publish')

for obj in res:
    print(obj.publish.name)

article= Article.objects.select_related("blog__user").get(nid=1)
print(article.blog.user.username)

可以直接调用create方法新增,也可以实例化model并在赋值后save。
两种方法返回值均为model的实例。

Blog.objects.create(name="VV", age=8, has_hair=False)

vv = {"name": "VV2", "age": 6, "has_hair": False}
Blog.objects.create(**vv)

instance = Blog(name = "VV", age=7)
instance.has_hair= False
instance.save()

queryset实例 都具有 delete 方法
注意该方式是硬删除

Blog.objects.all().delete()
Blog.objects.first().delete()
r = Blog.objects.create(name="VV", age=8, has_hair=False)
r.delete()

queryset 具有 update 方法
实例 可以修改后调用 save 方法

Blog.objects.all().update(age=18)
Blog.objects.first().update(age=18)
r = Blog.objects.create(name="VV", age=8, has_hair=False)
r.age = 18
r.save()

通过上述QuerySet语法进行查询,注意缓存的使用即可。

Entry.objects.filter(is_delete=0,headline__startswith="What")
.exclude(pub_date__gte=datetime.date.today())
.order_by('-title', 'pub_date')

外键

创建

其中on_delete可选的值包括:CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()

# models.py
    type_id = models.ForeignKey(  # 外键
        Type,  # 主表
        # related_name="blog_set",  # 默认即为 子表的小写名称_set
        on_delete=models.SET(cb),  # 主表对应行删除时的子表行为
        # on_delete=models.CASCADE,
        null=True)
赋值

Django中,外键属性赋值时,需直接赋值所在行的实例对象:

def db_update_2(request):
    vv = Blog.objects.first()
    vv.type_id = Type.objects.get(id=1)
    vv.save()
    return JsonResponse(model_to_dict(vv), json_dumps_params={'ensure_ascii': False})
主表实例查看关联的子表QuerySet

默认可以通过主表实例.子表小写名称_set.all()获取。
也可以在子表类中声明外键字段时,通过related_name自定义名称。

type = Type.objects.first()
type.blog_set.all()

查看Django实际的数据库语句

from django.db import connection
print(connection.queries[-1]['sql'])

相关文章

  • 如何查看Django ORM执行的SQL语句

    Django ORM对数据库操作的封装相当完善,日常大部分数据库操作都可以通过ORM实现。但django将查询过程...

  • Django模型类的属性与关系

    在django中, 所有的数据库操作都是面向对象编程,基本上看不到操作数据库的sql语句,方便快捷是django开...

  • 数据模型操作(1)

    Django操作数据库 -----数据模型MVT:model数据模型部分 1. Django 连接数据库 修改项目...

  • Django:04.ORM操作

    一、Django连接数据库操作 1、创建数据库 (注意设置 数据的字符编码)由于Django自带的orm是data...

  • django

    django django同步表结构建表修改表数据库操作增删改查 同步表结构 所有操作需要在项目manage.py...

  • Django事务操作

    在Django中实现数据库的事务操作 在学习MySQL数据库时,MySQL数据库是支持原子操作的. 什么是数据库的...

  • Django连接MySQL数据库

    1.1.Django项目连接mysql数据库 Django项目要操作数据库,首先要和数据库建立连接,才能让程序中的...

  • Django数据存储到数据库(建表)

    Django中要将数据库存储到数据库中,步骤如下环境:python3+Django2.0 操作过程 1.在sett...

  • django 工程创建

    创建Django工程 创建APP 数据库操作 运行Django 配置settings.py 模板路径 静态文件st...

  • Web开发(十)Django模型-自定义模型(配置数据库)

    一、数据库配置 Django 如何使用 mysql 数据库 创建 MySQL 数据库( ORM 无法操作到数据库级...

网友评论

      本文标题:Django 数据库操作

      本文链接:https://www.haomeiwen.com/subject/itrdxrtx.html