• 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()
  • 相关阅读:
    Windows Server 2008 R2 下配置证书服务器和HTTPS方式访问网站
    C# AD(Active Directory)域信息同步,组织单位、用户等信息查询
    Windows Server 2008 R2 配置Exchange 2010邮件服务器并使用EWS发送邮件
    体验vs11 Beta
    jQuery Gallery Plugin在Asp.Net中使用
    第一个Python程序——博客自动访问脚本
    网盘:不仅仅是存储
    TCP/UDP端口列表
    Linux的时间 HZ,Tick,Jiffies
    Intel Data Plane Development Kit(DPDK) 1.2.3特性介绍
  • 原文地址:https://www.cnblogs.com/zhoujunhao/p/7999590.html
Copyright © 2020-2023  润新知