ORM

一  概述

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。

每个模型都是一个Python类,它是django.db.models.Model的子类。模型的每个属性都代表一个数据库字段。

1. Django使用MySQL配置

1. 在Django项目的settings.py文件中,配置数据库连接信息:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "你的数据库名称",  # 需要自己手动创建数据库
        "USER": "数据库用户名",
        "PASSWORD": "数据库密码",
        "HOST": "数据库IP",
        "POST": 3306
    }
}

2. 在Django项目的__init__.py文件中写如下代码,告诉Django使用pymysql模块连接MySQL数据库:

import pymysql
pymysql.install_as_MySQLdb()

二  ORM 常用字段和参数

1. 字段

  AutoField(Field)
        - int自增列,必须填入参数 primary_key=True。id字段是自动添加的,如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
  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)
        - 二进制类型

多选的话需要导入:

from multiselectfield import MultiSelectField

2. 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)',

3. 自定义字段

class FixedCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        super().__init__(max_length=max_length, *args, **kwargs)
        self.length = max_length
    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为length指定的值
        """
        return 'char(%s)' % self.length  #数据库的语法char()

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

4. 字段参数

   null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    default             数据库中字段的默认值
    primary_key         数据库中字段是否为主键
    db_index            数据库中字段是否可以建立索引
    unique              数据库中字段是否可以建立唯一索引
    unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
    unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
    unique_for_year     数据库中字段【年】部分是否可以建立唯一索引
 
    verbose_name        Admin中显示的字段名称
    blank               Admin中是否允许用户输入为空
    editable            Admin中是否可以编辑
    help_text           Admin中该字段的提示信息
    choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                        如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
 
    error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                        如:{'null': "不能为空.", 'invalid': '格式错误'}
 
    validators          自定义错误验证(列表类型),从而定制想要的验证规则
                        from django.core.validators import RegexValidator
                        from django.core.validators import EmailValidator,URLValidator,DecimalValidator,
                        MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                        如:
                            test = models.CharField(
                                max_length=32,
                                error_messages={
                                    'c1': '优先错信息1',
                                    'c2': '优先错信息2',
                                    'c3': '优先错信息3',},
                                validators=[
                                    RegexValidator(regex='root_d+', message='错误了', code='c1'),
                                    RegexValidator(regex='root_112233d+', message='又错误了', code='c2'),
                                    EmailValidator(message='又错误了', code='c3'), ] )

5. Model Meta参数

class UserInfo(models.Model):
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32)
    class Meta:
        # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
        db_table = "table_name"
 
        # admin中显示的表名称
        verbose_name = '个人信息'
 
        # verbose_name加s,admin中显示改变
        verbose_name_plural = '所有用户信息'
 
        # 联合索引 
        index_together = [
            ("pub_date", "deadline"),   # 应为两个存在的字段
        ]
 
        # 联合唯一索引
        unique_together = (("driver", "restaurant"),)   # 应为两个存在的字段
     
    ordering=(‘id’)  #默认查询对象的排序

6. 时间字段

DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。

  • auto_now_add:配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
  • auto_now:配置auto_now=True,每次更新数据记录的时候会更新该字段。

三  单表操作

1. 基本查询

<1> all():                 查询所有结果,返回queryset对象
 
<2> filter(**kwargs):      它包含了与所给筛选条件相匹配的对象,返回queryset对象
 
<3> get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个(默认返回最后一个元素)或者没有都会抛出错误。返回queryset元素
 
<4> exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象,返回queryset对象
 
<5> values(*field):        返回一个可迭代的字典序列
 
<6> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,只有值,没有字段的名称

<7> order_by(*field): 对查询结果排序,返回queryset对象;例如orser_by(-id),降序 <8> reverse(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。 <9> distinct(): 从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。) <10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。 <11> first(): 返回第一条记录,返回queryset元素,为空返回None,[0]为空报错 <12> last(): 返回最后一条记录,返回queryset元素,为空返回None <13> exists(): 如果QuerySet包含数据,就返回True,否则返回False

2. 双下划线查询

models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
(id__gte=1),(id__lte=1)               #大于等于,小于等于
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的 models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and 类似的还有:startswith,istartswith, endswith, iendswith  date字段还可以: models.Class.objects.filter(first_day__year=2017)

3. 增删改查

#
1.  object.creat() models.Tb1.objects.create(c1='xx', c2='oo') # 增加一条数据,可以接受字典类型数据 **kwargs, 2.  实例化
obj = models.Tb1(c1='xx', c2='oo') obj.save() # models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议) models.Tb1.objects.all() # 获取全部 models.Tb1.objects.filter(name='seven') # 获取指定条件的数据 # models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据 # models.Tb1.objects.filter(name='seven').update(gender='0') # 将指定条件的数据更新,均支持 **kwargs
obj = models.Tb1.objects.get(id=1) obj.c1 = '111' obj.save() # 修改整条数据

四  OneToOneField

用途不多,一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系。

class Author(models.Model):
    name = models.CharField(max_length=32)
    info = models.OneToOneField(to='AuthorInfo')

class AuthorInfo(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()

五  ForeignKey

一对多的连表就是使用的foreignkey关键字,一定标明外键关系名称的起始终止方向,设置的时候保持关系名称与被指向数据库同名。

一般是在“多”中建立外键,生成管理对象。

class Publisher(models.Model):
    name = models.CharField(max_length=32)  # 出版社名称
class Book(models.Model):
    title = models.CharField(max_length=30)  # 书名
    price = models.IntegerField()  # 价格
    publisher = models.ForeignKey(to='Publisher', related_name='books')

QL语句中可以使用连表查询,外键的用途不是特别广,主要起到约束的作用;但在ORM中,只能通过外键进行连表,作用明显

create table userinfo(
          uid int auto_increment primary key,
          name varchar(32),
          department_id int,
          constraint fk_user_depar foreign key (department_id) references department(id)
         )engine=innodb default charset=utf8;

1. 字段参数

ForeignKey(ForeignObject) # ForeignObject(RelatedField)
    to,                 # 要进行关联的表名
    to_field=None,      # 要关联的表中的字段名称
    on_delete=None,     # 当删除关联表中的数据时,当前表与其关联的行的行为
                        - 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(可执行对象)
                                    def func():
                                        return 10
                                    class MyModel(models.Model):
                                        user = models.ForeignKey(
                                            to="User",
                                            to_field="id"
                                            on_delete=models.SET(func),)
    related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
    related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
    limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
                                # 如:
                        - limit_choices_to={'nid__gt': 5}
                        - limit_choices_to=lambda : {'nid__gt': 5}
 
                        from django.db.models import Q
                        - limit_choices_to=Q(nid__gt=10)
                        - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                        - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
    db_constraint=True          # 是否在数据库中创建外键约束
    parent_link=False           # 在Admin中是否显示关联数据
 
 
OneToOneField(ForeignKey)
    to,                 # 要进行关联的表名
    to_field=None       # 要关联的表中的字段名称
    on_delete=None,     # 当删除关联表中的数据时,当前表与其关联的行的行为
 
                        ###### 对于一对一 ######
                        # 1. 一对一其实就是 一对多 + 唯一索引
                        # 2.当两个类之间有继承关系时,默认会创建一个一对一字段
                        # 如下会在A表中额外增加一个c_ptr_id列且唯一:
                                class C(models.Model):
                                    nid = models.AutoField(primary_key=True)
                                    part = models.CharField(max_length=12)
 
                                class A(C):
                                    id = models.AutoField(primary_key=True)
                                    code = models.CharField(max_length=1)
 
ManyToManyField(RelatedField)
    to,                         # 要进行关联的表名
    related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
    related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
    limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
                                # 如:
                                    - limit_choices_to={'nid__gt': 5}
                                    - limit_choices_to=lambda : {'nid__gt': 5}
 
                                    from django.db.models import Q
                                    - limit_choices_to=Q(nid__gt=10)
                                    - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                                    - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
    symmetrical=None,           # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
                                # 做如下操作时,不同的symmetrical会有不同的可选字段
                                    models.BB.objects.filter(...)
 
                                    # 可选字段有:code, id, m1
                                        class BB(models.Model):
 
                                        code = models.CharField(max_length=12)
                                        m1 = models.ManyToManyField('self',symmetrical=True)
 
                                    # 可选字段有: bb, code, id, m1
                                        class BB(models.Model):
 
                                        code = models.CharField(max_length=12)
                                        m1 = models.ManyToManyField('self',symmetrical=False)
 
    through=None,               # 自定义第三张表时,使用字段用于指定关系表
    through_fields=None,        # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
                                    from django.db import models
 
                                    class Person(models.Model):
                                        name = models.CharField(max_length=50)
 
                                    class Group(models.Model):
                                        name = models.CharField(max_length=128)
                                        members = models.ManyToManyField(
                                            Person,
                                            through='Membership',
                                            through_fields=('group', 'person'),
                                        )
 
                                    class Membership(models.Model):
                                        group = models.ForeignKey(Group, on_delete=models.CASCADE)
                                        person = models.ForeignKey(Person, on_delete=models.CASCADE)
                                        inviter = models.ForeignKey(
                                            Person,
                                            on_delete=models.CASCADE,
                                            related_name="membership_invites",
                                        )
                                        invite_reason = models.CharField(max_length=64)
    db_constraint=True,         # 是否在数据库中创建外键约束
    db_table=None,              # 默认创建第三张表时,数据库中表的名称

2.  正向查询

2.1 对象查询

book_obj = models.Book.objects.first()  # 第一本书对象
print(book_obj.publisher)  # 得到这本书关联的出版社对象
print(book_obj.publisher.name)  # 得到出版社对象的名称

结果为:多次查询

SELECT `mainApp_book`.`id`, `mainApp_book`.`title`, `mainApp_book`.`price`, `mainApp_book`.`publisher_id` FROM `mainApp_book` ORDER BY `mainApp_book`.`id` ASC  LIMIT 1; args=()
中国地理出版社
SELECT `mainApp_publisher`.`id`, `mainApp_publisher`.`name` FROM `mainApp_publisher` WHERE `mainApp_publisher`.`id` = 1; args=(1,)

2.2 字段查询

print(models.Book.objects.values_list("publisher__name"))    #双下滑线标明是连表查询

结果为:连表查询

(0.000) SELECT `mainApp_publisher`.`name` FROM `mainApp_book` INNER JOIN `mainApp_publisher` ON (`mainApp_book`.`publisher_id` = `mainApp_publisher`.`id`)  LIMIT 21; args=()
<QuerySet [('中国地理出版社',)]>

3.  反向查询

反向关系名称:

  • tablename_set
  • tablename·_字段
  • relate_name设置

3.1 对象查询

publisher_obj = models.Publisher.objects.first()  # 找到第一个出版社对象
books = publisher_obj.book_set.all()  # 找到第一个出版社出版的所有书
print(books.values_list("title") )  # 找到第一个出版社出版的所有书的书名
# 或者
publisher_obj = models.Publisher.objects.first()  # 找到第一个出版社对象
book = publisher_obj.books.all()  # 找到第一个出版社出版的所有书
print(book.values_list("title") )  # 找到第一个出版社出版的所有书的书

3.2 字段查询

titles = models.Publisher.objects.values_list("book__title")
#或者
titles = models.Publisher.objects.values_list("books__title")

4 方法

create():与单表操作一致

# object.create()方法
models.Book.objects.create(title='书摩纳哥', price=12.36, publisher_id=1)
# 实例化
obj = models.Book(title='肖申克的救赎', price=15, publisher_id=1)
obj.save()

add()、set()、remove()、clear()都是针对管理对象进行的操作,都是对“一”进行的添加“多”。

add():必要时,参数需要加*打散

    obj = models.Publisher.objects.last()
    obj.books.add(models.Book.objects.filter(id=2).first())

set():参数为可迭代对象

    obj = models.Publisher.objects.last()
    obj.books.set([models.Book.objects.filter(id=1).first()])

remove():ForeignKey字段没设置null=True时才生效

    obj = models.Publisher.objects.last()
    obj.books.remove(models.Book.objects.filter(id=1).first())

clear():ForeignKey字段没设置null=True时才生效

    obj = models.Publisher.objects.last()
    obj.books.clear()

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

六  ManyToManyField

class Book(models.Model):
    title = models.CharField(max_length=30)  # 书名
    price = models.IntegerField()  # 价格
    publisher = models.ForeignKey(to='Publisher', null=True,
                                  related_name='books', on_delete=models.CASCADE)
    def __str__(self):
        return '<Book %s-%s>' % (self.id, self.title)

class Author(models.Model):
    name = models.CharField(max_length=32)  # 作者名字
    book2author = models.ManyToManyField(to='Book', related_name='author2book')

1. 多对多关联关系的三种方式 

1.1 自行创建第三张表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")

class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")

# 自己创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")

    class Meta:
        unique_together = ("author", "book")

1.2 通过ManyToManyField自动创建第三张表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


# 通过ORM自带的ManyToManyField自动创建第三张表
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    books = models.ManyToManyField(to="Book", related_name="authors")

1.3 设置ManyTomanyField并指定自行创建的第三张表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


# 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
    # through_fields接受一个2元组('field1','field2'):
    # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。

class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")

    class Meta:
        unique_together = ("author", "book")

注意:

当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式。但是当我们使用第三种方式系时,就无法使用set、add、remove、clear方法,需要通过第三张表的model来管理多对多关系。

2. 参数字段

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

举个例子:

class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self")

此时,person对象就没有person_set属性。

class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self", symmetrical=False)

此时,person对象现在就可以使用person_set属性进行反向查询。

through

在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。

但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。

through_fields

设置关联的字段。

db_table

默认创建第三张表时,数据库中表的名称。

3. 方法

create()

在原表基础上创建

models.Author.objects.create(name='tony')

在管理对象表创建:创建一个新的对象,并将它添加到关联对象集之中。foreignkey没有此方法。

 models.Book.objects.first().book2author.create(name='lucy')

add()

把指定的model对象添加到关联对象集中。在原来的关系基础上添加

    # 添加id
    models.Book.objects.first().book2author.add(1, 3)
    # 添加对象
    obj = models.Author.objects.filter(id__in=[1, 2, 3, 4])
    models.Book.objects.first().book2author.add(*obj)

set()

更新model对象的关联对象。先清除关系,再添加

    # 设置id
    models.Book.objects.first().book2author.set([1, 3])
    # 设置对象
    obj = models.Author.objects.filter(id__in=[1, 2, 3, 4])
    models.Book.objects.first().book2author.set(obj)

因为原数据表中的每一条数据不一定存在关系,不一定存在于第三张表中,即null默认为True,所以remove和clear能够使用

remove()

从关联对象集中移除执行的model对象

    # 清除id
    models.Book.objects.first().book2author.remove(1, 3)
    # 清除对象
    obj = models.Author.objects.filter(id__in=[1, 2, 3, 4])
    models.Book.objects.first().book2author.remove(*obj)

clear()

从关联对象集中移除一切对象。

models.Book.objects.first().book2author.clear()

七 聚合

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
用到的内置函数:from django.db.models import Avg, Sum, Max, Min, Count

from django.db.models import Avg, Sum, Max, Min, Count
book_obj
=models.Book.objects.all() print(book_obj.aggregate(Max("price"))) #{'price__max': Decimal('9999.99')}
#指定一个名称 print(book_obj.aggregate(max_price=Max("price"))) #{'max_price': Decimal('9999.99')}
#生成多个聚合 print(book_obj.aggregate(Max("price"),Min("price"),Avg("price"))) #{'price__max': Decimal('9999.99'), 'price__min': Decimal('10.00'), 'price__avg': 1507.141429}

八 分组

#单表查询
select dept,AVG(salary) from employee group by dept; from django.db.models import Avg model.Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")

和sql对比:

  • annotate前面的values值相当于group by的字段,默认为一组
  • values('dept')、(a=Avg("salary"))内部如是跨表查询就需要使用双下划线
  • annotate后面的values 是select的字段
  • annotate前面是queryset对象;annotate()返回值依然是queryset对象

# 多表查询
select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id; from django.db.models import Avg models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

 更多实例:

1.ORM中values或者values_list中的字段,相当于select字段
 ORM分组查询 每个部门名称及部门的平均年龄
ret = models.Employee.objects.all().values("dept", "age")
SQL语句
"""
SELECT `employee`.`dept`, `employee`.`age` FROM `employee` LIMIT 21; args=()
"""
2.ORM中 annotate 前面values()是group_by()字段 from django.db.models import Avg ret = models.Employee.objects.values("province").annotate(a=Avg("salary")).values("province", "a") 相当于: SELECT `employee`.`province`, AVG(`employee`.`salary`) AS `a` FROM `employee` GROUP BY `employee`.`province` ORDER BY NULL LIMIT 21; args=()
3. ORM跨表分组查询,queryset对象跨表查询时使用双下划线 ret = models.Person.objects.values("dept_id").annotate(a=Avg("salary")).values("dept__name", "a") """ SELECT `dept`.`name`, AVG(`person`.`salary`) AS `a` FROM `person` INNER JOIN `dept` ON (`person`.`dept_id` = `dept`.`id`) GROUP BY `person`.`dept_id`, `dept`.`name` ORDER BY NULL LIMIT 21; args=() """
4.查询每一个部门的名称和人数 #正向查询: models.emp.objects.values("dept_id").annotate(c=Count("name")).values("dept__name","c") #反向查询 models.dep.objects.values("name").annotate(c=Count("emp__name")).values("name","c") SQL: select dep_name Count(emp.name) from emp inner join dep on .... group by dep_id 5.查询每一个作者的名字及出版过的书籍的最高价 models.Author.objects.values('id').annotate(c=Max('book__price')).values("name","c")
6.查询每一本书作者的个数 models.Book.objects.values("id").annotate(c=Count("authors__name")).values("title","c") ret=models.Book.objects.annotate(author_num=Count("author")) for book in ret: print("书名:{},作者数量:{}".format(book.title, book.author_num))
7.统计不止一个作者的图书(过滤完后显示)
models.Book.objects.values("title").annotate(c=Count("authors__name")).filter(c__gt=1).values('name',"c") 8.查询各个作者出的书的总价格
ret = models.Author.objects.annotate(price_sum=Sum("books__price"))
for i in ret:
   print(i, i.name, i.price_sum)

九  F查询

F查询就是取表中的动态变量

查询评论数大于收藏数的书籍

from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

models.Book.objects.filter(commnet_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(")")))

十 Q查询

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

Q的导入

from django.db.models import Q

示例1:

查询作者名是小仙女或小魔女的

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: 橘子物语>]>

Q的进阶用法:

    q = Q()
    q.connector = 'or'  # 修改查询条件的关系   默认是and
    q.children.append(('title__contains','三国演义'))  # 往列表中添加筛选条件
    q.children.append(('price__gt',444))  # 往列表中添加筛选条件
    res = models.Book.objects.filter(q)  # filter支持你直接传q对象  但是默认还是and关系
    print(res)

十一 Django终端打印SQL语句

在Django项目的settings.py文件中,在最后复制粘贴如下代码:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

十二 在Python脚本中调用Django环境

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

    from app01 import models

    books = models.Book.objects.all()
    print(books)

 参考:

ORM大汇总:https://www.cnblogs.com/sss4/p/7070942.html

分组查询详解:https://www.cnblogs.com/zgf-666/p/9119126.html

原文地址:https://www.cnblogs.com/mushuiyishan/p/11574794.html