Django-ORM框架

对象关系映射模型是通过面向对象的方式来操作数据库,这就需要对应的关系映射,数据中可以分为库,表,字段信息,一条条数据,而需要用面向对象的关系去对应。于是就有了下面对应关系。

数据库  --   面向对象模型
  表   <-->    类
 字段  <-->   类属性
 记录  <-->   每个实例

Django中的关系映射

使用面向对象的方式描述数据库的关系模型,Django采用了以下的方式。

class Employees(models.Model):   # 类名
    class Meta:                  # Meta类中指定表元数据
        db_table = "Employees"    # 指定数据库中的表名,否则为类名小写

    # 每一个model的Field类型属性都对应数据库表中的一条字段。数据类型通过不同类型的Field属性指定,约束条件通过参数传递的方式
    emp_no = models.AutoField(primary_key=True, null=False)  
    birth_data = models.DateField(null=False)                # 对应日期类型
    first_name = models.CharField(null=False, max_length=14)
    last_name = models.CharField(null=False, max_length=16)  # 对应varchar 类型
    gender = models.SmallIntegerField(null=False)            # 对应int类型
    hire_data = models.DateField(null=False)

Fieldl类型

常用类型说明
AutoField自增整数类型
BigAutoField更大自增整数类型
BigIntegerField大整数
BooleanField布尔类型True或者False
CharField字符串类型
DateField日期类型
DateTimeField日期时间
DecimalField使用python的Decimal实例表示10进制浮点数,需要极高精度的数值使用该字段
EmailField能做Email检验,基于CharField,最大254
FileField文件字段
FilePathField文件路径
FloatField浮点数
ImageField继承了FileField,但是对上传的文件进行检验,确保为一个图片
IntegerFieldint类型
GenericIPAddressFieldIpv4/Ipv6校验
NullBooleanField布尔值可以为空
PositiveIntegerField正整数
TextField大文本,超过4000字符
TimeField时间类型
URLField能做URL检验,最大200

关系映射

ForeignKey一对多关系

关系类型字段可以将两张表进行关联,该字段在一对多方字段建立。例如官方的实例,一个Manufacture可以生产多个车型的汽车,所以在车辆表中建立一个外键(多端),标识该汽车类属于哪一个Manufacture

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

创建外键至少需要指定两个参数,指定关联的类和on_delete,主表字段和从表字段之间联系,上面指定为级联,即主表中的某个数据删除,从表中关联了该主表数据的数据将会被删除。

上面Car中的manufacturer字段与Manufacture类进行了关联,在生成的manufacture表中,将会多出一个Car_set的字段,可以通过一条Manufacture信息查询出多条Car信息。而Car中manufacturer字段将被设置为manufacture_id,该数据将会关联到Manufacture中对应的数据。

ManyToManyField多对多关系

官网的示例:一种Topping可能存在于多个Pizza中,并且每个Pizza含有多种Topping,这就是一个多对多的关系

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

这个属性可以在关联的任何一个类中创建,不能同时在连个类中添加该字段。一般来讲,应该把 ManyToManyField 实例放到需要在表单中被编辑的对象中

在定义的字段类型中,指定某些参数选项,可以将这个字段做一些特殊的设置或者做一些特殊的验证。主要包括以下的字段选项。

字段参数选项

选项说明
null是否可以null
blank是否可以为空
choices类似枚举
db_column指定数据库列名
db_index是否在该字段建索引
db_tablespace 
default默认值
editable 
error_messages指定后覆盖默认的错误信息
help_text指定帮助信息
primary_key定义为主键
unique定义为唯一键
unique_for_date 
verbose_name字段备注名
validatorsmodels.IntegerField(validators=[validate_even]),添加验证函数

Manager管理器对象

管理器对象用于实现表的增删改查。管理器对象是类型为django.db.models.manager.Manager类型,管理器对象可以在定义模型类中创建,如果没有创建,默认会创建一个objects管理器,手动创建后将不会自动创建。

管理器Django查询数据库的接口,每一个模型都至少有一个管理器,用户自定义管理器模型需要继承于django.db.models.manager.Manager类。

内部测试 manager管理器

根下创建一个Python 文件,写入以下内容即可使用

# 从wsgi复制

import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault(‘DJANGO_SETTINGS_MODULE‘, ‘ORM.settings‘)
application = get_wsgi_application()

# 将模型类导入,也就是定义模型类的model文件,
from salary.models import Employees

# object 是Employees自带的一个管理器,这样我们可以使用这个管理进行查询
mgr = Employees.objects

print(mgr.all())

配置日志输出

import os

LOGGING = {
    ‘version‘: 1,
    ‘disable_existing_loggers‘: False,
    ‘handlers‘: {
        ‘console‘: {
            ‘class‘: ‘logging.StreamHandler‘,
        },
    },
    ‘loggers‘: {
        ‘django‘: {
            ‘handlers‘: [‘console‘],
            ‘level‘: os.getenv(‘DJANGO_LOG_LEVEL‘, ‘INFO‘),
        },
    },
}

查询

使用管理器对象查询,将会返回一个查询集,Django内部将这些结果封装为一个Django.db.models.manager.query.Query类型。这个结果是惰性(只有需要时才会真的查询),可以进行迭代。查询的结果不会缓存,再次使用必须再次从数据库查询。

mgr = Employees.objects

result_sset = mgr.all()  # 查询所有结果,将整个表查询返回结果集print(mgr.all())         # 获取数据时,才会执行sql查询

并且由于我们的日志配置,我们可以在日志中看到执行sql语句信息,这样方便我们对查询进行调优。

查询方法

方法说明
all()查询所有结果的查询集
filter()过滤查询,类似于where
exclude()过滤的结果取反
order_by()排序结果 类似于order by
values()返回这个查询集,每一个元素是{字段:值}的字典
方法说明
get(pk=1)返回单个值,没有数据和多条数据都将报错
count()返回查询的总条数
first()返回第一个对象
last()返回最后一个对象
exist()如果能查到返回True

条件表达式

filter,exclude,get等方法中需要控制查询条件。django使用了一套条件表达式作为查询方法中的参数,实现查询条件筛选。

表达式参数说明

=,

exact

iexact

等于,mgr.filter(id=1)

不等于 mgr.filter(id__iexact=3)

gt, gte大于,大于等于, mge.filter(id__gt=10001)
lt, lte小于,小于等于 mgr.exclude(id__lte=10010)

startwith

endswith

首尾匹配,大小写敏感

isnull

isnotnull

是否为null
in在某个范围,mgr.filter(pk__in=(1,2,3))

contains

icontains

包含指定的值,%值%,效率极低

iexact

icontains

istartwith

iendswith

i,表示忽略大小写

year

month

判断一个时间,需要时时间类型的字段,mgr.filter(birth_date__year=2000) 年份为2000的

Q 对象

在一个函数中连续写条件表示这些条件是and(与)关系,并不能表示(or)或(!)非的关系,此时需要使用Q对象,Q对象可以使用 |或, &与, ~非;来描述条件之间的关系。

Q 对象的类型:django.db.models.Q

from django.db.models import Q

# 使用 Q 对象处理这个条件,并使用 | 连接,表示或关系,& 表示与
mgr.filter( Q(id__lt=10005) | Q(lastname__startswith="K") & Q(age=12))  # 与或
mgr.filter( ~Q(id__lt=10005))                  # ~ 表示取反

分组,聚合

聚合函数

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

aggregate

将所有列的数据分为一组进行聚合

UseInfo.objects.all().aggregate(Sum("age"))   # 所有年龄之和,自动使用主键分组

返回一个字典{"age__sum": 120}, 如果想控制age_sum这个key的名字,使用aggregate(agesum=Sum("age"))key将会替换成agesum。

annotate()

annotate可以和value组合实现分组聚合

from django.db.models import Q, Avg, Sum, Max, Min, Count
# emp_no 员工编号
# salary 工资

# 使用emp_no分组,计算每个员工的工资总和
s2 = sal.all().values("emp_no").annotate(Sum("salary"))   # 返回查询后的结果集

# 只会显示两个字段
# <QuerySet [{‘emp_no‘: 1, ‘salary__sum‘: 1281612}, 
             {‘emp_no‘: 2, ‘salary__sum‘: 413127}]}>
# 显示所有字段使用values,或者在values中指定想要的字段,没有使用聚合函数的行使用第一行数据为准
s2.values()
# 还可以对其继续排序。等操作
s2.values().order_by("salary_sum")

Raw

直接使用原生的sql语句

empmgr = Employee.objects
raw_query = "select * from Employee where id > %s"

emp = empmgr.raw(raw_query, params=None)

extra

表示添加额外的部分,select参数表示再select处添加字段以及值,所以使用字段key-value,where表示添加额外的条件,使用列表,and连接内部元素。tables参数表示关联的表。

Userinfo.objects.all().values("id", name)
            .extra(
                select={
                    "total":"select count("id") from table ",
                    "height":"select count("height") from table where gengder=%s",
                    },
                select_params=[1,男] 
                where=["user_type.id=userinfo.type_id"],
                params=[0, 18]
                tables="app01.user_type"
            )
    等价的sql
        "select 
        id, name, 
        select count("id") from table as "total", 
        select count("height") from table as height
        
        form (userinfo, app01.user_type) 
        where user_type.id=userinfo.type_id

select_related

会根据指定的字段做连表查询

Userinfo.objects.all().select_related("group")    # 查询group 表的内容

select * from `userinfo` inner join `group` on userinfo.group_id = group.id

prefetch_related

效果同select_releted,但是执行多次单表查询,用连表字段的条件进行查询。

Userinfo.objects.all().perfetch_releted("group")    

select 其他字段, group_id from userinfo              # 查处所有group_id
select * from group where group.id in group_ids     # 更具id查出所有组

ManytoMany

class Boy:
    id = AutoField()
    name = models.Charfield()
    friend = models.ManyToMany("Girl")     # 会自动建立第三章表
    # f = models.ManyToMany("Girl", through="Friend", through_field = ["b", "g"])
    # 使用我们自己建立的第三章表来关联,指定表和字段,第三章表的b,g一定是分别做了外键的才可以
    
class Girl:
    id = AutoField()
    name = models.Charfield()
    
    # boy_set  反向找boy对象的属性
    
    
obj = models.objects.get(name="小明")
# 操作这个第三张表,进行查询操作
obj.friend.add(10)      # 对应的一个girl的id值
obj.friend.remove(10)   #  
obj.friend.set([10, 20])        # 设置
obj.friend.clear()
obj.friend.all()        # 获得所有关联的girl对象,是一个查询集。可以继续filter查询

foreigenKey实现多对多

使用第三张表,第三张表两个字段,分别于其他两个表做外键

class Boy:
    id = AutoField()
    name = models.Charfield()
    
class Girl:
    id = AutoField()
    name = models.Charfield()

class Friend:
    b = models.ForeigenKey("Boy")
    g = models.ForeigenKey("Boy")

如果需要查询时小明的所有女性朋友名字,可以有三种方式,

# 小明的所有女性朋友名字
# 第一种
boy = Boy.objects.filter(name="小明").first()   # 小明对象
for f in boy.Friend_set:    # Friend中和小明有关的所有Fiend对象
    g_name = f.g.name        # 每个f对象关联了一个girl对象获得
# for 循环中出现了连表,出现了N+1多次查表得问题

# 第二种: 使用join连表,从中间表起手,使用values连表或者select_related都可以
friends = Friend.objects.filter(b__name="小明").values("g__name")  # 使用了join连表,得到了
for friend in friends:
    g_name = friend.g_name

# 第三种:perfetch_related多次单表查询
friends = Friend.objects.filter(b__name="小明").perfetch_related("g__name")  # 使用了join连表,得到了
for friend in friends:
    g_name = friend.g_name

建立联合唯一索引

class friend:
    g = field()
    b = field()
    class Meta:
        unique_together=["g", "b"]

td

相关推荐