Django 模型层

ORM简介

MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动。

ORM是“对象-关系-映射”的简称。

ORM概念

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

简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

ORM在业务逻辑层和数据库层之间充当了桥梁的作用。

ORM由来

让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。

几乎所有的软件开发过程中都会涉及到对象和关系数据库。在用户层面和业务逻辑层面,我们是面向对象的。当对象的信息发生变化的时候,我们就需要把对象的信息保存在关系数据库中。

按照之前的方式来进行开发就会出现程序员会在自己的业务逻辑代码中夹杂很多SQL语句用来增加、读取、修改、删除相关数据,而这些代码通常都是极其相似或者重复的。

ORM的优势

ORM解决的主要问题是对象和关系的映射。它通常将一个类和一张表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。 

ORM提供了对数据库的映射,不用直接编写SQL代码,只需操作对象就能对数据库操作数据。

让软件开发人员专注于业务逻辑的处理,提高了开发效率。

ORM的劣势

ORM的缺点是会在一定程度上牺牲程序的执行效率。

ORM的操作是有限的,也就是ORM定义好的操作是可以完成的,一些复杂的查询操作是完成不了。

ORM用多了SQL语句就不会写了,关系数据库相关技能退化...

ORM总结

ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。

但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。

但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。

字段与参数

每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR数据库字段的大小。还有一些适用于所有字段的通用参数。 这些参数在文档中有详细定义,这里我们只简单介绍一些最常用的:

Model

在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表。

基本情况:

  • 每个模型都是一个Python类,它是django.db.models.Model的子类。
  • 模型的每个属性都代表一个数据库字段。
  • 综上所述,Django为您提供了一个自动生成的数据库访问API,详询官方文档链接

快速入门 

下面这个例子定义了一个 Person 模型,包含 first_name last_name

1
2
3
4
5
from django.db import models
 
class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。

上面的 Person 模型将会像这样创建一个数据库表:

1
2
3
4
5
CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

一些说明:

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

字段 

常用字段 

AutoField

自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动创建。

一个model不能有两个AutoField字段。

IntegerField

一个整数类型。数值的范围是 -2147483648 ~ 2147483647。

CharField

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

DateField

日期类型,日期格式为YYYY-MM-DD,相当于Python中的datetime.date的实例。

参数:

  • auto_now:每次修改时修改为当前日期时间。
  • auto_now_add:新创建对象时自动添加当前日期时间。

auto_now和auto_now_add和default参数是互斥的,不能同时设置。

DatetimeField

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

字段类型,详情可点击查询官网

自定义字段

自定义一个二进制字段,以及Django字段与数据库字段类型的对应关系。

View Code

自定义一个char类型字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
 
    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return 'char(%s)' % self.max_length
使用自定义char类型字段:
1
2
3
4
5
class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定义的char类型的字段
    cname = MyCharField(max_length=25)

创建的表结构:

字段参数

字段参数,详情可点击查看官网

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    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_112233\d+', message='又错误了', code='c2'),
                                    EmailValidator(message='又错误了', code='c3'), ]
                            )
 
字段参数

Model Meta参数

这个不是很常用,如果你有特殊需要可以使用。详情点击查看官网

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
        verbose_name_plural = '所有用户信息'
 
        # 联合索引 
        index_together = [
            ("pub_date", "deadline"),   # 应为两个存在的字段
        ]
 
        # 联合唯一索引
        unique_together = (("driver", "restaurant"),)   # 应为两个存在的字段

多表关系和参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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,              # 默认创建第三张表时,数据库中表的名称

ORM操作

基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 增
models.Tb1.objects.create(c1='xx', c2='oo')   # 增加一条数据,可以接受字典类型数据 **kwargs
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.exclude(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()   # 修改单条数据

进阶操作

# 获取个数
#
# models.Tb1.objects.filter(name='seven').count()

# 大于,小于
#
# models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
# models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
# models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
# models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

# 成员判断in
#
# models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

# 是否为空 isnull
# Entry.objects.filter(pub_date__isnull=True)

# 包括contains
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
# models.Tb1.objects.exclude(name__icontains="ven")

# 范围range
#
# models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and

# 其他类似
#
# startswith,istartswith, endswith, iendswith,

# 排序order by
#
# models.Tb1.objects.filter(name='seven').order_by('id')    # asc
# models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

# 分组group by
#
# from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

# limit 、offset
#
# models.Tb1.objects.all()[10:20]

# regex正则匹配,iregex 不区分大小写
#
# Entry.objects.get(title__regex=r'^(An?|The) +')
# Entry.objects.get(title__iregex=r'^(an?|the) +')

# date
#
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

# year
#
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005)

# month
#
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6)

# day
#
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3)

# week_day
#
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2)

# hour
#
# Event.objects.filter(timestamp__hour=23)
# Event.objects.filter(time__hour=5)
# Event.objects.filter(timestamp__hour__gte=12)

# minute
#
# Event.objects.filter(timestamp__minute=29)
# Event.objects.filter(time__minute=46)
# Event.objects.filter(timestamp__minute__gte=29)

# second
#
# Event.objects.filter(timestamp__second=31)
# Event.objects.filter(time__second=2)
# Event.objects.filter(timestamp__second__gte=31)
进阶操作

高级操作

# extra
# 在QuerySet的基础上继续执行子语句
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

# select和select_params是一组,where和params是一组,tables用来设置from哪个表
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

举个例子:
models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,],
                    where = ['age>%s'],
                    params=[18,],
                    order_by=['-age'],
                    tables=['app01_usertype']
                )
                """
                select 
                    app01_userinfo.id,
                    (select count(1) from app01_usertype where id>1) as newid
                from app01_userinfo,app01_usertype
                where 
                    app01_userinfo.age > 18
                order by 
                    app01_userinfo.age desc
                """


# 执行原生SQL
# 更高灵活度的方式执行原生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()

QuerySet相关方法

##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################

def all(self)
    # 获取所有的数据对象

def filter(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def select_related(self, *fields)
    性能相关:表之间进行join连表操作,一次性获取关联的数据。

    总结:
    1. select_related主要针一对一和多对一关系进行优化。
    2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。

    总结:
    1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
    2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。

def annotate(self, *args, **kwargs)
    # 用于实现聚合group by查询

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

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    # 用于distinct去重
    models.UserInfo.objects.values('nid').distinct()
    # select distinct nid from userinfo

    注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
    # 用于排序
    models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    # 构造额外的查询条件或者映射,如:子查询

    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

 def reverse(self):
    # 倒序
    models.UserInfo.objects.all().order_by('-nid').reverse()
    # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


 def defer(self, *fields):
    models.UserInfo.objects.defer('username','id')
    或
    models.UserInfo.objects.filter(...).defer('username','id')
    #映射中排除某列数据

 def only(self, *fields):
    #仅取某个表中的数据
     models.UserInfo.objects.only('username','id')
     或
     models.UserInfo.objects.filter(...).only('username','id')

 def using(self, alias):
     指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

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")

    ################### 原生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(..)


def values(self, *fields):
    # 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
    # 获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
    # 根据时间进行某一部分进行去重查找并截取指定内容
    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    # order只能是:"ASC"  "DESC"
    # 并获取转换后的时间
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日

    models.DatePlus.objects.dates('ctime','day','DESC')

def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    # kind只能是 "year", "month", "day", "hour", "minute", "second"
    # order只能是:"ASC"  "DESC"
    # tzinfo时区对象
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

    """
    pip3 install pytz
    import pytz
    pytz.all_timezones
    pytz.timezone(‘Asia/Shanghai’)
    """

def none(self):
    # 空QuerySet对象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
   # 聚合函数,获取字典类型聚合结果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
   ===> {'k': 3, 'n': 4}

def count(self):
   # 获取个数

def get(self, *args, **kwargs):
   # 获取单个对象

def create(self, **kwargs):
   # 创建对象

def bulk_create(self, objs, batch_size=None):
    # 批量插入
    # batch_size表示一次插入的个数
    objs = [
        models.DDD(name='r11'),
        models.DDD(name='r22')
    ]
    models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
    # 如果存在,则获取,否则,创建
    # defaults 指定创建时,其他字段的值
    obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
    # 如果存在,则更新,否则,创建
    # defaults 指定创建时或更新时的其他字段
    obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
   # 获取第一个

def last(self):
   # 获取最后一个

def in_bulk(self, id_list=None):
   # 根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
   # 删除

def update(self, **kwargs):
    # 更新

def exists(self):
   # 是否有结果

其他操作

一、字段

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

二、参数

    null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    db_tablespace
    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_112233\d+', message='又错误了', code='c2'),
                                    EmailValidator(message='又错误了', code='c3'), ]
                            )
参数

三、元信息

"""
Django模型类的Meta
Django模型类的Meta是一个内部类,它用于定义一些Django模型类的行为特性。而可用的选项大致包含以下几类:

unique_together

unique_together这个选项用于:当你需要通过两个字段保持唯一性时使用。比如假设你希望,一个Person的FirstName和LastName两者的组合必须是唯一的,那么需要这样设置:
unique_together = (("first_name", "last_name"),)
一个ManyToManyField不能包含在unique_together中。如果你需要验证关联到ManyToManyField字段的唯一验证,尝试使用signal(信号)或者明确指定through属性。

verbose_name

verbose_name的意思很简单,就是给你的模型类起一个更可读的名字一般定义为中文,我们:
verbose_name = "学校"

verbose_name_plural

这个选项是指定,模型的复数形式是什么,比如:
verbose_name_plural = "学校"
如果不指定Django会自动在模型名称后加一个’s’。

abstract

这个属性是定义当前的模型是不是一个抽象类。所谓抽象类是不会对应数据库表的。一般我们用它来归纳一些公共属性字段,然后继承它的子类可以继承这些字段。

Options.abstract
如果abstract = True 这个model就是一个抽象类

app_label

这个选型只在一种情况下使用,就是你的模型不在默认的应用程序包下的models.py文件中,这时候需要指定你这个模型是哪个应用程序的。

Options.app_label
如果一个model定义在默认的models.py,例如如果你的app的models在myapp.models子模块下,你必须定义app_label让Django知道它属于哪一个app
app_label = 'myapp'

db_table

db_table是指定自定义数据库表明的。Django有一套默认的按照一定规则生成数据模型对应的数据库表明。
Options.db_table
定义该model在数据库中的表名称
  db_table = 'Students'
如果你想使用自定义的表名,可以通过以下该属性
  table_name = 'my_owner_table'

db_teblespace

Options.db_teblespace
定义这个model所使用的数据库表空间。如果在项目的settin中定义那么它会使用这个值

get_latest_by

Options.get_latest_by
在model中指定一个DateField或者DateTimeField。这个设置让你在使用model的Manager上的lastest方法时,默认使用指定字段来排序

managed

Options.managed
默认值为True,这意味着Django可以使用syncdb和reset命令来创建或移除对应的数据库。默认值为True,如果你不希望这么做,可以把manage的值设置为False

order_with_respect_to

这个选项一般用于多对多的关系中,它指向一个关联对象,就是说关联对象找到这个对象后它是经过排序的。指定这个属性后你会得到一个get_xxx_order()和set_xxx_order()的方法,通过它们你可以设置或者回去排序的对象

ordering

这个字段是告诉Django模型对象返回的记录结果集是按照哪个字段排序的。这是一个字符串的元组或列表,没有一个字符串都是一个字段和用一个可选的表明降序的'-'构成。当字段名前面没有'-'时,将默认使用升序排列。使用'?'将会随机排列

ordering=['order_date'] # 按订单升序排列
ordering=['-order_date'] # 按订单降序排列,-表示降序
ordering=['?order_date'] # 随机排序,?表示随机
ordering=['-pub_date','author'] # 以pub_date为降序,在以author升序排列
permissions

permissions主要是为了在Django Admin管理模块下使用的,如果你设置了这个属性可以让指定的方法权限描述更清晰可读。Django自动为每个设置了admin的对象创建添加,删除和修改的权限。
permissions = (('can_deliver_pizzas','Can deliver pizzas'))

proxy

这是为了实现代理模型使用的,如果proxy = True,表示model是其父的代理 model 

官方文档:https://docs.djangoproject.com/en/1.11/ref/models/options/
"""
Django模型类的Meta
class UserInfo(models.Model):
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32)

    class Meta:
        # django以后再做数据库迁移时,不再为UserInfo类创建相关的表以及表结构了。
        # 此类可以当做"父类",被其他Model类继承。
        abstract = True

        # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
        db_table = "table_name"

        # 联合索引
        index_together = [
            ("pub_date", "deadline"),
        ]

        # 联合唯一索引
        unique_together = (("driver", "restaurant"),)

        # admin中显示的表名称
        verbose_name

        # verbose_name加s
        verbose_name_plural


更多:https: // docs.djangoproject.com / en / 1.10 / ref / models / options /
元信息

四、连表结构

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,        # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表

        db_constraint = True,       # 是否在数据库中创建外键约束
        db_table = 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)

"""
与普通的多对多不一样,使用自定义中间表的多对多不能使用add(), create(),remove(),和set()方法来创建、删除关系
beatles = Group.objects.create(name="The Beatles")
>>> # 无效
>>> beatles.members.add(john)
>>> # 无效
>>> beatles.members.create(name="George Harrison")
>>> # 无效
>>> beatles.members.set([john, paul, ringo, george])
但是,clear()方法是有效的,它能清空所有的多对多关系。
>>> # 有效
>>> beatles.members.clear()
一旦通过创建中间模型实例的方法建立了多对多的关联,你立刻就可以像普通的多对多那样进行查询操作:
"""
连表结构

五、双下划线

# 获取个数
        #
        # models.Tb1.objects.filter(name='seven').count()

        # 大于,小于
        #
        # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
        # models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
        # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
        # models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
        # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

        # in
        #
        # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
        # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

        # isnull
        # Entry.objects.filter(pub_date__isnull=True)

        # contains
        #
        # models.Tb1.objects.filter(name__contains="ven")
        # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
        # models.Tb1.objects.exclude(name__icontains="ven")

        # range
        #
        # models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and

        # 其他类似
        #
        # startswith,istartswith, endswith, iendswith,

        # order by
        #
        # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
        # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

        # group by
        #
        # from django.db.models import Count, Min, Max, Sum
        # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
        # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

        # limit 、offset
        #
        # models.Tb1.objects.all()[10:20]

        # regex正则匹配,iregex 不区分大小写
        #
        # Entry.objects.get(title__regex=r'^(An?|The) +')
        # Entry.objects.get(title__iregex=r'^(an?|the) +')

        # date
        #
        # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
        # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

        # year
        #
        # Entry.objects.filter(pub_date__year=2005)
        # Entry.objects.filter(pub_date__year__gte=2005)

        # month
        #
        # Entry.objects.filter(pub_date__month=12)
        # Entry.objects.filter(pub_date__month__gte=6)

        # day
        #
        # Entry.objects.filter(pub_date__day=3)
        # Entry.objects.filter(pub_date__day__gte=3)

        # week_day
        #
        # Entry.objects.filter(pub_date__week_day=2)
        # Entry.objects.filter(pub_date__week_day__gte=2)

        # hour
        #
        # Event.objects.filter(timestamp__hour=23)
        # Event.objects.filter(time__hour=5)
        # Event.objects.filter(timestamp__hour__gte=12)

        # minute
        #
        # Event.objects.filter(timestamp__minute=29)
        # Event.objects.filter(time__minute=46)
        # Event.objects.filter(timestamp__minute__gte=29)

        # second
        #
        # Event.objects.filter(timestamp__second=31)
        # Event.objects.filter(time__second=2)
        # Event.objects.filter(timestamp__second__gte=31)
进阶操作
# extra
    #
    # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    #    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    #    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    #    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    #    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

    # F
    #
    # from django.db.models import F
    # models.Tb1.objects.update(num=F('num')+1)


    # Q
    #
    # 方式一:
    # Q(nid__gt=10)
    # Q(nid=8) | Q(nid__gt=10)
    # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
    # 方式二:
    # con = Q()
    # q1 = Q()
    # q1.connector = 'OR'
    # q1.children.append(('id', 1))
    # q1.children.append(('id', 10))
    # q1.children.append(('id', 9))
    # q2 = Q()
    # q2.connector = 'OR'
    # q2.children.append(('c1', 1))
    # q2.children.append(('c1', 10))
    # q2.children.append(('c1', 9))
    # con.add(q1, 'AND')
    # con.add(q2, 'AND')
    #
    # models.Tb1.objects.filter(con)


    # 执行原生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()
其他操作
on_delete=None,               # 删除关联表中的数据时,当前表与其关联的field的行为
on_delete=models.CASCADE,     # 删除关联数据,与之关联也删除
on_delete=models.DO_NOTHING,  # 删除关联数据,什么也不做
on_delete=models.PROTECT,     # 删除关联数据,引发错误ProtectedError
# models.ForeignKey('关联表', on_delete=models.SET_NULL, blank=True, null=True)
on_delete=models.SET_NULL,    # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理)
# models.ForeignKey('关联表', on_delete=models.SET_DEFAULT, default='默认值')
on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理)
on_delete=models.SET,         # 删除关联数据,
————————————————
版权声明:本文为CSDN博主「buxianghejiu」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/buxianghejiu/article/details/79086011
on_delete

单表操作

一、创建表

1、创建模型

创建名为app01的app,在app01下的models.py中创建模型:

from django.db import models
# Create your models here.
class Book(models.Model):
     id=models.AutoField(primary_key=True)
     title=models.CharField(max_length=32,unique=True)
     pub_date=models.DateField()
     price=models.DecimalField(max_digits=8,decimal_places=2) # 999999.99
     publish=models.CharField(max_length=32)
 
     def __str__(self):
         return self.title
View Code

2、settings配置:DATABASES

若想将模型转为mysql数据库中的表,需要在settings中配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'orm9',# 要连接的数据库,连接前需要创建好
        'USER':'root',# 连接数据库的用户名
        'PASSWORD':'',# 连接数据库的密码
        'HOST':'127.0.0.1',# 连接主机,默认本级
        'PORT':3306 ,# 端口 默认3306
    }
}
View Code

3、settings配置:INSTALLED_APPS

确保配置文件中的INSTALLED_APPS中写入我们创建的app名称

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "app01",
]
View Code

4、项目名文件下的__init__配置

django默认导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的__init__,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()
View Code

5、数据库迁移命令

python manage.py makemigrations
python manage.py migrate
View Code

6、静态文件配置:

#在settings.py中:
STATIC_URL = '/static/'
STATICFILES_DIRS=(
    os.path.join(BASE_DIR,'static'),
)
View Code

7、打印orm转换过程中的sql,在settings中配置:

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

8、常见报错

  • 报错:no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的__init__,在里面写入:
import pymysql
pymysql.install_as_MySQLdb()
View Code
  • 报错:django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None
  • MySQLclient目前只支持到python3.4,因此如果使用的更高版本的python,需要修改如下: 通过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql 这个路径里的文件把下面代码注释掉就OK了。
if version < (1, 3, 3):
     raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)
View Code
  • 报错:‘str’ object has no attribute ‘decode’
  • File "D:\Python38\lib\site-packages\django\db\backends\mysql\operations.py", line 146, in last_executed_query
    query = query.decode(errors='replace')
    AttributeError: 'str' object has no attribute 'decode'

  • 解决:将提示目录的operations.py文件对应的decode改为encode
  • 报错:数据库安全模式

  • "D:\Python38\lib\site-packages\pymysql\cursors.py:170: Warning: (3135, "'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict mode. They will be merged with strict mode in a future release.")
    result = self._query(query)
    No changes detected 

  • 解决:把项目的settings.py关于数据的option注释掉
  • =
  • 其他 :某些问题下,数据库迁移不成功可以把迁移文件删掉重新迁移
  • python manage.py makemigrations
    python manage.py check # 检查是否错误
    python manage.py migrate

二、增

#app01.views.py
from django.shortcuts import render,HttpResponse
from app01.models import Book
def index(request):
    # ==================================添加表记录 ==================================
  
    # 方式1:
    # book_obj=Book(id=1,title="python",price=100,pub_date="2012-12-12",publish="人民出版社")
    # book_obj.save()
  
    #方式2: create返回值就是当前生成的对象纪录
  
    book_obj=Book.objects.create(title="php2",price=100,pub_date="2013-12-12",publish="人民出版社")
    print(book_obj.title)
    print(book_obj.price)
    print(book_obj.pub_date)
    return HttpResponse("OK")
     
    #方式3: 批量导入:
     
    book_list=[]
    for i in range(100):
        book=Book(title="book_%s"%i,price=i*i)
        book_list.append(book)
 
    Book.objects.bulk_create(book_list)
View Code

三、删

  • 在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。
  • 如果不想级联删除,可以设置为:pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)
  • delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:Entry.objects.all().delete() 
#app01.views.py
from django.shortcuts import render,HttpResponse
from app01.models import Book
def index(request):
    # ==================================删除表纪录=================================
    # delete: 调用者: queryset对象  model对象
    ret=Book.objects.filter(price=100).delete()
    print(ret)
    ret2 =Book.objects.filter(price=100).first().delete()
    print(ret2)
    return HttpResponse("OK")
View Code

四、改

update()方法对于任何结果集(QuerySet)均有效,你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。

#app01.views.py
from django.shortcuts import render,HttpResponse
from app01.models import Book
def index(request):
    # ==================================修改表纪录 =================================
    # update :  调用者: queryset对象
 
    #方式1
    book_obj=Book.objects.get(pk=1)
    book_obj.title='新值'
    book_obj.save()
 
    #方式2
    ret=Book.objects.filter(title="php2").update(title="php")
    return HttpResponse("OK")
View Code

五、查

#app01.views.py
from django.shortcuts import render,HttpResponse
from app01.models import Book
def index(request):
    # ================================== 查询表记录API ==================================
     
    #(1) all方法:   返回值一个queryset对象                      ## 查询所有结果
    #book_list=Book.objects.all()
    #print(book_list)  # [obj1,obj2,.....]
 
    # for  obj in book_list:
    #     print(obj.title,obj.price)
    #print(book_list[1].title)
 
    #(2) first,last : 调用者:queryset对象  返回值:model对象     ## first:返回第一条记录  last:返回最后一条记录
    #book=Book.objects.all().first()
    #book=Book.objects.all()[0]
 
    #(3) filter()  返回值:queryset对象                          ## 它包含了与所给筛选条件相匹配的对象
    # book_list=Book.objects.filter(price=100)     # [obj1,obj2,....]
    # print(book_list) #<QuerySet [<Book: python>, <Book: php>]>
    # book_obj=Book.objects.filter(price=100).first()
 
    # ret=Book.objects.filter(title="go",price=200)
    # print(ret)
 
    #(4) get()  有且只有一个查询结果时才有意义  返回值:model对象  ## 如果符合筛选条件的对象超过一个或者没有都会抛出错误。
    # book_obj=Book.objects.get(title="go")
    # book_obj=Book.objects.get(price=100)
    # print(book_obj.price)
 
    # (5) exclude 返回值:queryset对象                            ##它包含了与所给筛选条件不匹配的对象 与filter() 相反
    # ret=Book.objects.exclude(title="go")
    # print(ret)
 
    # (6) order_by   调用者: queryset对象   返回值:  queryset对象  ## 对查询结果排序
    # ret=Book.objects.all().order_by("-id")
    # ret=Book.objects.all().order_by("price","id")
    # print(ret)
 
    # (7) count()   调用者: queryset对象   返回值: int           ##返回数据库中匹配查询(QuerySet)的对象数量。
    # ret=Book.objects.all().count()
    # print(ret)
 
    # (8) exist()                                                ##如果QuerySet包含数据,就返回True,否则返回False
    # ret=Book.objects.all().exists()
    #
    # if ret:
    #     print("ok")
 
    # (9) values 方法  调用者: queryset对象  返回值:queryset对象
    # ret=Book.objects.all()
    # for i in ret:
    #     print(i.title)
    # ret=Book.objects.all().values("price","title")
    # print(ret)
    '''
    values:
    temp=[]
 
    for obj in Book.objects.all()
         temp.append({
             "price"=obj.price
             "title"=obj.title
         })
    return temp
    '''
    # <QuerySet [{'price': Decimal('100.00')}, {'price': Decimal('100.00')}, {'price': Decimal('200.00')}]>
    #print(ret[0].get("price")) # 100.00
 
    # (10) values_list 方法  调用者: queryset对象  返回值:queryset对象
    # ret=Book.objects.all().values_list("price","title")
    # # print(ret) #<QuerySet [(Decimal('100.00'),), (Decimal('100.00'),), (Decimal('200.00'),)]>
    # print(ret)
 
    '''
values:
    <QuerySet [{'title': 'python红宝书', 'price': Decimal('100.00')}, {'title': 'php', 'price': Decimal('100.00')}, {'title': 'go', 'price': Decimal('200.00')}]>
values_list:
    <QuerySet [(Decimal('100.00'), 'python红宝书'), (Decimal('100.00'), 'php'), (Decimal('200.00'), 'go')]>
 
    '''
    # 11 distinct                                       ##从返回结果中剔除重复纪录  reverse对查询结果反向排序
    # ret=Book.objects.all().distinct()   #没有意义
    # ret=Book.objects.all().values("price").distinct()
 
    #Book.objects.all().filter().order_by().filter().reverse().first()
 
    # ================================== 基于双下划线的模糊查询 ==================================
 
    # ret=Book.objects.filter(price__gt=10,price__lt=200)
 
    # ret=Book.objects.filter(title__startswith="p")
 
    #ret=Book.objects.filter(title__contains="h")
    #ret=Book.objects.filter(title__icontains="h")  #忽略大小写
 
    #ret=Book.objects.filter(price__in=[100,200,300])
 
    #ret=Book.objects.filter(price__range=[200,300])
 
    #ret=Book.objects.filter(pub_date__year=2018,pub_date__month=5)
 
    return HttpResponse("OK")
View Code

六、聚合查询与分组查询

# app01.models.py
 
from django.db import models
 
 
class Emp(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.CharField(max_length=32)
    province = models.CharField(max_length=32)
 
 
# app01.views.py
 
from django.shortcuts import render, HttpResponse
from app01.models import *
from django.db.models import Avg, Max, Min, Count
 
 
def query(request):
    # -------------------------单表 聚合与分组查询---------------------------
 
    # ------------------------->聚合 aggregate:返回值是一个字典,不再是queryset
    # 查询所有书籍的平均价格
    ret = Book.objects.all().aggregate(avg_price=Avg("price"), max_price=Max("price"))
    print(ret)  # {'avg_price': 151.0, 'max_price': Decimal('301.00')}
 
    ret = Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    print(ret)  # {'price__avg': 151.0, 'price__max': Decimal('301.00'), 'price__min': Decimal('12.99')}
 
    # ------------------------->分组查询 annotate ,返回值依然是queryset
    # 单表分组查询的ORM语法: 单表模型.objects.values("group by的字段").annotate(聚合函数("统计字段"))
    # 在单表分组下, 按着主键进行group by是没有任何意义的.
 
    # 查询每一个部门的名称以及员工的平均薪水
    # select dep,Avg(salary) from emp group by dep
 
    ret = Emp.objects.values("dep").annotate(avg_salary=Avg("salary"))
    print(ret)  # <QuerySet [{'avg_salary': 5000.0, 'dep': '保安部'}, {'avg_salary': 51000.0, 'dep': '教学部'}]>
 
    # 查询每一个省份的名称以及员工数
    ret = Emp.objects.values("province").annotate(c=Count("id"))
    print(ret)  # <QuerySet [{'province': '山东省', 'c': 2}, {'province': '河北省', 'c': 1}]>
 
    # 补充知识点:
    # ret=Emp.objects.all()
    # print(ret)  # select * from emp
    # ret=Emp.objects.values("name")
    # print(ret)  # select name from emp
 
    return HttpResponse('OK')
View Code

多表操作

一、创建表

  • id字段是自动添加的
  • 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
from django.db import models
 
# Create your models here.
from django.db import models
 
'''
Book  ----   Publish 一对多
 
'''
 
 
class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 一对一
    authordetail = models.OneToOneField(to="AuthorDetail", to_field="nid", on_delete=models.CASCADE)
 
    def __str__(self):
        return self.name
 
 
# 作者详情表
class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField()
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=64)
 
 
# 出版社表
class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()
 
    def __str__(self):
        return self.name
 
 
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
 
    # 一对多关系
    publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE, )
    '''
        publish_id INT ,
        FOREIGN KEY (publish_id) REFERENCES publish(id)
 
    '''
 
    # 多对多 方式一:Book表中用ManyToManyField
    authors = models.ManyToManyField(to="Author")
 
    def __str__(self):
        return self.title
 
 
# CREATE  TABLE book_authors(
#    id INT PRIMARY KEY auto_increment ,
#    book_id INT ,
#    author_id INT ,
#    FOREIGN KEY (book_id) REFERENCES book(id),
#    FOREIGN KEY (author_id) REFERENCES author(id)
#     )
 
 
"""
#多对多 方式二:Book表中不用ManyToManyField,自己建第三张表
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
 
    # 一对多关系
    publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE, )
 
    def __str__(self):
        return self.title
 
class Book2Author(models.Model):
    book=models.ForeignKey(to="Book", on_delete=models.CASCADE)
    author=models.ForeignKey(to="Author", on_delete=models.CASCADE)
 
 
#多对多 方式三:混合
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
 
    # 一对多关系
    publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE, )
    authors = models.ManyToManyField(to="Author",through="Book2Author",through_fields=['book','author'])  #through告知第三张表
 
    def __str__(self):
        return self.title
 
class Book2Author(models.Model):
    book=models.ForeignKey(to="Book", on_delete=models.CASCADE)
    author=models.ForeignKey(to="Author", on_delete=models.CASCADE, related_name='au')
    mentor=models.ForeignKey(to="Author", on_delete=models.CASCADE, related_name='me')
"""
View Code

二、增

from django.shortcuts import render, HttpResponse
from app01.models import *
def add(request):
    # -------------------------一对多的关系-------------------------
    #方式1:
    #为book表绑定出版社: book  ---    publish
    book_obj=Book.objects.create(title="红楼梦",price=100,publishDate="2012-12-12",publish_id=1)
    print(book_obj.title)
 
    #方式2: 
    #pub_obj = Publish.objects.get(nid=1)
    pub_obj=Publish.objects.filter(nid=1).first()
    book_obj=Book.objects.create(title="三国演绎",price=100,publishDate="2012-12-12",publish=pub_obj)
    print(book_obj.publish)       #  与这本书籍关联的出版社对象
    print(book_obj.publish_id)
 
    # -------------------------多对多的关系-------------------------
    book_obj=Book.objects.create(title="python全栈开发",price=100,publishDate="2012-12-12",publish_id=1)
 
    egon=Author.objects.get(name="egon")
    alex=Author.objects.get(name="alex")
 
    #绑定多对多关系的API
    book_obj.authors.add(egon,alex)
    book_obj.authors.add(1,2,3)
    book_obj.authors.add(*[1,2,3])
 
    #解除多对多关系
    book_obj=Book.objects.filter(nid=4).first()
    book_obj.authors.remove(2)
    #book_obj.authors.remove(*[1,2])  # 将某个特定的对象从被关联对象集合中去除。
 
    book_obj.authors.clear()    # 清空被关联对象集合
    book_obj.authors.set()  # 先清空再设置 
    return HttpResponse("OK")
View Code

三、删

def delete_book(request,delete_book_id):
    Book.objects.filter(pk=delete_book_id).delete()
    return redirect("/books/")
View Code

四、改

def change_book(request,edit_book_id):
    edit_book_obj=Book.objects.filter(pk=edit_book_id).first()
 
    if  request.method=="POST":
        title=request.POST.get("title")
        price=request.POST.get("price")
        pub_date=request.POST.get("pub_date")
         
        # 多对一
        publish_id=request.POST.get("publish_id")
        #多对多
        authors_id_list=request.POST.getlist("authors_id_list") # checkbox,select
 
        Book.objects.filter(pk=edit_book_id).update(title=title,price=price,publishDate=pub_date,publish_id=publish_id)
        # edit_book_obj.authors.clear()
        # edit_book_obj.authors.add(*authors_id_list)
 
        #多对多
        edit_book_obj.authors.set(authors_id_list)
 
        return redirect("/books/")
 
    publish_list=Publish.objects.all()
    author_list=Author.objects.all()
 
    return render(request,"editbook.html",{"edit_book_obj":edit_book_obj,"publish_list":publish_list,"author_list":author_list})
View Code

五、查

1、基于对象的跨表查询(子查询)

A-B
关联属性在A表中
 
正向查询: A------>B
反向查询: B------>A
 
基于对象的跨表查询(子查询)
    # 一对多查询
           正向查询:按字段
           反向查询:表名小写_set.all()
 
                                     book_obj.publish
            Book(关联属性:publish)对象  --------------> Publish对象
                                     <--------------
                                 publish_obj.book_set.all()  # queryset
 
    # 多对多查询
           正向查询:按字段
           反向查询:表名小写_set.all()
 
                                     book_obj.authors.all()
            Book(关联属性:authors)对象  ------------------------> Author对象
                                     <------------------------
                                     author_obj.book_set.all() # queryset
 
    # 一对一查询
           正向查询:按字段
           反向查询:表名小写
 
                                              author.authordetail
            Author(关联属性:authordetail)对象  ------------------------>AuthorDetail对象
                                             <------------------------
                                              authordetail.author

from django.shortcuts import render, HttpResponse
from app01.models import *
 
def query(request):
    # -------------------------基于对象的跨表查询(子查询)-----------------------
 
    # 一对多查询的正向查询 : 查询python全栈开发这本书的出版社的名字
    book_obj=Book.objects.filter(title="python全栈开发").first()
    print(book_obj.publish) # 与这本书关联的出版社对象
    print(book_obj.publish.name)
 
    # 一对多查询的反向查询 : 查询人民出版社出版过的书籍名称
    publish=Publish.objects.filter(name="人民出版社").first()
    ret=publish.book_set.all()
    print(ret)
 
    # 多对多查询的正向查询 : 查询python全栈开发这本书的所有作者的名字
    book_obj=Book.objects.filter(title="python全栈开发").first()
    author_list=book_obj.authors.all() # queryset对象  [author_obj1,...]
 
    for author in author_list:
        print(author.name)
 
    # 多对多查询的反向查询 : 查询alex出版过的所有书籍名称
    alex=Author.objects.filter(name="alex").first()
 
    book_list=alex.book_set.all()
    for book in book_list:
        print(book.title)
 
 
    # 一对一查询的正向查询 : 查询alex的手机号
    alex=Author.objects.filter(name="alex").first()
    print(alex.authordetail.telephone)
 
    # 一对一查询的反向查询 : 查询手机号为110的作者的名字和年龄
    ad=AuthorDetail.objects.filter(telephone="110").first()
    print(ad.author.name)
    print(ad.author.age)
 
    return HttpResponse("ok")
View Code

2、基于双下划线的跨表查询

A-B
关联属性在A表中
 
正向查询: A------>B
反向查询: B------>A
 
基于双下划线的跨表查询(join查询)
     key:正向查询按字段,反向查询按表名小写

from django.shortcuts import render, HttpResponse
from app01.models import *
 
def query(request):
 
    # -------------------------基于双下划线的跨表查询(join查询)-----------------------
 
    '''
    正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表
    '''
 
    # 一对多查询 : 查询python全栈开发这本书的出版社的名字
    # 方式1:
    ret=Book.objects.filter(title="python全栈开发").values("publish__name")
    print(ret) # <QuerySet [{'publish__name': '南京出版社'}]>
 
    # 方式2:
    ret=Publish.objects.filter(book__title="python全栈开发").values("name")
    print(ret)
 
    # 多对多查询 : 查询python全栈开发这本书的所有作者的名字
    # 方式1:
    # 需求: 通过Book表join与其关联的Author表,属于正向查询:按字段authors通知ORM引擎join book_authors与author
    ret=Book.objects.filter(title="python全栈开发").values("authors__name")
    print(ret) # <QuerySet [{'authors__name': 'alex'}, {'authors__name': 'egon'}]>
 
    # 方式2:
    # 需求: 通过Author表join与其关联的Book表,属于反向查询:按表名小写book通知ORM引擎join book_authors与book表
    ret=Author.objects.filter(book__title="python全栈开发").values("name")
    print(ret) # <QuerySet [{'name': 'alex'}, {'name': 'egon'}]>
 
    # 一对一查询的查询 : 查询alex的手机号
    # 方式1:
    # 需求: 通过Author表join与其关联的AuthorDetail表,属于正向查询:按字段authordetail通知ORM引擎join Authordetail表
    ret=Author.objects.filter(name="alex").values("authordetail__telephone")
    print(ret) # <QuerySet [{'authordetail__telephone': 110}]>
 
    # 方式2:
    # 需求: 通过AuthorDetail表join与其关联的Author表,属于反向查询:按表名小写author通知ORM引擎join Author表
    ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")
    print(ret) # <QuerySet [{'telephone': 110}]>
 
    # 进阶练习:(连续跨表)
    # 练习: 手机号以110开头的作者出版过的所有书籍名称以及书籍出版社名称
 
    # 方式1:
    # 需求: 通过Book表join AuthorDetail表, Book与AuthorDetail无关联,所以必需连续跨表
    ret=Book.objects.filter(authors__authordetail__telephone__startswith="110").values("title","publish__name")
    print(ret)
 
    # 方式2:
    ret=Author.objects.filter(authordetail__telephone__startswith="110").values("book__title","book__publish__name")
    print(ret)
 
    return HttpResponse("OK")
View Code

3、聚合和分组查询

from django.shortcuts import render, HttpResponse
from app01.models import *


def query(request):
    # ------------------------->聚合 aggregate:返回值是一个字典,不再是queryset
    # 查询所有书籍的平均价格
    from django.db.models import Avg, Max, Min, Count
    ret = Book.objects.all().aggregate(avg_price=Avg("price"), max_price=Max("price"))
    print(ret)  # {'avg_price': 151.0, 'max_price': Decimal('301.00')}

    ret = Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    print(ret)  # {'price__avg': 151.0, 'price__max': Decimal('301.00'), 'price__min': Decimal('12.99')}

    # sql:select avg(price),min(price),max(price) from book;

    # ------------------------->单表分组查询 annotate ,返回值依然是queryset
    # 单表总结: objects.values("group by的字段").annotate(聚合函数("统计字段"))
    # 在单表分组下, 按着主键进行group by是没有任何意义的.
    # 查询每一个部门的名称以及员工的平均薪水
    # sql:select dep,Avg(salary) from emp group by dep
    ret = Emp.objects.values("dep").annotate(avg_salary=Avg("salary"))
    print(ret)  # <QuerySet [{'avg_salary': 5000.0, 'dep': '保安部'}, {'avg_salary': 51000.0, 'dep': '教学部'}]>


    # ------------------------->多表分组查询 annotate ,返回值依然是queryset
    # 多表总结:跨表的分组查询的模型:
    # 方式1:每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    # 方式2:每一个后的表模型.objects.annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")

    #################### 方式1: 跨表分组查询####################

    # 查询每一个出版社的名称以及出版的书籍个数
    """
    分组查询sql:
           select publish.name,Count("title") from Book inner join Publish on book.publish_id=publish.id
               group by  publish.id,publish.name,publish.addr,publish.email
    """
    # 法1:
    ret = Publish.objects.values("name").annotate(c=Count("book__title"))
    print(ret)  # <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '南京出版社', 'c': 1}]>

    ret = Publish.objects.values("nid").annotate(c=Count("book__title"))
    print(ret)  # <QuerySet [{'nid': 1, 'c': 3}, {'nid': 2, 'c': 1}]>

    # 法2:
    ret = Publish.objects.values("nid").annotate(c=Count("book__title")).values("name", "c")
    print(ret)  # <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '南京出版社', 'c': 1}]>

    # 查询每一个作者的名字以及出版过的书籍的最高价格
    ret = Author.objects.values("pk").annotate(max_price=Max("book__price")).values("name", "max_price")
    print(ret)

    # 查询每一个书籍的名称以及对应的作者个数
    ret = Book.objects.values("pk").annotate(c=Count("authors__name")).values("title", "c")
    print(ret)

    #################### 方式2: 跨表分组查询####################

    # 示例1 查询每一个出版社的名称以及出版的书籍个数
    # ret=Publish.objects.values("nid").annotate(c=Count("book__title")).values("name","email","c")
    # ret=Publish.objects.all().annotate(c=Count("book__title")).values("name","c","city")
    ret = Publish.objects.annotate(c=Count("book__title")).values("name", "c", "city")
    print(ret)

    ##################### 练习   ####################

    # 统计每一本以py开头的书籍的作者个数:
    # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    ret = Book.objects.filter(title__startswith="py").values("pk").annotate(c=Count("authors__name")).values("title",
                                                                                                             "c")

    # 统计不止一个作者的图书
    ret = Book.objects.values("pk").annotate(c=Count("authors__name")).filter(c__gt=1).values("title", "c")
    print(ret)

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

    return HttpResponse("查询成功")
View Code
import os
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Aida_crm.settings")
    import django
    django.setup()

    from crm import models
    from django.db.models import Count

    # 每个校区有多少个班级
    qq1=models.Campus.objects.annotate(Count('classlist')).values()
    qq2=models.Campus.objects.values('id').annotate(Count('classlist')).values()

    #方式2
    qq3=models.ClassList.objects.values('campuses')
    qq4=models.ClassList.objects.values('campuses').annotate(Count('campuses'))
    qq5=models.ClassList.objects.values('campuses').annotate(Count('campuses')).values('campuses__name','campuses__address','campuses__count')
    qq6=models.ClassList.objects.values('campuses').annotate(Count('campuses')).values()

    print(qq1)
    print(qq2)
    print(qq3)
    print(qq4)
    print(qq5)
    print(qq6)

"""
<QuerySet [{'address': '北京沙河', 'classlist__count': 11, 'id': 1, 'name': '北京校区'}, {'address': '上海浦东新区', 'classlist__count': 1, 'id': 2, 'name': '上海校区'}]>
<QuerySet [{'address': '北京沙河', 'classlist__count': 11, 'id': 1, 'name': '北京校区'}, {'address': '上海浦东新区', 'classlist__count': 1, 'id': 2, 'name': '上海校区'}]>
<QuerySet [{'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 1}, {'campuses': 2}]>
<QuerySet [{'campuses__count': 11, 'campuses': 1}, {'campuses__count': 1, 'campuses': 2}]>
<QuerySet [{'campuses__count': 11, 'campuses__name': '北京校区', 'campuses__address': '北京沙河'}, {'campuses__count': 1, 'campuses__name': '上海校区', 'campuses__address': '上海浦东新区'}]>
<QuerySet [{'memo': None, 'campuses__count': 1, 'semester': 1, 'id': 1, 'class_type': 'fulltime', 'price': 10000, 'graduate_date': datetime.date(2020, 3, 25), 'start_date': datetime.date(2020, 3, 25), 'course': 'Linux', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 2, 'id': 2, 'class_type': 'online', 'price': 10000, 'graduate_date': datetime.date(2020, 3, 25), 'start_date': datetime.date(2020, 3, 25), 'course': 'PythonFullStack', 'campuses_id': 2}, {'memo': None, 'campuses__count': 1, 'semester': 99, 'id': 3, 'class_type': 'weekend', 'price': 10000, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 3, 'id': 4, 'class_type': 'fulltime', 'price': 100, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'PythonFullStack', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 4, 'id': 5, 'class_type': 'online', 'price': 10, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}, 
{'memo': None, 'campuses__count': 1, 'semester': 5, 'id': 6, 'class_type': 'weekend', 'price': 1, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'PythonFullStack', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 10, 'id': 7, 'class_type': 'fulltime', 'price': 1, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}, 
{'memo': None, 'campuses__count': 1, 'semester': 11, 'id': 8, 'class_type': 'fulltime', 'price': 1, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 12, 'id': 9, 'class_type': 'fulltime', 'price': 1, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 13, 'id': 10, 'class_type': 'fulltime', 'price': 1, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 14, 'id': 11, 'class_type': 'fulltime', 'price': 1, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}, {'memo': None, 'campuses__count': 1, 'semester': 15, 'id': 12, 'class_type': 'fulltime', 'price': 1, 'graduate_date': None, 'start_date': datetime.date(2020, 2, 2), 'course': 'Linux', 'campuses_id': 1}]>
"""
View Code

4、F 与 Q查询

使用F表达式引用模型的字段

from django.shortcuts import render, HttpResponse
from app01.models import *
from django.db.models import F,Q
 
def query(request):
    #################### F查询####################
    #两个字段的值做比较, F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
 
    # 查询评论数大于收藏数的书籍
    Book.objects.filter(commnetNum__lt=F('keepNum'))
 
    # 查询评论数大于收藏数2倍的书籍
    Book.objects.filter(commnetNum__lt=F('keepNum') * 2)
 
    #修改操作也可以使用F函数,比如将每一本书的价格提高30元:
    Book.objects.all().update(price=F("price") + 30)
 
    ####################Q查询####################
    #filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。
 
    #Q对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。
    bookList = Book.objects.filter(Q(authors__name="yuan") | Q(authors__name="egon"))
    #sql ...WHERE name ="yuan" OR name ="egon"
 
    #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"
                                   )
    return HttpResponse("查询成功")
View Code

5、extra

有些情况下,Django的查询语法难以简单的表达复杂的 WHERE 子句,对于这种情况, Django 提供了 extra() QuerySet修改机制 它能在 QuerySet生成的SQL从句中注入新子句,extra可以指定一个或多个 参数,例如 select, where or tables.这些参数都不是必须的,但是你至少要使用一个!要注意这些额外的方式对不同的数据库引擎可能存在移植性问题.(因为你在显式的书写SQL语句),除非万不得已,尽量避免这样做.

# 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
 
# 查询每一个分类名称以及对应的文章数
ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
 
# 查询当前站点的每一个分类名称以及对应的文章数
cate_list=models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list("title","c")
 
# 查询当前站点的每一个标签名称以及对应的文章数
tag_list=models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title","c")
 
# 查询当前站点每一个年月的名称以及对应的文章数
 
"""
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
 
参数之select
        The select 参数可以让你在 SELECT 从句中添加其他字段信息,它应该是一个字典,存放着属性名到 SQL 从句的映射。
        queryResult=models.Article
                   .objects.extra(select={'is_recent': "create_time > '2017-09-05'"})
        结果集中每个 Entry 对象都有一个额外的属性is_recent, 它是一个布尔值,表示 Article对象的create_time 是否晚于2017-09-05.
"""
 
 
ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
# 方式1:
# mysql
date_list=models.Article.objects.filter(user=user).extra(select={"y_m_date":"date_format(create_time,'%%Y/%%m')"}
                                                         ).values("y_m_date").annotate(c=Count("nid")).values_list("y_m_date","c")
# sqlite
date_list=models.Article.objects.filter(user=user).extra(select={"y_m_date":"strftime('%%Y/%%m',create_time)"}
                                                         ).values("y_m_date").annotate(c=Count("nid")).values_list("y_m_date","c")
 
# 方式2:
from django.db.models.functions import TruncMonth
ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
 
# 日期归档查询的方式2
from django.db.models.functions import TruncMonth
"""
Sales.objects
.annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
.values('month')  # Group By month
.annotate(c=Count('id'))  # Select the count of the grouping
.values('month', 'c')  # (might be redundant, haven't tested) select month and count
"""
 
"""
参数之where
"""
val="2018-10"
# mysql
all_count = models.Article.objects.filter(blog=blog).extra(
    where=['date_format(create_time,"%%Y-%%m")=%s'], params=[val, ]).count()
article_list = models.Article.objects.filter(blog=blog).extra(
    where=['date_format(create_time,"%%Y-%%m")=%s'], params=[val, ])[page_info.start():page_info.end()]
# sqlite
all_count = models.Article.objects.filter(blog=blog).extra(
    where=['strftime("%%Y-%%m",create_time)=%s'], params=[val, ]).count()
article_list = models.Article.objects.filter(blog=blog).extra(
    where=['strftime("%%Y-%%m",create_time)=%s'], params=[val, ])[page_info.start():page_info.end()]
 
#直接写sql语句
# mysql
cursor = connection.cursor()
sql = 'select count(1) as nm,date_format(create_time,"%%Y-%%m") as ctime from article where blog_id= %s group by date_format(create_time,"%%Y-%%m")'
cursor.execute(sql,(blog.nid,))
date_list = cursor.fetchall()
# sqlite
date_list = models.Article.objects.raw(
    'select nid, count(1) as num,strftime("%Y-%m",create_time) as ctime from article group by strftime("%Y-%m",create_time)')
View Code

6、pk

主键的快捷查询方式:pk

Django ORM 中的批量操作

1、数据模型定义

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)



2、批量插入数据

批量插入数据的时候,首先要创建一个对象的列表,然后调用bulk_create方法,一次将列表中的数据插入到数据库中。

product_list_to_insert = list()
for x in range(10):
    product_list_to_insert.append(Product(name='product name ' + str(x), price=x))
Product.objects.bulk_create(product_list_to_insert)



3、批量更新数据

批量更新数据时,先进行数据过滤,然后再调用update方法进行一次性地更新。下面的语句将生成类似update....frrom....的SQL语句。

Product.objects.filter(name__contains='name').update(name='new name')



4、批量删除数据

批量更新数据时,先是进行数据过滤,然后再调用delete方法进行一次性删除。下面的语句讲生成类似delete from ... where ... 的SQL语句。

Product.objects.filter(name__contains='name query').delete()
Django ORM 中的批量操作

7、blog

from django.db import models


# from django.contrib.auth.models import AbstractUser
# class UserInfo(AbstractUser):
#     """
#     用户信息
#     """
#     nid = models.AutoField(primary_key=True)
#     telephone = models.CharField(max_length=11, null=True, unique=True)
#     avatar = models.FileField(upload_to='avatars/', default="avatars/default.png")
#     create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
#
#     blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE)
#
#     def __str__(self):
#         return self.username
#
#     class Meta:
#         verbose_name = '个人博客信息'
#         verbose_name_plural = '博客信息'


class UserInfo(models.Model):
    """
    用户表
    """
    nid = models.BigAutoField(primary_key=True)
    username = models.CharField(verbose_name='用户名', max_length=32, unique=True)
    password = models.CharField(verbose_name='密码', max_length=64)
    nickname = models.CharField(verbose_name='昵称', max_length=32)
    email = models.EmailField(verbose_name='邮箱', unique=True)

    avatar = models.FileField(verbose_name='头像',upload_to='avatars/', default="avatars/default.png")
    # 被传到`MEDIA_ROOT/uploads/2015/01/30`目录,增加了一个时间划分
    # avatar = models.FileField(upload_to='uploads/%Y/%m/%d/')

    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    fans = models.ManyToManyField(verbose_name='粉丝们',
                                  to='UserInfo',
                                  through='UserFans',
                                  related_name='f',
                                  through_fields=('user', 'follower'))
    class Meta:
        verbose_name = '个人博客信息'
        verbose_name_plural = '博客信息'


class Blog(models.Model):
    """
    博客信息
    """
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(verbose_name='个人博客标题', max_length=64)
    site = models.CharField(verbose_name='站点名称', max_length=32, unique=True)
    theme = models.CharField(verbose_name='博客主题', max_length=32)
    user = models.OneToOneField(to='UserInfo', to_field='nid',on_delete=models.CASCADE)

    theme_choices = [
        ('default', "默认主题"),
        ('bule', "蓝色主题"),
        ('red', "红色主题"),
    ]

    def __str__(self):

        return self.title

    class Meta:
        verbose_name = '个人博客信息'
        verbose_name_plural = '博客信息'

class UserFans(models.Model):
    """
    互粉关系表
    """
    user = models.ForeignKey(
        verbose_name='博主', to='UserInfo', to_field='nid', related_name='users',on_delete=models.CASCADE
    )
    follower = models.ForeignKey(
        verbose_name='粉丝', to='UserInfo', to_field='nid', related_name='followers',on_delete=models.CASCADE
    )

    class Meta:
        verbose_name = '互粉关系'
        verbose_name_plural = '互粉关系'
        unique_together = [
            ('user', 'follower'),
        ]

class Category(models.Model):
    """
    博主个人文章分类表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='分类标题', max_length=32)
    parent = models.ForeignKey(verbose_name='父ID', to='Category', to_field='nid', on_delete=models.CASCADE, null=True)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid',on_delete=models.CASCADE)
    class Meta:
        verbose_name = '文章分类'
        verbose_name_plural = '文章分类'

class ArticleDetail(models.Model):
    """
    文章详细表
    """
    content = models.TextField(verbose_name='文章内容', )

    article = models.OneToOneField(verbose_name='所属文章', to='Article', to_field='nid',on_delete=models.CASCADE)

    class Meta:
        verbose_name = '文章详细'
        verbose_name_plural = '文章详细'

class UpDown(models.Model):
    """
    文章顶或踩
    """
    article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid',on_delete=models.CASCADE)
    user = models.ForeignKey(verbose_name='赞或踩用户', to='UserInfo', to_field='nid',on_delete=models.CASCADE)
    up = models.BooleanField(verbose_name='是否赞')

    class Meta:
        verbose_name = '文章顶踩'
        verbose_name_plural = '文章顶踩'
        unique_together = [
            ('article', 'user'),
        ]


class Comment(models.Model):
    """
    评论表
    """
    nid = models.BigAutoField(primary_key=True)
    content = models.CharField(verbose_name='评论内容', max_length=255)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    reply = models.ForeignKey(verbose_name='回复评论', to='self', related_name='back', null=True,on_delete=models.CASCADE)
    article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid',on_delete=models.CASCADE)
    user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid',on_delete=models.CASCADE)
    class Meta:
        verbose_name = '文章评论'
        verbose_name_plural = '文章评论'

class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='标签名称', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid',on_delete=models.CASCADE)
    class Meta:
        verbose_name = '文章标签'
        verbose_name_plural = '文章标签'


class Article(models.Model):
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(verbose_name='文章标题', max_length=128)
    summary = models.CharField(verbose_name='文章简介', max_length=255)
    read_count = models.IntegerField(default=0)
    comment_count = models.IntegerField(default=0)
    up_count = models.IntegerField(default=0)
    down_count = models.IntegerField(default=0)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid',on_delete=models.CASCADE)
    category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True,on_delete=models.CASCADE)

    type_choices = [
        (1, "编程语言"),
        (2, "操作系统"),
        (3, "Web前端"),
        (4, "数据库"),
        (5, "English"),
        (6, "其他分类"),
    ]

    article_type_id = models.IntegerField(choices=type_choices, default=None)

    tags = models.ManyToManyField(
        to="Tag",
        through='Article2Tag',
        through_fields=('article', 'tag'),
    )

    class Meta:
        verbose_name = '文章信息'
        verbose_name_plural = '文章信息'

class Article2Tag(models.Model):
    article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid',on_delete=models.CASCADE)
    tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid',on_delete=models.CASCADE)

    class Meta:
        verbose_name = '文章与标签'
        verbose_name_plural = '文章与标签'
        unique_together = [
            ('article', 'tag'),
        ]


class Tpl(models.Model):
    user = models.ForeignKey(UserInfo, on_delete=models.CASCADE)
    title = models.CharField(max_length=32)
    content = models.TextField()


class Trouble(models.Model):
    title = models.CharField(max_length=32)
    detail = models.TextField()
    user = models.ForeignKey(UserInfo,related_name='u',on_delete=models.CASCADE)
    # ctime = models.CharField(max_length=32) # 1491527007.452494
    ctime = models.DateTimeField()
    status_choices = (
        (1,'未处理'),
        (2,'处理中'),
        (3,'已处理'),
    )
    status = models.IntegerField(choices=status_choices,default=1)

    processer = models.ForeignKey(UserInfo,related_name='p',null=True,blank=True,on_delete=models.CASCADE)
    solution = models.TextField(null=True)
    ptime = models.DateTimeField(null=True)
    pj_choices = (
        (1, '不满意'),
        (2, '一般'),
        (3, '很满意'),
    )
    pj = models.IntegerField(choices=pj_choices,null=True,default=2)


"""
form中:
from django.forms import fields
    theme = fields.ChoiceField(
        choices=models.Blog.theme_choices,
        widget=widgets.Select(attrs={'class': 'form-control'})
    )
模板中:
{{ form.theme }}


models中:
    status_choices = (
        (1,'未处理'),
        (2,'处理中'),
        (3,'已处理'),
    )
    status = models.IntegerField(choices=status_choices,default=1)
    ptime = models.DateTimeField(null=True,blank=True)

views中:
    dic['ptime'] = datetime.datetime.now()

模板中:
{% for row in result %}
<td>{{ row.get_status_display }}</td>
{% endfor %}


models中:
    type_choices = [
        (1, "编程语言"),
        (2, "操作系统"),
        (3, "Web前端"),
        (4, "数据库"),
        (5, "English"),
        (6, "其他分类"),
    ]

views:
    article_type_list = models.Article.type_choices

"""
blog models
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_contenttype.settings")

    import django
    django.setup()

    from app01.models import Post, Picture, Comment
    from django.contrib.auth.models import User
    # 准备测试数据
    user_1 = User.objects.create_user(username='aaa', password='123')
    user_2 = User.objects.create_user(username='bbb', password='123')
    user_3 = User.objects.create_user(username='ccc', password='123')

    post_1 = Post.objects.create(author=user_1, title='Python入门教程')
    post_2 = Post.objects.create(author=user_2, title='Python进阶教程')
    post_3 = Post.objects.create(author=user_1, title='Python入土教程')

    picture_1 = Picture.objects.create(author=user_1, image='小姐姐01.jpg')
    picture_2 = Picture.objects.create(author=user_1, image='小姐姐02.jpg')
    picture_3 = Picture.objects.create(author=user_3, image='小哥哥01.jpg')

    # 给帖子创建评论数据
    comment_1 = Comment.objects.create(author=user_1, content='好文!', content_object=post_1)
    # 给图片创建评论数据
    comment_2 = Comment.objects.create(author=user_2, content='好美!', content_object=picture_1)
django.setup()

六、模板

<!--查看所有 views.py 传过来的book_list    book_list=Book.objects.all()-->
<tbody>
              {% for book in book_list %}
              <tr>
                   <td>{{ forloop.counter }}</td>
                   <td>{{ book.title }}</td>
                   <td>{{ book.price }}</td>
                   <td>{{ book.publishDate|date:"Y-m-d" }}</td>
                   <td>
                       {{ book.publish.name }}
                   </td>
                   <td>
                       {% for author in book.authors.all %}
                          {% if forloop.last %}
                           <span>{{ author.name }}</span>
                          {% else %}
                           <span>{{ author.name }}</span>,
                          {% endif %}
                       {% endfor %}
                   </td>
                   <td>
                       <a href="/books/{{ book.pk }}/change/" class="btn btn-warning">编辑</a>
                       <a href="/books/{{ book.pk }}/delete/" class="btn btn-danger">删除</a>
                   </td>
              </tr>
              {% endfor %}
</tbody>
 
 
<!--编辑  views.py 传过来的edit_book_obj    publish_list=Publish.objects.all()   author_list=Author.objects.all()-->
 <form action="" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <label for="">名称</label>
                    <input type="text" name="title" class="form-control" value="{{ edit_book_obj.title }}">
                </div>
 
                <div class="form-group">
                    <label for="">价格</label>
                    <input type="text" name="price" class="form-control" value="{{ edit_book_obj.price }}">
                </div>
 
                <div class="form-group">
                    <label for="">出版日期</label>
                    <input type="date" name="pub_date" class="form-control"
                           value="{{ edit_book_obj.publishDate|date:'Y-m-d' }}">
                </div>
 
                <div class="form-group">
                    <label for="">出版社</label>
                    <select name="publish_id" id="" class="form-control">
                        {% for publish in publish_list %}
                            {% if edit_book_obj.publish == publish %}
                                <option selected value="{{ publish.pk }}">{{ publish.name }}</option>
                            {% else %}
                                <option value="{{ publish.pk }}">{{ publish.name }}</option>
                            {% endif %}
                        {% endfor %}
                    </select>
                </div>
 
                <div class="form-group">
                    <label for="">作者</label>
                    <select type="text" name="authors_id_list" multiple class="form-control">
                        {% for author in author_list %}
 
                        {% if author in edit_book_obj.authors.all %}
                            <option selected value="{{ author.pk }}">{{ author.name }}</option>
                        {% else %}
                             <option value="{{ author.pk }}">{{ author.name }}</option>
                        {% endif %}
 
                        {% endfor %}
                    </select>
                </div>
                <input type="submit" class="btn btn-default">
            </form>
View Code

七、返回新QuerySets的API

方法名解释
filter() 过滤查询对象。
exclude() 排除满足条件的对象
annotate() 使用聚合函数
order_by() 对查询集进行排序
reverse() 反向排序
distinct() 对查询集去重
values() 返回包含对象具体值的字典的QuerySet
values_list() 与values()类似,只是返回的是元组而不是字典。
dates() 根据日期获取查询集
datetimes() 根据时间获取查询集
none() 创建空的查询集
all() 获取所有的对象
union() 并集
intersection() 交集
difference() 差集
select_related() 附带查询关联对象
prefetch_related() 预先查询
extra() 附加SQL查询
defer() 不加载指定字段
only() 只加载指定的字段
using() 选择数据库
select_for_update() 锁住选择的对象,直到事务结束。
raw() 接收一个原始的SQL查询

八、不返回QuerySets的API

方法名解释
get() 获取单个对象
create() 创建对象,无需save()
get_or_create() 查询对象,如果没有找到就新建对象
update_or_create() 更新对象,如果没有找到就创建对象
bulk_create() 批量创建对象
count() 统计对象的个数
in_bulk() 根据主键值的列表,批量返回对象
iterator() 获取包含对象的迭代器
latest() 获取最近的对象
earliest() 获取最早的对象
first() 获取第一个对象
last() 获取最后一个对象
aggregate() 聚合操作
exists() 判断queryset中是否有对象
update() 批量更新对象
delete() 批量删除对象
as_manager() 获取管理器

九、Django 字段查询参数及聚合函数

字段查询是指如何指定SQL WHERE子句的内容。它们用作QuerySet的filter(), exclude()和get()方法的关键字参数。

字段名 含义 示例 等价SQL语句
exact 精确等于 Comment.objects.filter(id__exact=14) select * from Comment where id=14
iexact 大小写不敏感的等于 Comment.objects.filter(headline__iexact=’I like this’) select * from Comment where upper(headline)=’I LIKE THIS’
contains 模糊匹配 Comment.objects.filter(headline__contains=’good’) select * from Comment where headline like “%good%”
icontains 不区分大小写的包含匹配    
in 包含 Comment.objects.filter(id__in=[1,5,9]) select * from Comment where id in (1,5,9)
gt 大于 Comment.objects.filter(n_visits__gt=30) select * from Comment where n_visits>30
gte 大于等于 Comment.objects.filter(n_visits__gte=30) select * from COmment where n_visits>=30
lt 小于    
lte 小于等于    
startswith 以…开头 Comment.objects.filter(body_text__startswith=”Hello”) select * from Comment where body_text like ‘Hello%’
istartswith 不区分大小写从开头匹配    
endswith 以…结尾    
iendswith 不区分大小写从结尾处匹配    
range 在…范围内 start_date=datetime.date(2015,1,1)
end_date=datetime.date(2015.2.1)
Comment.objects.filter(
    pub_date__range=(start_date,end_date)
)
select * from Comment
where pub_date
between ‘2015-1-1’ and ‘2015-2-1’
date 日期匹配    
year Comment.objects.filter(
    pub_date__year=2015
)
select * from Comment
where pub_date
between ‘2015-1-1 0:0:0’
and ‘2015-12-31 23:59:59’
month    
day    
week 第几周    
week_day 星期几    
time 时间    
hour 小时    
minute 分钟    
second    
search 1.10中被废弃    
regex 区分大小写的正则匹配    
iregex 不区分大小写的正则匹配    
isnull 是否为空 Comment.objects.filter(
    pub_date__isnull=True
)
select * from Comment where
pub_date is NULL

filter(**kwargs): 返回符合筛选条件的数据集

exclude(**kwargs):   返回不符合筛选条件的数据集

Comment.objects.filter(pub_date__year=2015)

多个filter和exclude可以链接在一起查询

Comment.objects.filter(pub_date__year=2015).exclude(pub_date__month=1).exclude(n_visits__exact=0)

get() :查询单条记录,注意没有查询到数据的时候会报错

Comment.objects.get(id_exact=1)

all():  查询所有数据

Comment.objects.all()
Comment.objects.all()[:10]
Comment.objects.all[10:20]

order_by():   排序

Comment.objects.order_by('headline')
import os

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

    from app01 import models

    # pc1 = models.PC.objects.create(name='华为')
    # pc2 = models.PC.objects.create(name='联想')
    # pc3 = models.PC.objects.create(name='小米')
    #
    # phone1 = models.Phone.objects.create(name='华为')
    # phone2 = models.Phone.objects.create(name='vivo')
    # phone3 = models.Phone.objects.create(name='oppo')
    #
    # f1 = models.FuZhuang.objects.create(name='阿玛尼')
    # f2 = models.FuZhuang.objects.create(name='范思哲')
    # f3 = models.FuZhuang.objects.create(name='美特斯邦威')
    #
    # models.Coupon.objects.create(info='全场1折', content_object=pc1)
    # models.Coupon.objects.create(info='满300减30', content_object=phone3)

    # 查询 华为PC1的优惠券

    pc1 = models.PC.objects.first()
    models.Coupon.objects.create(info='买10送1', content_object=pc1)
    ret = pc1.coupons.all()
    print(ret)
django.setup()
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 PC(models.Model):
    name = models.CharField(max_length=32)
    # 不会在数据库创建字段,只是提供了一个反向查询的方法
    coupons = GenericRelation(to='Coupon')


class Phone(models.Model):
    name = models.CharField(max_length=32)

    coupons = GenericRelation(to='Coupon')


class FuZhuang(models.Model):
    name = models.CharField(max_length=32)

    coupons = GenericRelation(to='Coupon')


class Coupon(models.Model):
    info = models.CharField(max_length=128)
    # 1. 外键关联 ContentType这张表(自带的) --> 确定了关联哪张表
    content_type = models.ForeignKey(to=ContentType)
    # 2. 只要是一个能保存数据库id的字段就可以  --> 确定数据
    object_id = models.PositiveIntegerField()
    # 3. 告诉Django由上面两条字段唯一确定关联的一条记录
    content_object = GenericForeignKey()

    def __str__(self):
        return self.info
models.py

Django ORM 中的批量操作

数据模型定义

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)

批量插入数据

批量插入数据的时候,首先要创建一个对象的列表,然后调用bulk_create方法,一次将列表中的数据插入到数据库中。

product_list_to_insert = list()
for x in range(10):
    product_list_to_insert.append(Product(name='product name ' + str(x), price=x))
Product.objects.bulk_create(product_list_to_insert)

批量更新数据

批量更新数据时,先进行数据过滤,然后再调用update方法进行一次性地更新。下面的语句将生成类似update....frrom....的SQL语句。

Product.objects.filter(name__contains='name').update(name='new name')

批量删除数据

批量更新数据时,先是进行数据过滤,然后再调用delete方法进行一次性删除。下面的语句讲生成类似delete from ... where ... 的SQL语句。

Product.objects.filter(name__contains='name query').delete()
import os

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

    from app01 import models
    from django.db.models import F, Q

    # news=[
    #     [("authors__name", "alex"), ("authors__age","18")],
    #     [("authors__name", "wusir"), ("authors__age","20")]
    # ]

    news = models.Book.objects.all().values('authors__name','authors__age')

    q_out = Q()
    q_out.connector = 'or'
    for item in news:
        q = Q()
        q.connector = 'and'
        for k,v in item.items():
            q.children.append((k,v))
        q_out.children.append(Q(q))

    bookList = models.Book.objects.filter(q_out)
    print(bookList)



########################
# # 模糊查询
# filter(Q(__contains=query) | Q())
# Q(Q(qq__contains=query) | Q(name__contains=query))
#
# field_list = []
#
# q = Q()
# q.connector = 'OR'
# # q.children.append(Q(qq__contains=query))
#
# # Q(qq__contains=query)  等价于   Q(('qq__contains',query))
# for field in field_list:
#     q.children.append(Q(('{}__contains'.format(field), query)))
########################
Q

Django之Model操作

一、字段

    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

    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)
        - 二进制类型
字段列表
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)',
自定义无符号整数字段
    1.触发Model中的验证和错误提示有两种方式:
        a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息
        b. 使用ModelForm
        c. 调用Model对象的 clean_fields 方法,如:
            # models.py
            class UserInfo(models.Model):
                nid = models.AutoField(primary_key=True)
                username = models.CharField(max_length=32)

                email = models.EmailField(error_messages={'invalid': '格式错了.'})

            # views.py
            def index(request):
                obj = models.UserInfo(username='11234', email='uu')
                try:
                    print(obj.clean_fields())
                except Exception as e:
                    print(e)
                return HttpResponse('ok')

           # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。

    2.Admin中修改错误提示
        # admin.py
        from django.contrib import admin
        from model_club import models
        from django import forms


        class UserInfoForm(forms.ModelForm):
            age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'})

            class Meta:
                model = models.UserInfo
                # fields = ('username',)
                fields = "__all__"
                exclude = ['title']
                labels = { 'name':'Writer', }
                help_texts = {'name':'some useful help text.',}
                error_messages={ 'name':{'max_length':"this writer name is too long"} }
                widgets={'name':Textarea(attrs={'cols':80,'rows':20})}

        class UserInfoAdmin(admin.ModelAdmin):
            form = UserInfoForm

        admin.site.register(models.UserInfo, UserInfoAdmin)
注意事项

二、字段参数

    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_112233\d+', message='又错误了', code='c2'),
                                    EmailValidator(message='又错误了', code='c3'), ]
                            )
View Code

三、元信息

    class UserInfo(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32)
        class Meta:
            # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
            db_table = "table_name"

            # 联合索引
            index_together = [
                ("pub_date", "deadline"),
            ]

            # 联合唯一索引
            unique_together = (("driver", "restaurant"),)

            # admin中显示的表名称
            verbose_name

            # verbose_name加s
            verbose_name_plural
View Code

更多:https://docs.djangoproject.com/en/1.10/ref/models/options/

四、多表关系以及参数

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

五、ORM操作

        #
        #
        # models.Tb1.objects.create(c1='xx', c2='oo')  增加一条数据,可以接受字典类型数据 **kwargs

        # 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.exclude(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()                                                 # 修改单条数据
基本操作
        # 获取个数
        #
        # models.Tb1.objects.filter(name='seven').count()

        # 大于,小于
        #
        # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
        # models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
        # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
        # models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
        # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

        # in
        #
        # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
        # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

        # isnull
        # Entry.objects.filter(pub_date__isnull=True)

        # contains
        #
        # models.Tb1.objects.filter(name__contains="ven")
        # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
        # models.Tb1.objects.exclude(name__icontains="ven")

        # range
        #
        # models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and

        # 其他类似
        #
        # startswith,istartswith, endswith, iendswith,

        # order by
        #
        # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
        # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

        # group by
        #
        # from django.db.models import Count, Min, Max, Sum
        # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
        # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

        # limit 、offset
        #
        # models.Tb1.objects.all()[10:20]

        # regex正则匹配,iregex 不区分大小写
        #
        # Entry.objects.get(title__regex=r'^(An?|The) +')
        # Entry.objects.get(title__iregex=r'^(an?|the) +')

        # date
        #
        # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
        # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

        # year
        #
        # Entry.objects.filter(pub_date__year=2005)
        # Entry.objects.filter(pub_date__year__gte=2005)

        # month
        #
        # Entry.objects.filter(pub_date__month=12)
        # Entry.objects.filter(pub_date__month__gte=6)

        # day
        #
        # Entry.objects.filter(pub_date__day=3)
        # Entry.objects.filter(pub_date__day__gte=3)

        # week_day
        #
        # Entry.objects.filter(pub_date__week_day=2)
        # Entry.objects.filter(pub_date__week_day__gte=2)

        # hour
        #
        # Event.objects.filter(timestamp__hour=23)
        # Event.objects.filter(time__hour=5)
        # Event.objects.filter(timestamp__hour__gte=12)

        # minute
        #
        # Event.objects.filter(timestamp__minute=29)
        # Event.objects.filter(time__minute=46)
        # Event.objects.filter(timestamp__minute__gte=29)

        # second
        #
        # Event.objects.filter(timestamp__second=31)
        # Event.objects.filter(time__second=2)
        # Event.objects.filter(timestamp__second__gte=31)
进阶操作
        # extra
        #
        # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
        #    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
        #    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
        #    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
        #    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

        # F
        #
        # from django.db.models import F
        # models.Tb1.objects.update(num=F('num')+1)


        # Q
        #
        # 方式一:
        # Q(nid__gt=10)
        # Q(nid=8) | Q(nid__gt=10)
        # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')

        # 方式二:
        # con = Q()
        # q1 = Q()
        # q1.connector = 'OR'
        # q1.children.append(('id', 1))
        # q1.children.append(('id', 10))
        # q1.children.append(('id', 9))
        # q2 = Q()
        # q2.connector = 'OR'
        # q2.children.append(('c1', 1))
        # q2.children.append(('c1', 10))
        # q2.children.append(('c1', 9))
        # con.add(q1, 'AND')
        # con.add(q2, 'AND')
        #
        # models.Tb1.objects.filter(con)


        # 执行原生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()
高级操作
##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################

def all(self)
    # 获取所有的数据对象

def filter(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def select_related(self, *fields)
     性能相关:表之间进行join连表操作,一次性获取关联的数据。
     model.tb.objects.all().select_related()
     model.tb.objects.all().select_related('外键字段')
     model.tb.objects.all().select_related('外键字段__外键字段')

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
            # 获取所有用户表
            # 获取用户类型表where id in (用户表中的查到的所有用户ID)
            models.UserInfo.objects.prefetch_related('外键字段')



            from django.db.models import Count, Case, When, IntegerField
            Article.objects.annotate(
                numviews=Count(Case(
                    When(readership__what_time__lt=treshold, then=1),
                    output_field=CharField(),
                ))
            )

            students = Student.objects.all().annotate(num_excused_absences=models.Sum(
                models.Case(
                    models.When(absence__type='Excused', then=1),
                default=0,
                output_field=models.IntegerField()
            )))

def annotate(self, *args, **kwargs)
    # 用于实现聚合group by查询

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

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    # 用于distinct去重
    models.UserInfo.objects.values('nid').distinct()
    # select distinct nid from userinfo

    注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
    # 用于排序
    models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    # 构造额外的查询条件或者映射,如:子查询

    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

 def reverse(self):
    # 倒序
    models.UserInfo.objects.all().order_by('-nid').reverse()
    # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


 def defer(self, *fields):
    models.UserInfo.objects.defer('username','id')
    或
    models.UserInfo.objects.filter(...).defer('username','id')
    #映射中排除某列数据

 def only(self, *fields):
    #仅取某个表中的数据
     models.UserInfo.objects.only('username','id')
     或
     models.UserInfo.objects.filter(...).only('username','id')

 def using(self, alias):
     指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

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")

    ################### 原生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(..)


def values(self, *fields):
    # 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
    # 获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
    # 根据时间进行某一部分进行去重查找并截取指定内容
    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    # order只能是:"ASC"  "DESC"
    # 并获取转换后的时间
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日

    models.DatePlus.objects.dates('ctime','day','DESC')

def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    # kind只能是 "year", "month", "day", "hour", "minute", "second"
    # order只能是:"ASC"  "DESC"
    # tzinfo时区对象
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

    """
    pip3 install pytz
    import pytz
    pytz.all_timezones
    pytz.timezone(‘Asia/Shanghai’)
    """

def none(self):
    # 空QuerySet对象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
   # 聚合函数,获取字典类型聚合结果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
   ===> {'k': 3, 'n': 4}

def count(self):
   # 获取个数

def get(self, *args, **kwargs):
   # 获取单个对象

def create(self, **kwargs):
   # 创建对象

def bulk_create(self, objs, batch_size=None):
    # 批量插入
    # batch_size表示一次插入的个数
    objs = [
        models.DDD(name='r11'),
        models.DDD(name='r22')
    ]
    models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
    # 如果存在,则获取,否则,创建
    # defaults 指定创建时,其他字段的值
    obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
    # 如果存在,则更新,否则,创建
    # defaults 指定创建时或更新时的其他字段
    obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
   # 获取第一个

def last(self):
   # 获取最后一个

def in_bulk(self, id_list=None):
   # 根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
   # 删除

def update(self, **kwargs):
    # 更新

def exists(self):
   # 是否有结果
其他操作
...
连表正反操作

六、其他

import pymysql
    from django.db import connection, connections
 
    connection.connect()
    conn = connection.connection
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute("""SELECT * from app01_userinfo""")
    row = cursor.fetchone()
    connection.close()
Django原生SQL获取cursor字典
    
    # 数字自增
    from django.db.models import F
    models.UserInfo.objects.update(num=F('num') + 1)

    # 字符串更新
    from django.db.models.functions import Concat
    from django.db.models import Value

    models.UserInfo.objects.update(name=Concat('name', 'pwd'))
    models.UserInfo.objects.update(name=Concat('name', Value('666')))
数字自增、字符串更新
    # ########### 基础函数 ###########

    # 1. Concat,用于做类型转换
    # v = models.UserInfo.objects.annotate(c=Cast('pwd', FloatField()))

    # 2. Coalesce,从前向后,查询第一个不为空的值
    # v = models.UserInfo.objects.annotate(c=Coalesce('name', 'pwd'))
    # v = models.UserInfo.objects.annotate(c=Coalesce(Value('666'),'name', 'pwd'))

    # 3. Concat,拼接
    # models.UserInfo.objects.update(name=Concat('name', 'pwd'))
    # models.UserInfo.objects.update(name=Concat('name', Value('666')))
    # models.UserInfo.objects.update(name=Concat('name', Value('666'),Value('999')))

    # 4.ConcatPair,拼接(仅两个参数)
    # v = models.UserInfo.objects.annotate(c=ConcatPair('name', 'pwd'))
    # v = models.UserInfo.objects.annotate(c=ConcatPair('name', Value('666')))

    # 5.Greatest,获取比较大的值;least 获取比较小的值;
    # v = models.UserInfo.objects.annotate(c=Greatest('id', 'pwd',output_field=FloatField()))

    # 6.Length,获取长度
    # v = models.UserInfo.objects.annotate(c=Length('name'))

    # 7. Lower,Upper,变大小写
    # v = models.UserInfo.objects.annotate(c=Lower('name'))
    # v = models.UserInfo.objects.annotate(c=Upper('name'))

    # 8. Now,获取当前时间
    # v = models.UserInfo.objects.annotate(c=Now())

    # 9. substr,子序列
    # v = models.UserInfo.objects.annotate(c=Substr('name',1,2))

    # ########### 时间类函数 ###########
    # 1. 时间截取,不保留其他:Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,ExtractSecond, ExtractWeekDay, ExtractYear,
    # v = models.UserInfo.objects.annotate(c=functions.ExtractYear('ctime'))
    # v = models.UserInfo.objects.annotate(c=functions.ExtractMonth('ctime'))
    # v = models.UserInfo.objects.annotate(c=functions.ExtractDay('ctime'))
    #
    # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year'))
    # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'month'))
    # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year_month'))
    """
    MICROSECOND
    SECOND
    MINUTE
    HOUR
    DAY
    WEEK
    MONTH
    QUARTER
    YEAR
    SECOND_MICROSECOND
    MINUTE_MICROSECOND
    MINUTE_SECOND
    HOUR_MICROSECOND
    HOUR_SECOND
    HOUR_MINUTE
    DAY_MICROSECOND
    DAY_SECOND
    DAY_MINUTE
    DAY_HOUR
    YEAR_MONTH
    """

    # 2. 时间截图,保留其他:Trunc, TruncDate, TruncDay,TruncHour, TruncMinute, TruncMonth, TruncSecond, TruncYear
    # v = models.UserInfo.objects.annotate(c=functions.TruncHour('ctime'))
    # v = models.UserInfo.objects.annotate(c=functions.TruncDate('ctime'))
    # v = models.UserInfo.objects.annotate(c=functions.Trunc('ctime','year'))
ORM函数相关
    from django.db.models.functions.base import Func
    class CustomeFunc(Func):
        function = 'DATE_FORMAT'
        template = '%(function)s(%(expressions)s,%(format)s)'

        def __init__(self, expression, **extra):
            expressions = [expression]
            super(CustomeFunc, self).__init__(*expressions, **extra)

    v = models.UserInfo.objects.annotate(c=CustomeFunc('ctime',format="'%%Y-%%m'"))
ORM自定义函数

 

 

posted @ 2019-01-11 19:40  silencio。  阅读(397)  评论(0编辑  收藏  举报