Django ORM(Object-Relational Mapping) 把对象自动映射到数据库中,是业务逻辑和数据库层的桥梁
使用 ORM 可以提升开发效率,但是会一定程度上影响程序的性能。
http请求都封装成事务
DATABASES[“default”][“ATOMIC_REQUESTS”] = True
模型类常用字段
from django.db import models
class NormalField(modes.Model):
# 自增字段
auto = models.AutoField()
big_auto = models.BigAutoField() # 64位整数 保证从数字1到9223372036854775807
# 二进制数据
binary = models.BinaryField()
# 布尔型
boolean = models.BooleanField()
null_boolean = models.NullBooleanField() # 允许为空的布尔型
# 整型
positive_small_integer = models.PositiveSmallIntegerField() # 正整数 5个字节
small_integer = models.SmallIntegerField() # 整数 6个字节
positive_integer = models.PositiveIntegerField() # 10个字节
integer = models.IntegerField() # 11个字节
bit_integer = models.BigIntegerField() # 20个字节
# 字符串类型
char = models.CharField() # varchar
text = models.TextField() # lonetext 不需要指定长度
# 时间日期类型
date = models.DateField()
date_time = models.DateTimeField()
duration = models.DurationField() # int, 通过 timedelta 实现 obj = timedelta(days=20, hours=10)
# 浮点型
float = models.FloatField()
decimal = models.DecimalField(..., max_digits=5, decimal_places=2) # 需要指明整数位和小数位
# 其他字段
email = models.EmailField()
image = models.ImageField()
file = models.FileField()
file_path = models.FilePathField()
url = models.URLField()
uuid = models.UUIDField()
generic_IP_address = models.GenericIPAddressField()
class A(models.Model):
one_to_one = models.OneToOneField(NormalField)
class B(models.Model):
foreign = models.ForeiognKey(A)
class C(model.Model):
many_to_many = models.ManyToManyField(B)
在数据库中对应的表名与字段名
表名:
- app 的名为 course 模型名为 Teacher,表名为:course_teacher
字段名:
- 如果字段名为 sex 数据库中也会为 sex,可以通过属性 (db_colum=’gender’) 更改在数据库中的字段名
注意:由于 Django 的查询语法,字段名除了不能是关键字外,也不能包含连续多个下划线
自定义表名与字段名
Django 用来做数据库管理系统是极其方便的,如果用来管理已存在的数据库一般要自定义表名与字段名
class Article(models.Model):
"""文章模型"""
# 通过db_column自定义数据表中字段名
title = models.CharField('标题', max_length=200, db_column='article_title')
slug = models.SlugField('slug', max_length=60, blank=True)
def __str__(self):
return self.title
class Meta:
db_table = 'article' # 通过db_table自定义数据表名
字段参数
通用参数
primary_key=True/False
- 设置字段是否为主键,每个表只能有一个主键
verbose_name=’xx’
- 字段的别名或备注
unique=True
- 该字段在表中值唯一
null=True,blank=True
- null 是数据库层是否为空,而blank=True表示在表单提交时是否可以为空
db_index=True
- 是否为该字段建立索引,每次插入数据都会更新索引,查看慢查询日志考量后添加索引
help_text=’xx’
- 帮助说明
editable=False
auto_now=True
- 更新当前记录的时间
auto_now_add=True
decimal_places=2
- 小数位2位
关系型字段参数
related_name=’books’
- 用于外键关联中的反向查询
on_delete=models.XXX
当一个外键被删除是,Django 会模仿 on_delete 参数定义的 SQL 约束执行相应操作
- CASCADE:模拟 SQL 语言中的 ON DELETE CASCADE 约束,将定义有外键的模型对象同时删除
- PROTECT: 阻止上面的删除操作,并报 ProtectedError 异常
- SET_NULL: 将外键字段设置为 null,只有当前字段设置了 null=True 时,方可使用该值
- SET_DEFAULT: 将外键字段设置为默认值,只有当字段设置了 default 参数时,方可使用
- DO_NOTHING: 什么都不做
- SET(): 设置为一个传递给 SET() 的值或者一个回调函数的返回值
自关联
class AddressInfo(models.Model):
"""省市县地址信息"""
address = models.CharField(max_length=200, null=True, blank=True, verbose_name="地址")
pid = models.ForeignKey('self', null=True, blank=True, verbose_name="上级地址")
# 或者 pid = models.ForeignKey('AddressInfo', null=True, blank=True, verbose_name="上级地址")
def __str__(self): # python2 __unicode__(self) 返回一个可读的字符串
return self.address
元数据
class SomeModel(models.Model):
title = models.CharField(max_length=100)
note = models.CharField(max_length=100)
sort = models.PositiveIntegerField(default=0)
order_date = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'some_model' # 自定义表名
ordering = 'sort', # 设置排序
verbose_name = '某个模型' # 设置直观可读名称
verbose_name_plural = verbose_name # 设置可读名称的复数形式
abstract = True # 不生成数据表,只供其他子类集成
permissions = (('定义的权限', '权限说明'), ('xxx', 'xx')) # 给表设置额外的权限,二元tuple
managed = False # 由于Django会自动根据模型类生成映射的数据库表,如果你不希望Django这么做,可以把managed的值设置为False
unique_together = ('title', 'note') # 对应数据表中的联合唯一约束,可以是一元元或多元元组
app_label = 'xxx' # 如果没有在 Django settings 的 INSTALLED_PPS 添加app,就要设置该类属于哪个应用
db_tablespace = 'xxx' # 定义数据表空间的名字
get_latest_by = "order_date" # 可以在模型中使用Manager的 latest()和 earliest()方法找到对应数据
Manager
通过其向Django模型提供数据库查询操作的接口
返回 QuerySet 的 API
新的 QuerySet
all() : 查询所有
fileter(): 返回符合规则的
order_by(): 根据字段排序
exclude(): 排除符合规则的
reverse(): 相反排序,要在源数据中设置 order
distinct(): 去重
extra():
- 取别名:
- Student.object.all().extra(select={“name”: “nickname”})
- key 是新字段名,value是原字段名
defer():
- 结果模型中排除某些字段
only():
- 限制模型返回的字段
- Student.object.all().only(“nickname”, “age”)
字典或元组形式的 QuerySet
values():
- 返回字典形式的 QuerySet
values_list():
- 返回元组形式的 QuerySet,只有value,如果是单个字段,可以设置参数 flat=True 返回数组形式的querySet
一对一,多对多查询优化
select_related()
- 一对一查询优化
查询子表时一次性吧附表也查询出来,否则遍历取附表值时每次都要执行一遍对附表的查询
course = Course.objects.all().select_related('teacher')
for c in courses:
print(f"{c.teacher.nickname}")
prefetch_related()
多对多查询优化
- 如果不优化同样在遍历时都要执行关联查询
students = Student.object.filter(age__lt=20).prefetch_related('course')
for item in students:
print(item.course.all())
聚合
annotate()
- 对分组后的结果进行统计,一般配合 values 使用 ```python from django.db.models import Sum, Count, Avg, Max, Min
每个老师有多个课程,返回老师和对应老师的 volume 和
print(Course.objects.values(‘teacher’).annotate(vol=Sum(‘volume’)))
<a name="gx7Fm"></a>
### 不返回 QuerySet 的 API
get(): 返回单条数据,没有或者有多条会报错<br />get_or_created(): 与 get() 不同的是如果没有匹配数据就创建
create(): 根据传入的键值对参数创建数据<br />bulk_create(): 根据传入的 对象列表,批量创建数据<br />update_or_create(): 如果没有匹配的值,就创建,default参数里是默认值, 如果有就更新 default 里面的字段
- CustomUser.objects.update_or_create(nickname='小张', defaults={'age': 20, gender=1})
update(): 更新前面所有对象里的值<br />delete(): 删除前面对象
first(): 首条记录<br />last(): 末尾记录<br />latest(): 在 model 设置 get_latest_by 的前提下,输出最近时间数据<br />earliest(): 输出 最早的 数据<br />in_bulk(): 根据列表参数里的值,批量返回对象
- Course.objects.in_bulk(['课程名1', '课程名2'])
exists(): 结果是否存在 <br />count(): 结果数量
aggregate(): 对前面的数据进行统计
```python
from django.db.models import Max, Min, Avg, Sum
Course.objects.aggregate(Max('price'), Min('price'), average=Avg('price'), Sum('price'))
# 结果
{'price__max': 300, 'price__min':210, 'average': 22.22, 'price__sum': 42888}
F 对象与 Q 对象
F对象 使原本 先 select 再 处理 数据的方式只需一条查询即可完成
from django.db.models import F
Course.objects.update(price=F('price') - 11)
Course.objects.filter(volume__lte=F('price') * 10)
Q 对象 可以结合 与(&), 或(|), 非(~) 完成复杂查询
from django.db.models import Q
Course.objects.filter(~Q(title__icontains='java') | Q(price__gat=10))
获取随机 N 条数据
使用mysql数据库,数据量在百万级以下时
Record.objects.order_by('?')[:2]
来获取随机记录序列,性能不会比
sample = random.sample(xrange(Record.objects.count()),n)
result = [Record.objects.all()[i]) for i in sample]
差。