python_fullstack—Django框架(五)-Django ORM

Django 对象关系映射(ORM)

一、ORM基本介绍

1、ORM介绍

1.1 ORM概念
  • 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
  • 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
  • ORM在业务逻辑层和数据库层之间充当了桥梁的作用。
1.2 ORM的优缺点
  • 优点

    • ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段
    • ORM提供了对数据库的映射,不用直接编写SQL代码,只需像操作对象一样从数据库操作数据
    • 使开发人员专注于业务逻辑处理,提高开发效率
  • 缺点

    • ORM间接操作数据库,所以会在一定程度上牺牲程序的执行效率
    • 经常使用ORM操作数据库会弱化SQL编写能力,致使关系数据库相关技能退化
1.3 ORM总结

ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。
但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。

2、Django中的ORM

2.1 连接数据库(MySQL为例)
  • 在Django项目的settings.py文件中,配置数据库连接信息:
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "欲连接的数据库名称",  # 需要自己手动创建数据库
        "USER": "数据库用户名",
        "PASSWORD": "数据库密码",
        "HOST": "数据库IP",
        "POST": 3306 }
}
  • 在Django项目的__init__.py文件中写如下代码,告诉Django使用pymysql模块连接MySQL数据库:
# python3中需要写入以下内容
import pymysql

pymysql.install_as_MySQLdb()
2.2 Model与DataBase映射
  • 基本映射关系介绍
    在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。

    • 每个模型都是一个Python类,它是django.db.models.Model的子类
    • 模型的每个属性都代表一个数据库字段
    • Django提供了一个自动生成的数据库访问API
    • 官方文档链接
      ORM-DB映射关系图示
  • 入门示例

# 在models.py中定义了一个 **Person** 模型,包含first_name 和 last_name
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

# first_name 和 last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。上面的 Person 模型将会像这样创建一个数据库表:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL 
);
  • 一些说明:
    • 表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
    • id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
    • 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。
    • Django支持MySQL5.5及更高版本。

二、Django ORM常用字段和参数

1、常用字段

  • AutoField

    • int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。
  • IntegerField

    • 一个整数类型,范围在 -2147483648 to 2147483647。
  • CharField

    • 字符类型,必须提供max_length参数, max_length表示字符长度。
  • DateField

    • 日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。
  • DateTimeField

    • 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。

2、字段合集

  • AutoField(Field)

    • int自增列,必须填入参数primary_key=True
  • BigAutoField(AutoField)

    • bigint自增列,必须填入参数primary_key=True
  • SmallIntegerField(IntegerField)

    • 小整数-32768 ~ 32767
  • PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)

    • 正小整数 0 ~ 32767
  • IntegerField(Field)

    • 整数列(有符号的) -2147483648 ~ 2147483647
  • PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)

    • 正整数 0 ~ 2147483647
  • BigIntegerField(IntegerField)

    • 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
  • BooleanField(Field)

    • 布尔值类型
  • NullBooleanField(Field)

    • 可以为空的布尔值
  • CharField(Field)

    • 字符类型
    • 必须提供max_length参数, max_length表示字符长度
  • TextField(Field)

    • 文本类型
  • EmailField(CharField)

    • 字符串类型,Django Admin以及ModelForm中提供验证机制
  • IPAddressField(Field)

    • 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
  • GenericIPAddressField(Field)

    • 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
    • 参数:
      protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
      unpack_ipv4, 如果指定为True,则输入: : ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
  • URLField(CharField)

    • 字符串类型,Django Admin以及ModelForm中提供验证 URL
  • SlugField(CharField)

    • 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
  • CommaSeparatedIntegerField(CharField)

    • 字符串类型,格式必须为逗号分割的数字
  • UUIDField(Field)

    • 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
  • FilePathField(Field)

    • 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
    • 参数:
      path, 文件夹路径
      match=None, 正则匹配
      recursive=False, 递归下面的文件夹
      allow_files=True, 允许文件
      allow_folders=False, 允许文件夹
  • FileField(Field)

    • 字符串,路径保存在数据库,文件上传到指定目录
    • 参数:
      upload_to = “” 上传文件的保存路径
      storage = None 存储组件,默认Django.core.files.storage.FileSystemStorage
  • ImageField(FileField)

    • 字符串,路径保存在数据库,文件上传到指定目录
    • 参数:
      upload_to = “” 上传文件的保存路径
      storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
      width_field=None, 上传图片的高度保存的数据库字段名(字符串)
      height_field=None 上传图片的宽度保存的数据库字段名(字符串)
  • DateTimeField(DateField)

    • 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
  • DateField(DateTimeCheckMixin, Field)

    • 日期格式 YYYY-MM-DD
  • TimeField(DateTimeCheckMixin, Field)

    • 时间格式 HH:MM[:ss[.uuuuuu]]
  • DurationField(Field)

    • 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
  • FloatField(Field)

    • 浮点型
  • DecimalField(Field)

    • 10进制小数
    • 参数:
      max_digits,小数总长度
      decimal_places,小数位长度
  • BinaryField(Field)

    • 二进制类型

3、自定义字段

# 自定义Char类型字段
class FixedCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return 'char(%s)' % self.max_length

class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定义的char类型的字段
    cname = FixedCharField(max_length=25)

4、ORM字段与数据库实际字段的对应关系

# 对应关系:
    'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',

5、字段参数

  • null

    • 用于表示某个字段可以为空。
  • unique

    • 如果设置为unique=True 则该字段在此表中必须是唯一的 。
  • db_index

    • 如果db_index=True 则代表着为此字段设置索引。
  • default

    • 为该字段设置默认值。
  • auto_now_add (用于DateField、DateTimeField)

    • 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
  • auto_now (用于DateField、DateTimeField)

    • 配置上auto_now=True,每次更新数据记录的时候会更新该字段。

6、关系字段

6.1 ForeignKey 字段参数

外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 ‘一对多’中’多’的一方。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。
- to
- 设置要关联的表

  • to_field

    • 设置要关联的表的字段
  • related_name

    • 反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’
  • related_query_name

    • 反向查询操作时,使用的连接前缀,用于替换表名
  • db_constraint

    • 是否在数据库中创建外键约束,默认为True。
  • on_delete

    • 当删除关联表中的数据时,当前表与其关联的行的行为
models.CASCADE
删除关联数据,与之关联也删除

models.DO_NOTHING
删除关联数据,引发错误IntegrityError

models.PROTECT
删除关联数据,引发错误ProtectedError

models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)

models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

models.SET
删除关联数据
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
6.2 OneToOneField 字段参数

一对一字段,通常一对一字段用来扩展已有字段。
- to
- 设置要关联的表。

  • to_field

    • 设置要关联的字段。
  • on_delete

    • 同ForeignKey字段。
6.3 ManyToManyField 字段参数

用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系
- to
- 设置要关联的表

  • related_name

    • 同ForeignKey字段。
  • related_query_name

    • 同ForeignKey字段。
  • symmetrical

    • 仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。
  • through

    • 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
    • 但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。
  • through_fields

    • 设置关联的字段。
  • db_table

    • 默认创建第三张表时,数据库中表的名称。
  • **元信息
    ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:**

  • db_table

    • ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。
  • index_together

    • 联合索引。
  • unique_together

    • 联合唯一索引
  • ordering

    • 指定默认按什么字段排序。
    • 只有设置了该属性,我们查询到的结果才可以被reverse()

三、常用操作

1、必会ORM单表查询操作

1.1 返回QuerySet对象的方法
  • models.TableName.objects.all()
    查询所有结果

  • models.TableName.objects.filter(**kwargs)
    它包含了与所给筛选条件相匹配的对象

  • models.TableName.objects.exclude(**kwargs)
    它包含了与所给筛选条件不匹配的对象

  • models.TableName.objects.order_by(*field)
    对查询结果排序

  • models.TableName.objects.reverse()
    对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)

  • models.TableName.objects.distinct()
    从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)

1.2 特殊的QuerySet
  • models.TableName.objects.values(*field)
    返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列

  • models.TableName.objects.values_list(*field)
    它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

1.3 返回具体对象
  • models.TableName.objects.get(**kwargs)
    返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

  • models.TableName.objects.first()
    返回第一条记录

  • models.TableName.objects.last()
    返回最后一条记录

1.4 返回布尔值的方法
  • models.TableName.objects.exists()
    如果QuerySet包含数据,就返回True,否则返回False
1.5 返回数字的方法
  • models.TableName.objects.count()
    返回数据库中匹配查询(QuerySet)的对象数量
1.6 神奇的双下划线
  • models.TableName.objects.filter(attr__lt=value)
    获取attr小于value的值

  • models.TableName.objects.filter(attr__gt=value)
    获取attr大于value的值

  • models.TableName.objects.filter(attr__in=[value1,value2,value3])
    获取attr等于value1、value2、value3的值

  • models.TableName.objects.filter(attr__contains="substring")
    获取attr字段包含”substring”的值

  • models.TableName.objects.filter(attr__icontains="substring")
    获取attr字段包含”substring”的值(大小写不敏感)

  • models.TableName.objects.filter(attr__range=[value1,value3])
    获取attr范围在value1至value3的值(左右都包含)

  • models.TableName.objects.filter(attr__startswith="substring")
    获取以substring开头的值

  • models.TableName.objects.filter(attr__istartswith="substring")
    获取以substring开头的值(大小写不敏感)

  • models.TableName.objects.filter(attr__enswith="substring")
    获取以substring结尾的值

  • models.TableName.objects.filter(attr__ienswith="substring")
    获取以substring结尾的值(大小写不敏感)

  • models.TableName.objects.filter(attr__year=num)
    获取attr字段year值等于num的结果(year也可以为month、day分别获取月份、日期)

链接:https://docs.djangoproject.com/en/1.11/ref/models/querysets/

2、ORM多表查询操作

2.1 ForeignKey操作
  • 正向查找

    • 对象查找(跨表)语法:Object.related_field.field
    • 字段查找(跨表)语法:related_field__field
  • 反向查找

    • 对象查找语法:Object.TableName_set
    • 字段查找语法:TableName__field
2.2 ManyToManyField操作
2.2.1 RelatedManager
  • “关联管理器”是在一对多或者多对多的关联上下文中使用的管理器。它存在于下面两种情况:
    1. 外键关系的反向查询
    2. 多对多关联关系
2.2.2 方法
  • create()

    • 创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。
    • 简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。
  • add()

    • 把指定的model对象添加到关联对象集中。
  • remove()

    • 从关联对象集中移除执行的model对象
  • clear()

    • 从关联对象集中移除一切对象
    • 注意:对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。
  • 注意:

    • 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
2.3聚合查询和分组查询
2.3.1 聚合

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

>>> models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 13.233333}

# 如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
>>> models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 13.233333}

# 如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
>>> models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}
2.3.2 分组

为调用的QuerySet中每一个对象都生成一个独立的统计值

# 示例1:统计每一本书的作者个数
>>> book_list = models.Book.objects.all().annotate(author_num=Count("author"))
>>> for obj in book_list:
...     print(obj.author_num)
...
2
1
1

# 示例2:统计出每个出版社买的最便宜的书的价格
>>> publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
>>> for obj in publisher_list:
...     print(obj.min_price)
...
9.90
19.90

#方法二:
>>> models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{'publisher__name': '出版社0001', 'min_price': Decimal('9.90')}, {'publisher__name': '出版社002', 'min_price': Decimal('19.90')}]>

#示例3:统计不止一个作者的图书
>>> models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: 书籍001>]>

#示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序
>>> models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 书籍002>, <Book: 书籍003>, <Book: 书籍001>]>

# 示例5:查询各个作者出的书的总价格
>>> models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{'name': '作者001', 'sum_price': Decimal('9.90')}, {'name': '作者002', 'sum_price': Decimal('29.80')}, {'name': '作者003', 'sum_price': Decimal('9.90')}]>
2.4 F查询和Q查询
2.4.1 F查询

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

# 查询评论数大于收藏数的书籍
from django.db.models import F
models.Book.objects.filter(comment_num__lt=F('keep_num'))

# Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
models.Book.objects.filter(comment_num__lt=F('keep_num')*2)

# 修改操作也可以使用F函数,比如将每一本书的价格提高30元
models.Book.objects.all().update(price=F("price")+30)

# 引申:
# 修改char字段,如:把所有书名后面加上(第一版)
>>> from django.db.models.functions import Concat
>>> from django.db.models import Value
>>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))
2.4.2 Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。

# 查询作者名是小仙女或小魔女的
models.Book.objects.filter(Q(authors__name="小仙女")|Q(authors__name="小魔女"))

#你可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
# 示例:查询作者名字是小仙女并且不是2018年出版的书的书名。
>>> models.Book.objects.filter(Q(author__name="小仙女") & ~Q(publish_date__year=2018)).values_list("title")
<QuerySet [('番茄物语',)]>

# 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
# 例如:查询出版年份是2017或2018,书名中带物语的所有书。
>>> models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物语")
<QuerySet [<Book: 番茄物语>, <Book: 香蕉物语>, <Book: 橘子物语>]>

3、事务

  • 作用是使事务中的操作具有原子性,常用于财务相关操作
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") 
    import django
    django.setup()

    import datetime
    from app01 import models

    try: from django.db import transaction
        with transaction.atomic():
            new_publisher = models.Publisher.objects.create(name="出版社001")
            models.Book.objects.create(title="书籍001", publish_date=datetime.date.today(), publisher_id=10)  # 指定一个不存在的出版社id
    except Exception as e:
        print(str(e))

4、不常用的ORM操作

4.1 Django终端打印SQL语句
  • 在Django项目的settings.py文件中写入如下内容,可在终端输出SQL语句
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler',
        },
    }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG',
        },
    }
}<
4.2 在Python脚本中调用Django环境
import os if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") import django
    django.setup() from app01 import models

    books = models.Book.objects.all() print(books)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值