Django之ORM

 

阅读目录

 

对应关系

 映射关系:
       python的类名对应的SQL语句的表名
       python的类属性对应的SQL语句的表名下的字段
       python的类属性的约束对应的SQL语句的表名下的字段类型
                                     
       类的实例对象---------------表中的一条记录对象

 

创建表

        class Student(models.Model):
                nid=models.AutoField(primary_key=True)         #  主键约束
                name=models.CharField(max_length=32)           #  字符串字段
                birth=models.DateField()                       #  日期类型
                class_id=models.IntegerField(default=0)      #  整数类型

外键创建

#多对一,放在多的一边
publish=models.ForeignKey(to="Publish",to_field="nid")

#多对多,自动生成第三张表,放在任意一边
authors=models.ManyToManyField(to='Author') 

#一对一,放在常用的一边
authorDetail=models.OneToOneField(to="AuthorDetail")

字段类型详细

使用时需要引入django.db.models包,字段类型如下:

AutoField:自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性。

BooleanField:布尔字段,值为True或False。

NullBooleanField:支持Null、True、False三种值。

CharField(max_length=字符长度):字符串。TextField:大文本字段,一般超过4000个字符时使用。
参数max_length表示最大字符个数。

IntegerField:整数。

DecimalField(max_digits=None, decimal_places=None):十进制浮点数。FloatField:浮点数。
参数max_digits表示总位数。
参数decimal_places表示小数位数。

DateField[auto_now=False, auto_now_add=False]):日期。

TimeField:时间,参数同DateField。
参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false。
参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false。
参数auto_now_add和auto_now是相互排斥的,组合将会发生错误。

DateTimeField:日期时间,参数同DateField。

FileField:上传文件字段。

ImageField:继承于FileField,对上传的内容进行校验,确保是有效的图片。
View Code

字段选项

每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR数据库字段的大小。还有一些适用于所有字段的通用参数。 

这些参数在文档中有详细定义,这里我们只简单介绍一些最常用的:

(1)null

如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.

(1)blank

如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。

(2)default

字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。

(3)primary_key

如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。

(4)unique

如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的

(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

这是一个关于 choices 列表的例子:

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)
每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。 在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如:

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)


>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
View Code

字段补充

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

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

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    自定义无符号整数字段

        class UnsignedIntegerField(models.IntegerField):
            def db_type(self, connection):
                return 'integer UNSIGNED'

        PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
            '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)',

    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)
        - 二进制类型
字段

 

补充:

ForeignKey.on_delete

 

ForeignKey.on_delete¶
当一个ForeignKey 引用的对象被删除时,Django 默认模拟SQL 的ON DELETE CASCADE 的约束行为,并且删除包含该ForeignKey的对象。这种行为可以通过设置on_delete 参数来改变。例如,如果你有一个可以为空的ForeignKey,在其引用的对象被删除的时你想把这个ForeignKey 设置为空:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
on_delete 在django.db.models中可以找到的值有:

CASCADE¶
级联删除;默认值。

PROTECT¶
抛出ProtectedError 以阻止被引用对象的删除,它是django.db.IntegrityError 的一个子类。

SET_NULL¶
把ForeignKey 设置为null; null 参数为True 时才可以这样做。

SET_DEFAULT¶
ForeignKey 值设置成它的默认值;此时必须设置ForeignKey 的default 参数。

SET()¶
设置ForeignKey 为传递给SET() 的值,如果传递的是一个可调用对象,则为调用后的结果。在大部分情形下,传递一个可调用对象用于避免models.py 在导入时执行查询:

from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models

def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.SET(get_sentinel_user))
DO_NOTHING¶
Take no action. 如果你的数据库后端强制引用完整性,它将引发一个IntegrityError ,除非你手动添加一个ON DELETE 约束给数据库自动(可能要用到初始化的SQL)。
related_name   #反向查询的别名,就可以代替 表名_set

 

limit_choices_to={}  #用于选择时option只显示按条件过滤后的选项
#示例
limit_choices_to={'depart_id__in':[1008,1009]}
limit_choices_to={'depart_id':1001,}

auto_now 与 auto_now_add

DateTimeField.auto_now

这个参数的默认值为false,设置为true时,能够在保存该字段时,将其值设置为当前时间,并且每次修改model,都会自动更新。因此这个参数在需要存储“最后修改时间”的场景下,十分方便。需要注意的是,设置该参数为true时,并不简单地意味着字段的默认值为当前时间,而是指字段会被“强制”更新到当前时间,你无法程序中手动为字段赋值;如果使用django再带的admin管理器,那么该字段在admin中是只读的。
DateTimeField.auto_now_add

这个参数的默认值也为False,设置为True时,会在model对象第一次被创建时,将字段的值设置为创建时的时间,以后修改对象时,字段的值不会再更新。该属性通常被用在存储“创建时间”的场景下。与auto_now类似,auto_now_add也具有强制性,一旦被设置为True,就无法在程序中手动为字段赋值,在admin中字段也会成为只读的。



auto_now_add = True用于创建时间

auto_now = True用于更新时间
db_index = True 表示设置索引
unique(唯一的意思) = True 设置唯一索引

联合唯一
class Meta:
unique_together = (
 ('email','ctime'),
)
联合索引(不做限制)
index_together = (
('email','ctime'),
)
    class Meta:
        verbose_name = "课程大类"  #人类可读对象名
        verbose_name_plural = "课程大类" #人类可读对象名复数

多级表关系以及参数

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,              # 默认创建第三张表时,数据库中表的名称

  

 

 

 

更多详见模型字段参考

一旦你建立好数据模型之后,django会自动生成一套数据库抽象的API,可以让你执行关于表记录的增删改查的操作。

创建命令

python manage.py makemigrations  #生成文件

python manage.py migrate   #创建表

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',
        },
    }
}
View Code

 

 回到顶部

单表操作

    查询记录API:

           (1)Student.objects.all()    #返回的QuerySet类型 查询所有记录    [obj1,obj2....] 
           
           (2)Student.objects.filter() #返回的QuerySet类型 查询所有符合条件的记录

           (3)Student.objects.exclude()#返回的QuerySet类型 查询所有不符合条件的记录

           (4)Student.objects.get()    #返回的models对象   查询结果必须有且只有一个,否则报错

           (5)Student.objects.all().first()      #返回的models对象   查询结果集合中的第一个

           (6)Student.objects.filter().last()    #返回的models对象   查询结果集合中的最后一个

           (7)Student.objects.all().values("name","class_id")  #返回的QuerySet类型  ,列表中存放的字典

           (8)Student.objects.all().values_list("name","class_id")  #返回的QuerySet类型  ,列表中存放的元组

           (9)Student.objects.all().order_by("class_id")   # 按指定字段排序,不指定,按主键排序

           (9.1)Student.objects.all().order_by("-class_id")   # 按指定字段 降序 排序

           (10)Student.objects.all().count()  # 返回的记录个数  数字类型

           (11)Student.objects.all().values("name").distinct()  #字段去重,用在values() 后

           (12)Student.objects.all().exist()  #判断是否为空,如果QuerySet包含数据,就返回True,否则返回False 原理是limit 1
       
单表查询之双下划线 __ Student.objects.filter() models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
       lt 小于 gt大于
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") #包含ven 区分大小写 models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and 什么到什么之间(包含最后一个) startswith, #什么开头 istartswith, #什么开头,大小写不敏感 endswith, #什么结尾 iendswith #什么结尾,大小写不敏感 添加记录:
方式1:
          s
=Student(name='',birth='',class_id='')     s.save()
      方式2: stu_obj
=Student.objects.create(name='',birth='',class_id='') # stu_obj是添加的记录对象 删除记录: Student.objects.filter(nid=1).delete() # QuerySet类型调用 修改记录: Student.objects.filter(nid=1).update(name="yuan") # QuerySet类型调用

补充:

only 和 defer

				user_list = models.User.objects.all()
				# 相当于SQL语句 select * from user
				# user_list = QuerySet() = [obj(*),obj(*),obj(*) ]
				
				
				user_list = models.User.objects.all().only('id','name')
				# 只查询id,name这两个字段,不取所有

				# select id,name from user 
				# user_list = QuerySet() = [obj(id,name),obj(id,name),obj(id,name) ]
				# 如果取id,name之外的字段就会再自动走一遍查询,会降低效率
				# for item in user_list:
				# 	item.id
				# 	item.age  重新走数据库

				user_list = models.User.objects.all().defer('id','name')
				# 和only相反,不取这几个字段
					
				user_list = models.User.objects.all().vlaues('id','name')
				# select id,name from user 
				# user_list = QuerySet() = [{'id':1,'name':'老男孩'}, ]
				# for item in user_list:
				# 	item['id']
				# 	item['name']

  

 

 回到顶部

添加表记录

普通字段

方式1
publish_obj=Publish(name="人民出版社",city="北京",email="renMin@163.com")
publish_obj.save() # 将数据保存到数据库

方式2
publish_obj=Publish.objects.create(name="人民出版社",city="北京",email="renMin@163.com")

方式3
表.objects.create(**request.POST.dict())

外键字段

方式1:
   publish_obj=Publish.objects.get(nid=1)
   Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish=publish_obj)
 
方式2:
   Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish_id=1)  

多对多字段

book_obj=Book.objects.create(title="追风筝的人",publishDate="2012-11-12",price=69,pageNum=314,publish_id=1)
 
author_yuan=Author.objects.create(name="yuan",age=23,authorDetail_id=1)
author_egon=Author.objects.create(name="egon",age=32,authorDetail_id=2)
 
book_obj.authors.add(author_egon,author_yuan)    #  将某个特定的 model 对象添加到被关联对象集合中。   =======    book_obj.authors.add(*[])
 
book_obj.authors.add(1,2)   #可以直接放作者对象的id

book_obj.authors.create()      #创建并保存一个新对象,然后将这个对象加被关联对象的集合中,然后返回这个新对象。

解除关系

book_obj.authors.remove()     # 将某个特定的对象从被关联对象集合中去除。    ======   book_obj.authors.remove(*[])
book_obj.authors.clear()       #清空被关联对象集合。
book_obj.authors.set() #先清空再设置

 

 回到顶部

基于对象的跨表查询:   子查询

一对多查询

# 查询nid=1的书籍的出版社所在的城市
book_obj=Book.objects.get(nid=1)
print(book_obj.publish.city) # book_obj.publish 是nid=1的书籍对象关联的出版社对象 

反向查询:(按表名book_set)

# 查询 人民出版社出版过的所有书籍
 
    publish=Publish.objects.get(name="人民出版社")
 
    book_list=publish.book_set.all()  # 与人民出版社关联的所有书籍对象集合
 
    for book_obj in book_list:
        print(book_obj.title)

一对一查询

正向查询(按字段:authorDetail):

# 查询egon作者的手机号
 
    author_egon=Author.objects.get(name="egon")
    print(author_egon.authorDetail.telephone)

反向查询(按表名:author):

# 查询所有住址在北京的作者的姓名
 
    authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
 
    for obj in authorDetail_list:
        print(obj.author.name)

多对多查询

正向查询 (按字段)

# 金瓶眉所有作者的名字以及手机号
 
    book_obj=Book.objects.filter(title="金瓶眉").first()
 
    authors=book_obj.authors.all()
 
    for author_obj in authors:
 
        print(author_obj.name,author_obj.authorDetail.telephone)

反向查询(按表名:book_set)

# 查询egon出过的所有书籍的名字
 
    author_obj=Author.objects.get(name="egon")
    book_list=author_obj.book_set.all() #与egon作者相关的所有书籍
 
    for book_obj in book_list:
        print(book_obj.title)

 

 回到顶部

基于双下划线的跨表查询:  join查询

关键点:正向查询按字段,反向查询按表明。

一对多

        查询活着的出版社名称
        ret=Book.objects.filter(title="金瓶没").values("publish__name") # <QuerySet [{'publish__name': '沙河出版社'}]>
        print(ret)

        Publish.objects.filter(book__title="金瓶没").values("name")

        查询沙河出版社出版过的书籍名称
        ret=Publish.objects.filter(name="人民出版社").values("book__title")
        print(ret)
        Book.objects.filter(publish__name="人民出版社").values("title")

多对多

        查询活着3所有作者的名字
        ret=Book.objects.filter(title="金瓶没4").values_list("authors__name")
        print(ret)
        查询alex出版过的所有书籍
        ret=Author.objects.filter(name="alex").values("book__title")
        print(ret)

        Book.objects.filter(authors__name="alex").values("title")

一对一

        查询地址在烟台并且email是789的作者的名字
        ret=AuthorDetail.objects.filter(addr="烟台",email=789).values("author__name")
        print(ret)

         email以456开头的作者出版过的所有书籍名称以及出版社名称

多联

        email以456开头的作者出版过的所有书籍名称以及出版社名称

        ret=Book.objects.filter(authors__authordetail__email__startswith="456").values("title","publish__name")
        print(ret)

 

 回到顶部

聚合查询与分组查询

导入

   from django.db.models import Avg,Sum,Count,Min,Max

聚合:aggregate(*args, **kwargs)

# 计算所有图书的平均价格
    >>> from django.db.models import Avg
    >>> Book.objects.all().aggregate(Avg('price'))
    {'price__avg': 34.35}

aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。

键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

分组:annotate() 

为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。

annotate的返回值是querySet,如果不想遍历对象,可以用上valuelist:

    查询每一个出版社出版过的书籍个数
    ret=Publish.objects.all().annotate(c=Count("book__title"))
    for pub_obj  in ret:
        print(pub_obj.name,pub_obj.c)

    查询每一本书的作者个数

    ret=Book.objects.all().annotate(counts=Count("authors__id")).values("title","counts")
    print(ret)

    查询每一个作者出版过的书籍的平均价格

    ret=Author.objects.all().annotate(avgprice=Avg("book__price")).values("name","avgprice")
    print(ret)


查询各个作者出的书的总价格:

# 按author表的所有字段 group by
    queryResult=Author.objects
              .annotate(SumPrice=Sum("book__price"))
              .values_list("name","SumPrice")
    print(queryResult)
    
# 按authors__name group by
    queryResult2=Book.objects.values("authors__name")
              .annotate(SumPrice=Sum("price"))
              .values_list("authors__name","SumPrice")
    print(queryResult2)


统计每一本以py开头的书籍的作者个数:
 queryResult=Book.objects.filter(title__startswith="Py").annotate(num_authors=Count('authors'))

统计不止一个作者的图书:
queryResult=Book.objects
          .annotate(num_authors=Count('authors'))
          .filter(num_authors__gt=1)

根据一本图书作者数量的多少对查询集 QuerySet进行排序:
Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

 

回到顶部

F 与 Q 查询

F查询 (可对字段的值进行操作,int类型)

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

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

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

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

# 查询评论数大于收藏数2倍的书籍
    Book.objects.filter(commnetNum__lt=F('keepNum')*2)

修改操作也可以使用F函数,比如将每一本书的价格提高30元:

Book.objects.all().update(price=F("price")+30) 

Q 查询( "|"  "&"  "~" )

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

from django.db.models import Q
Q(title__startswith='Py')

Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

等同于下面的SQL WHERE 子句:

WHERE name ="yuan" OR name ="egon"

你可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:

bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:

    bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                                  title__icontains="python"
                                 )

补充用法:

from django.db.models import Q

condition = Q()
condition.connector = "or" #默认是and关系

condition.children.append(("title","linux"))
condition.children.append(("price",100))
#Book.objects.filter(condition) 相当于 Book.objects.filter(Q(title="linux")|Q(price=100))

  

ContentType

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey,GenericRelation

# Create your models here.
class Course(models.Model):
    """
    普通课程
    """
    title = models.CharField(max_length=32)
    # 仅用于反向查找,不会生成真实的字段
    pricepolicy_list = GenericRelation(to="PricePolicy")


class DegreeCourse(models.Model):
    """
    学位课程
    """
    title = models.CharField(max_length=32)

    # 仅用于反向查找,不会生成真实的字段
    pricepolicy_list = GenericRelation(to="PricePolicy")



class PricePolicy(models.Model):
    """
    价格策略
    """
    price = models.IntegerField()
    period = models.IntegerField()

    content_type = models.ForeignKey(ContentType,verbose_name="关联的表名称")
    object_id = models.IntegerField(verbose_name="关联的表中的数据的行ID")
    # 帮助你快速实现content_type操作,不会生成真实的字段
    content_obj = GenericForeignKey('content_type',"object_id")



# 会自动生成一张ContentType表,里面存着所有的表名称和对应的app名
# 插入数据的两种方法
# 方法一:
#插入表id和数据id
# cobj = ContentType.objects.first(model="course").first() #找到对应的表
# obj = Course.objects.filter(title="python全栈").first() # 找到表里的对象
# PricePolicy.objects.create(price=99,period=30,content_type_id=cobj.id,object_id=obj.id)
#
# 方法二:
#直接让content_obj等于一个数据对象
# PricePolicy.objects.create(price=99,period=30,content_obj=obj)

#查询
# 3. 根据课程ID获取课程, 并获取该课程的所有价格策略
course = models.Course.objects.filter(id=1).first()

price_policys = course.pricepolicy_list.all().values("price","period","content_type__model","object_id").distinct()

print(price_policys)

  

与性能相关

注意:
    使用Foreignkey,原因:
        - 约束
        - 节省硬盘
    大型程序:
        FK,不用原因:
        - 约束-->     自己代码判断
        - 节省硬盘--> 可以浪费,因为要加速
链表查询速度会慢

1. select_related  主动进行链表查询

问题:查看所有用户,并打印用户姓名、年龄、用户所在部门名称。

会有11次查询:
	users = 用户表.objects.all()
	for row in users:
		row.name
		row.age
		row.dp.title #每执行一次链表查询一次
		
	print(users.query) # 查看查询语句  select id,name,age,e,wpd from xxx

解决方法一: 使用values
	users = 用户表.objects.all().values('name','age','dp__title')
	for row in users:
		row['name']
		row['age']
		row['dp__title']
解决方法二:使用select_related 指定链表
	users = 用户表.objects.all().select_related('dp')
	for row in users:
		row.name
		row.age
		row.dp.title
	print(users.query) # select id,name,age,e,wpd from 用户表 join 部门表

2. prefetch_related  两次单表查询

2次单表查询	
	select * from users;
	# 将所有部门ID获取到:[1,2,3,4,8]
        # 再去部门表查询相应的部门
	select * from department where id in [1,2,3,4,8]

	users = 用户表.objects.all().prefetch_related('dp')
	for row in users:
		row.name
		row.age
		row.dp.title

  

执行原生sql语句

1. connection, connections

        from django.db import connection, connections

        cursor = connections["default"].cursor()
        cursor.execute("""select * from api_userinfo WHERE username=%s""",["zhou"])
        row = cursor.fetchall()
        print(row)  #[(1, '周军豪', 'zhou', '123', None)]
        return HttpResponse("ok")
    ################### 原生SQL ###################
    from django.db import connection, connections
    cursor = connection.cursor()  # cursor = connections['default'].cursor()
    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    row = cursor.fetchone() # fetchall()/fetchmany(..)

  

2. raw

def raw(self, raw_query, params=None, translations=None, using=None):
    # 执行原生SQL
    models.UserInfo.objects.raw('select * from userinfo')

    # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
    models.UserInfo.objects.raw('select id as nid from 其他表')

    # 为原生SQL设置参数
    models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

    # 将获取的到列名转换为指定列名
    name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
    Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

    # 指定数据库
    models.UserInfo.objects.raw('select * from userinfo', using="default")

3. extra

        # mysql 用date_format(date,format)  sqllist用strftime(format,date)
        c = models.Comment.objects.extra(select={'comment_data':"strftime('%%Y-%%m-%%d %%H:%%M:%%S',date)"}).values("comment_data")
        x = UserInfo.objects.extra(select={'new_id': "select username from api_userinfo where api_userinfo.id > %s"}, select_params=(1,)).values()
        y = UserInfo.objects.extra(where=['id=%s'], params=[1,]).values()
        z = UserInfo.objects.extra(where=["username='zhou' OR username = 'yu'", "name = '周军豪'"]).values()
原文地址:https://www.cnblogs.com/zhoujunhao/p/7999590.html