Django之ORM操作

1. ORM综述

ORM就是在python中利用Django中的封装好的功能,对数据库进行操作,但该操作只能对表,不能操作库,所以需要自己事先建立好库

2. 对整个项目配置文件的修改

2.1数据库配置

在settings中设置数据库名

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'orm', # 数据库名,需提前建好
'USER':'root',
'PASSWORD':'',
'HOST':'127.0.0.1',
'PORT':3306
}
}

2.2 在python中显示操作

在settings中配置logging信息

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}

2.3 导入pymysql模块

在项目同名的文件夹的_init_.py里写上

import pymysql
pymysql.install_as_MySQLdb()

2.4 数据库迁移

在terminal中执行

创建表python manage.py makemigrations

创建数据python manage.py migrate

2.5 注意

在‘INSTALLED_APPS’里写上app

2.6 继承表

from django.db import models
from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
tel = models.CharField(max_length=32)
nickname = models.CharField(max_length=32)

# settings

AUTH_USER_MODEL = 'blog.UserInfo'

3. 单表操作

3.1 创建一个表

在app名下的‘models.py’文件中创建

class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
state = models.BooleanField()
pub_date = models.DateField()
price = models.DecimalField(max_digits=8, decimal_places=2)
publish = models.CharField(max_length=32)

read_num models.InterField(default=0) # 后面添加字段,也是执行数据库迁移,但要加默认值

def __str__(self):
return self.title #模块内容的知识,打印模块的时候显示内容

# 批量数据操作
def index(request):
book_list=[]
for i in range(100):
book=Book(title='book_%s',price=i*2)
book_list.append(book)
Book.objects.bulk_create(book_list)

3.2 字段类型

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

字段

3.3 字段参数

null 用于表示某个字段可以为空。

unique 如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index 如果db_index=True 则代表着为此字段设置索引。

default 为该字段设置默认值。
  • DateField和DateTimeField
auto_now_add 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now 配置上auto_now=True,每次更新数据记录的时候会更新该字段。
  • ForeignKey
to 设置要关联的表

to_field 设置要关联的表的字段

related_name 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。

related_query_name 反向查询操作时,使用的连接前缀,用于替换表名。

on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。

db_constraint 是否在数据库中创建外键约束,默认为True。
  • OneToOneField
to 设置要关联的表。

to_field 设置要关联的字段。

on_delete 同ForeignKey字段。
  • ManyToManyField
to 设置要关联的表

related_name 同ForeignKey字段。

related_query_name 同ForeignKey字段。

symmetrical 仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。

through 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。

但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。

through_fields 设置关联的字段。

db_table 默认创建第三张表时,数据库中表的名称。

3.4 添加表记录

3.4.1 方式一

book_obj = Book(id=1, title='python', price=100, pub_date='2012-12-12', state=True, publish='luffy')
book_obj.save()

3.4.2 方式二,常用

book_obj=Book.objects.create(title='linux', price=120, pub_date='2013-12-12', state=True, publish='luffy')
print(book_obj.title) # 创建一个book对象,可以取出属性值

3.5 查询

<1> all(): # 查询所有结果,返回QuerySet,列表形式,相当于select * from 表

<2> filter(**kwargs): # 它包含了与所给筛选条件相匹配的对象,相当于where,返回QuerySet列表
book_obj=Book.objects.filter(title='python',多条件)

<3> get(**kwargs): # 同上,返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

<4> exclude(**kwargs): # 它包含了与所给筛选条件不匹配的对象

<5> order_by(*field): # 对查询结果排序
Book.objects.all().order_by('id'),-id是降序

<6> reverse(): # 对查询结果反向排序

<8> count(): # 返回数据库中匹配查询(QuerySet)的对象数量。int=Book.objects.all().count()

<9> first(): # 返回第一条记录,相当于QuerySet的第一个

<10> last(): # 返回最后一条记录

<11> exists(): # 如果QuerySet包含数据,就返回True,否则返回False

<12> values(*field): # 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列,model的实例化对象,而是一个可迭代的字典序列,相当于select value from 表
ret=Book.objects.all().values('price') 取出所有的价格
print(ret[0].get('price)) 取出第一个属性的值

<13> values_list(*field): # 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

<14> distinct(): # 从返回结果中剔除重复纪录
Book.objects.all().values('price').distinct()

3.6 模糊查询

Book.objects.filter(price__in=[100,200,300])
Book.objects.filter(price__gt=100) # 大于
Book.objects.filter(price__lt=100) # 小于
Book.objects.filter(price__range=[100,200])
Book.objects.filter(title__contains="python") # 包含
Book.objects.filter(title__icontains="python") # 不区分大小写
Book.objects.filter(title__startswith="py")
Book.objects.filter(pub_date__year=2012)

__exact 精确等于 like ‘aaa’
__iexact 精确等于 忽略大小写 ilike ‘aaa’
__contains 包含 like ‘%aaa%’
__icontains 包含 忽略大小写 ilike ‘%aaa%’,但是对于sqlite来说,contains的作用效果等同于icontains。
__gt 大于
__gte 大于等于
__lt 小于
__lte 小于等于
__in 存在于一个list范围内
__startswith 以…开头
__istartswith 以…开头 忽略大小写
__endswith 以…结尾
__iendswith 以…结尾,忽略大小写
__range 在…范围内
__year 日期字段的年份
__month 日期字段的月份
__day 日期字段的日
__isnull=True/False
__isnull=True 与 __exact=None的区别

3.7 删除

Book.objects.filter(pricr=200).delete()
# 过滤出所有的对象,删除,返回受影响的记录条数
Book.objects.filter(pricr=200).first().delete()
# 过滤出所有的对象,取第一个删除

3.8 修改

Book.objects.filter(title='go').update(title='php')
# 先过滤出需要修改的,再用update来修改字段,返回一个整型,表示收影响的记录条数

4.多表操作

4.1 多表之间的关系

A和B

  1. 一对多,A的一个字段能对应到B的多个字段,同时B的一个字段对应A的一个字段,在B里创建一个对应A的字段
  2. 多对多,A的一个字段能对应到B的多个字段,同时B的一个字段对应A的多个字段,创建第三张表来关联A和B
  3. 一对一,可在任意表内创建关联,但要加unique

4.2 创建关联

  1. 一对多,在多的表里创建

sql语句在book表里面创建一个关联的字段,然后创建关联author_id int,foreign key (author_id) refrences author(id)

在Django里面author = models.ForeignKey(to='Author', to_field='id', on_delete=models.CASCADE),会自动创建author_id字段

注意:表跟字段都要加引号,表加引号会去全局找,这样就没有先后关系了;要加on_delete

  1. 一对一

authordetail=models.OneToOneField(to='Authordetail',to_field='nid')

在author表里建,也是自动生成_id 字段,然后关联,要加on_delete

  1. 多对多

sql语句,先创建一张book_author的表,在表里id int primary key auto_increment,book_id int,author_id int,foreign key(book_id) refrences book(id),foreign key (author_id) refrences author(id)

Django语句,在book表里authors=models.ManyToManyField(to='Author')

注意事项

  1. 表的名字是app名加类名的小写
  2. id如果不写,会自动添加
  3. 外键字段,会自动添加_id
  4. settings里要加上app名
  5. INSTALL_APPS中要设置
  6. 外键字段可以设置null=True,允许这个字段为空
  7. 表跟字段加引号,会去全局找

4.3 一对多表操作

4.3.1 添加

一种是用具体的_id来给具体的值,一种是用author对象来给一个对象

  • 方式一:直接赋具体的值,在create 的时候赋值,book 表里面的author_id=1,具体的一个值,比如 1

  • 方式二:有时候不是拿到具体的对象的时候,用

author_obj=Author.objects.filter(id=1).first()

也是create的时候服饰author=author_obj

两种方法结果是一样的,都可以用author字段来取出对象的字段

4.3.2 查询

先过滤出具体的book对象,再取出对象的publish对象,再取出publish对象的属性

book_obj=Book.objects.filter(id='1').first().publish.email

4.4 多对多表操作

4.4.1 添加关系

在book表创建authros=models.ManyToManyField(to=‘Author’)

添加关系

book_obj=Book.objects.create(……) #创建boo实例
alex=Author.objects.get(name='alex') # 取到alex实例
egon=Author.objects.get(name='egon') # 取到egon实例
book_obj.authors.add(alex,egon) # book实例绑定alex跟egon实例
book_obj.authors.add(1,2) # 直接用id去绑定
book_obj.authors.add(*[1,2])
book_obj.authors.set(1,2) # 等于先clear再ad

4.4.2 删除关系

book_obj=Book.objects.filter(id=1).first()
book_obj.authors.remove(1) # 删除一个
book.author.clear() # 清空关系
book.author.all() # 取出这本书关联的所有作者对象

5. 跨表查询

5.1 基于对象的跨表查询

5.1.1 一对多

关联字段在A表中

  • 正向查询:A查B,按字段查
  • 反向查询:B查A,按关联的表名_set.all(),得到有结果的QuerySet列表

正向查询

# Book(关联字段publish) ---------> Publish
book_obj=Book.objects.filter(title=‘’).first()
print(book_obj.publish.name)

反向查询

# Book(关联字段publish) <--------- Publish
publish_obj=Publish.objects.filter(name='……').first()
publish_obj.book_set.all() # 到有结果的QuerySet列表

5.1.2 多对多

关联字段在A表中

  • 正向查询:A查B,按字段查
  • 反向查询:B查A,按关联的表名_set.all(),得到有结果的QuerySet列表

正向查询

# Book(关联字段author) ---------> Author
book_obj=Book.objects.filter(title='……').first()
author_list=book_obj.author.all()
for i in author:
print(i.name)

反向查询

# Book(关联字段author) <--------- Author
author_obj=Author.objects.filter(name='……').first()
book_list=author_obj.book_set.all()
for i in book_list:
print(i.title)

5.2 基于双下划线的跨表查询

5.2.1 一对多

正向查询按字段

反向查询按表名小写,来join表

两种查询方法

# 查询xx这本书的出版社的名字
# 正向查询
Book.objects.filter(title='……').values('publish__name')
# 反向查询
Publish.objects.filter(book__title='……').values('name')

5.2.2 多对多

# 查询xx这本书的作者的名字
# 正向查询
Book.objects.filter(title='……').values('author__name')
# 反向查询
Author.objects.filter(book__title='……').values('name')

5.2.3 一对一

# 查找alex的手机号
Author.objects.filter(name='alex').values('authordetail__phone')
AuthorDetail.objects.filter(author__name='alex').values('phone')

5.3 连续跨表查询

手机号以110开头的作者出版过的所有书籍名称以及书籍的出版社名称

Book.objects.filter(author__authordetail__phone__startswith='110').values('title','publish__name')
Author.objects.filter(authordetail__phone__startswish='110').values('book__title','book__publish__name')

5.4 聚合和分组查询

5.4.1 聚合查询,aggregate

from django.db.models import Avg,Max,Min,Count
ret = Book.objects.all().aggregate(Avg('price'))
print(ret) # {'price__avg':150.0}
Book.objects.all().aggregate(avg_price=Avg('price')) # 自己取名字

5.4.2 分组查询,annotate

  • 单表

单表模型.objects.values(‘group by 字段’).annotate(聚合函数(字段))

# 查询每一个部门名称以及对应的员工数
select count(id) from emp group by dep # sql

# 查询每一个部门的名称以及员工的平均薪水
select dep,Avg(salary) from emp group by dep # sql
Emp.objects.values('dep').annotate(avg_salary=Avg('salary'))
# 返回QuerySet列表
# [{'avg_salary':500,'dep':'开发'},{}]

# 查询每个省份的名称以及员工数
Emp.objects.values('province').annotate(num=Count('id'))
  • 多表
# 查询每一个出版社出版的书籍数
Book.objects.values('publish_id').annotate(Count('id'))

# 查询每个出版社的名称以及出版的书籍数
select publish.name,Count('title') from Book inner join Publish on book.publish_id=publish.id group by publish.id

Publish.objects.values('name').annotate(Count('book__title'))
# 两种方法,建议下面一种
Publish.objects.values('id').annotate(c=Count('book__title')).values('name','c')

# 查询每一个作者的名字以及出版过的书籍的最高价格,pk表示主键
Author.objects.values('pk').annotate(p=Max(book__price)).values('name','p')

# 查询每一个书籍的名称以及对应的作者个数
Book.objects.values('pk').annotate(n=Count(author__name)).values('title','n')

# 每一本以py开头的书籍的作者个数
Book.objects.filter(title__startswith='py').values('pk').annotate(Count(c='author__name')).values('title','c')

# 统计不止一个作者的书籍
Book.objects.values('pk').annotate(c=Count('author__name')).filter(c__gt=1).values('title','c')

总结:

每一个后面的表的模型.objects.values('pk').annotate(聚合函数(关联表__统计字段))相当于.objects.all().annotate()相当于.objects.annotate()

5.5 F和Q查询

5.5.1 F查询

from django.db.models import F
# 评论数大于阅读数的书
Book.objects.filter(comment_num__gt=F('read_num'))

# 所有的书价格加10
Book.objects.all().update(price=F('price')+10)

5.5.2 Q查询

from django.db.models import Q
Book.objects.filter(title='……',price=100) # 逗号表示且
Book.objects.filter(Q(title='……')&Q(price=100)) # 且
Book.objects.filter(Q(title='……')|Q(price=100)) # 或
Book.objects.filter(~Q(title='……')|Q(price=100)) #非
Book.objects.filter(~Q(title='……')|Q(price=100),再加条件Q要在前面)
posted @ 2018-12-20 08:50  球球-Ball  阅读(175)  评论(0编辑  收藏  举报