首页 > 数据库 >05 ORM数据库操作

05 ORM数据库操作

时间:2023-02-13 07:00:08浏览次数:41  
标签:__ objects 05 models 数据库 ORM filter Book print

ORM数据库操作

django使用mysql

django默认使用的是自带的sqlite数据库,如果想使用其他数据库,需要在settings配置文件中配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_study',
        'HOST':'127.0.0.1',
        'PORT':3306,
        'USER':'root',
        'PASSWORD':'123',
        'CHARSET':'utf8'
    }
}

# django1.11需要在项目名下的init文件或者应用名下的init文件中告诉django,不要使用默认的mysqldb连接mysql,而使用pymysql
import pymysql
pymysql.install_as_MySQLdb()

ORM对象关系映射

django orm不会创建库,需要手动创建库

表 ----------> 类
一条条记录 ----------> 对象
字段对应的值 ----------> 对象的属性

数据库迁移命令

python3 manage.py makemigrations # 仅仅是在小本本上(migrations文件夹)记录数据库的修改,并不会直接操作数据
python3 manage.py migrate # 将数据库修改记录真正同步到数据库
注意:只要动了models中跟数据库相关的代码,就必须重新执行上面的两条命令

pycharm可简写:Tools -> Run manage.py Task

ORM常用字段及参数

一些说明:

  • 表名称是自动生成的,如果要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
  • Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。

常用字段

AutoField

int自增列,主键字段,必须填入参数 primary_key=True。如果没有主键字段,则自动创建,列名为id。

IntegerField

一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存)

CharField

字符类型,必须提供max_length参数, max_length表示字符长度。Django中的CharField对应MySQL中的varchar类型,没有设置char类型对应字段,但Django允许自定义新字段

自定义mysql对应的char类型及使用:

class MyChar(models.Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        super().__init__(max_length=max_length,*args,**kwargs)
    def db_type(self, connection):
        return 'char(%s)'%self.max_length
    
class Classes(models.Model):
	desc = MyChar(max_length=64,null=True)

DateField

日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。

auto_now_add:配置auto_now_add=True,创建数据时会把当前时间添加到数据库,后续操作不会影响该字段

auto_now:配置auto_now=True,每次更新数据时会更新该字段。

DateTimeField

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

auto_now_add、auto_now 和DateFiled一样

choices字段

class User(models.Model):
    gender_choices = (
        (1,'男'),(2,'女'),(3,'保密')
    )
    gender = models.IntegerField(choices=gender_choices)  # 存gender_choice罗列的数字与中文对应关系

user_obj.gender # 拿到的是数字
user_obj.get_gender_display() # 拿到的是数字对应的注释,get_choices字段名_diaplay()

当gender存的是gender_choices中罗列的数字,那么就会有对应关系;存的不是gender_choices中罗列的数字那么也不会报错正常存储 ,只不过没有了对应关系

字段合集

字段合集

AutoField(Field)
- int自增列,必须填入参数 primary_key=True

BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列

SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767

IntegerField(Field) int()
- 整数列 -2147483648 ~ 2147483647

PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647

BigIntegerField(IntegerField):
- 长整型 -9223372036854775808 ~ 9223372036854775807

BooleanField(Field)
- 布尔值类型

NullBooleanField(Field):
- 可以为空的布尔值

CharField(Field) varchar()
- 字符类型,必须提供max_length参数,max_length表示字符长度

TextField(Field)
- 文本类型

EmailField(CharField) varchar(254):
- 字符串类型,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) datetime
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

DateField(DateTimeCheckMixin, Field) date
- 日期格式 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)
​ - 二进制类型

ORM字段与MySQL字段对应关系

对应关系:
'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)',

ORM字段与MySQL字段对应关系

字段参数

null

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

unique

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

db_index

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

default

为该字段设置默认值。

# 当表已经创建后续想添加字段,可以有两种方式
# 1.给新增的字段设置默认值
addr = models.CharField(max_length=32,default='China')
# 2.给新增的字段设置成可以为空
age = models.IntegerField(null=True)

关系字段

ForeignKey

user = models.ForeignKey(to="User",to_field="id",on_delete=models.CASCADE) # 创建出来的字段名为user_id

一对多,ForeignKey设置在多的一方,可以和其他表做关联关系也可以和自身做关联关系。

字段参数

to:设置要关联的表

to_field:设置要关联的表的字段,默认是跟指定表的主键字段做一对多外键关联

on_delete:当删除被关联表中的数据时,当前表与其关联的行的行为。值为models.CASCADE:删除被关联数据,当前表与其关联数据也删除,必配

1、models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除

2、models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空

3、models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除

4、models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值

5、models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数

6、models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似

OneToOneField

author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE) # 创建出来的字段名为author_detail_id

一对一,OneToOneField设置在查询频率较高的一方

字段参数

to:设置要关联的表

to_field:设置要关联的表的字段

on_delete:当删除被关联表中的数据时,当前表与其关联的行的行为。值为models.CASCADE:删除被关联数据,当前表与其关联数据也删除,必配

ManyToManyField

# 多对多
class Book(models.Model):
    authors = models.ManyToManyField(to='Author')  # 不会产生实际字段,而是自动创建第三张表

字段参数

to:设置要关联的表

to_field:设置要关联的表的字段

on_delete:当删除被关联表中的数据时,当前表与其关联的行的行为。值为models.CASCADE:删除被关联数据,当前表与其关联数据也删除,必配

多对多三种创建方式

1.全自动(推荐使用*)

优势: 不需要手动创建第三张表
不足: 由于第三张表不是手动创建的,也就意味着第三张表字段是固定的无法做扩展

class Book(models.Model):
   title = models.CharField(max_length=32)
   price = models.DecimalField(max_digits=8,decimal_places=2)
   authors = models.ManyToManyField(to='Author')

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

2.纯手动(了解即可)

自己创建第三张表
优势: 第三张表可以任意扩展字段
不足: orm查询不方便(正向查询无法按外键字段)

class Book(models.Model):
   title = models.CharField(max_length=32)
   price = models.DecimalField(max_digits=8,decimal_places=2)

class Author(models.Model):
   name = models.CharField(max_length=32)
   
class Book2Author(models.Model):
   book = models.ForeignKey(to='Book')
   author = models.ForeignKey(to='Author')
   create_time = models.DateField(auto_now_add=True)

3.半自动(推荐使用***)

优势: 结合了全自动和纯手动的两个优点

class Book(models.Model):
	title = models.CharField(max_length=32)
	price = models.DecimalField(max_digits=8,decimal_places=2)
	authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
	# through: 告诉django orm Book表和Author表的多对多关系是通过Book2Author表来记录的
	# through_fields: 告诉django orm Book表和Author表的多对多关系是通过Book2Author表book字段和author字段来记录的
	# 多对多字段的增删改add、set、remove、clear照样支持
    
class Author(models.Model):
	name = models.CharField(max_length=32)
	# books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=('author', 'book'))

class Book2Author(models.Model):
	book = models.ForeignKey(to='Book')
	author = models.ForeignKey(to='Author')
	create_time = models.DateField(auto_now_add=True)

ORM表操作

Python脚本中调用Django环境

在进行一般操作前先配置一下参数,则可以直接在Django中运行测试脚本

单表操作

基本单表查询

<1> all(): 查询所有结果

models.Book.object.all() # 返回对象集合(即QuerySet对象)

<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象,filter里面的参数相当于where或者having里面的筛选条件

models.Book.objects.filter(pk=2) # pk会自动查找到当前数据的主键字段,返回对象集合;条件可以是:参数、字典、Q

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

models.Book.objects.get(pk=1) # 返回对象

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

models.Book.objects.exclude(pk=1) # 只要pk不是1的对象全部查询出来,返回对象集合;条件可以是:参数、字典、Q

<5> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列,value里面的参数对应的是sql语句中的select要查找显示的字段

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

<7> order_by(*field): 对查询结果排序

models.Book.objects.order_by('price') # 默认是升序,返回对象集合
models.Book.objects.order_by('-price') # 加负号就是降序

<8> reverse(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。

models.Book.objects.order_by('price').reverse() # 返回对象集合

<9> distinct(): 从返回结果中剔除重复纪录(注意只有在PostgreSQL中支持按字段去重。)

models.Book.objects.filter(title='三国演义').distinct() # 返回对象集合

models.Book.objects.values('title','price','create_time').distinct()

<10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。

models.Book.objects.count()

models.Book.objects.all().count()

<11> first(): 返回第一条记录

models.Book.objects.filter(pk=1).first() # 返回对象

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

models.Book.objects.all().last() # 返回对象

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

models.Book.objects.filter(pk=1000).exists()

<14> create(): 加表数据

models.Book.objects.create(title='三国',price=19.99,create_time='2019-11-11') # 返回对象

print(book_obj.title)

加表数据还可以直接实例化模块类:

from app01 import models
book_obj = models.Book(title='金梅',price=96.66,create_time='2019-11-11')
book_boj.save()

<15> update(): 改表数据

models.Book.objects.filter(pk=1).update(title='三国演义') # filter.update()是批量操作,即若filter结果列表有多个那么会全部修改

改表数据还可以直接修改模块类实例属性:

from app01 import models
book_obj = models.Book.objects.get(pk=1)
book_obj.price = 666.66
book_obj.save()

update()与save()的区别:

两者都是对数据的修改进行保存,但save()是将所有字段全部重新写一遍,而update()则是针对修改的字段进行更新,效率高耗时少,所以对数据的修改推荐用update()

<16> delete() 删表数据

models.Book.objects.filter(pk=2).delete()

删表数据还可以直接山粗模块类实例:

from app01 import models
book_obj = models.Book.objects.get(pk=1)
book_obj.delete

双下划线单表查询

# 查询价格大于200的书籍
print(models.Book.objects.filter(price__gt=200))
# 查询价格小于200的书籍
print(models.Book.objects.filter(price__lt=200))
# 查询价格大于等于200.22的书籍
print(models.Book.objects.filter(price__gte=200.22))
# 查询价格小于等于200.22的书籍
print(models.Book.objects.filter(price__lte=200.22))
# 查询价格大于100且小于300的书籍
print(models.Book.objects.filter(price__gt=100,price__lt=300))

# 查询价格是200或300或666.66的书籍
print(models.Book.objects.filter(price__in=[200,300,666.66]))
# 查询价格不是200、300、666.66的书籍
print(models.Book.objects.exclude(price__in=[200,300,666.66]))

# 查询价格在200到800之间的书籍
print(models.Book.objects.filter(price__range=(200,800)))  # 两边都包含

# 查询名字中包含p的书籍
print(models.Book.objects.filter(title__contains='p'))  # 只能拿小写p
print(models.Book.objects.filter(title__icontains='p'))  # 忽略大小写

# 查询名字以‘p’开头的书籍
print(models.Book.objects.filter(title__startswith='p'))
print(models.Book.objects.filter(title__istartswith='p'))  # 忽略大小写
# 查询名字以‘p’结尾的书籍
print(models.Book.objects.filter(title__endswith='p'))
print(models.Book.objects.filter(title__iendswith='p'))  # 忽略大小写

# 查询出版日期是2017年的书籍,__date、__year、__month、__day、__week_day,需要注意的是在表示一年时间时通常用52周来表示
print(models.Book.objects.filter(create_time__year='2017'))
# 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)

多表操作

一对多字段的增删改查

# 增
# 方式一:传publish_id
models.Book.objects.create(title='三国演义',price=189.99,publish_id=1)
# 方式二:传publish对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='红楼梦',price=999.99,publish=publish_obj)

# 改
# 方式一:传publish_id
models.Book.objects.filter(pk=1).update(publish_id=3)
# 方式二:传publish对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)

# 删
models.Publish.objects.filter(pk=2).delete()

# 注意:对于ForeignKey对象,ManyToMany的clear()和remove()方法仅在null=True时存在

多对多字段的增删改查

# 增
# add()既可以传数字也可以传对象,并且支持传多个,用逗号隔开即可
# 方式一:传authors_id
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add(1)  # 对象.多对多虚拟字段,会直接跨到多对多的第三张表
book_obj.authors.add(2,3)
# 方式二:传authors对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj1,author_obj2)
# add()之外的另一个方法
models.Author.objects.first().book_set.create(title="偷塔秘籍")
"""
上面这一句干了哪些事儿:
	1.由作者表中的一个对象跨到书籍表
	2.新增名为偷塔秘籍的书籍并保存
	3.到作者与书籍的第三张表中新增两者之间的多对多关系并保存
"""

# 改
# set()需要传一个可迭代对象,可以是多个数字组合,也可以是多个对象组合
# 方式一:传authors_id
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.set([2,])
book_obj.authors.set([2,3])
# 方式二:传authors对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.set([author_obj,])
book_obj.authors.set([author_obj1, author_obj2])

# 删
# remove()既可以传数字也可以传对象,并且支持传对个,逗号隔开即可
# 方式一:传authors_id
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.remove(3)
book_obj.authors.remove(1,2)
# 方式二:传authors对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.remove(author_obj)
book_obj.authors.remove(author_obj1,author_obj2)
# clear()清空当前书籍与作者的所有关系
book_obj.authors.clear()  

跨表查询

"""
正向与反向的概念:
    一对一:
        正向:author---关联字段在author表里--->authordetail	按字段
        反向:authordetail---关联字段在author表里--->author	按表名
    一对多:
        正向:book---关联字段在book表里--->publish	按字段
        反向:publish---关联字段在book表里--->book	按表名_set.all(),因为一个出版社对应多个图书
    多对多:
        正向:book---关联字段在book表里--->author		按字段
        反向:author---关联字段在book表里--->book		按表名_set.all(),因为一个作者对应多个图书

正向查询按外键字段,反向查询按表名小写
"""

# 基于对象的跨表查询(子查询:将一个语句的查询结果作为另一个语句的条件)
"""
当正向查询结果是多个时需要加.all,否则直接按字段即可
当反向查询结果是多个时需要加_set,否则直接按表名即可
"""

# 正向
# 查询作者jason的家庭住址(一对一)
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail.addr)
# 查询书籍id是1的出版社名称(一对多)
book_obj = models.Book.objects.filter(pk=1).first()
print(book_obj.publish.name)
# 查询书籍id是2的作者姓名(多对多)
book_obj = models.Book.objects.filter(pk=2).first()
res = book_obj.authors.all()
for r in res:  # res.values_list("title")
    print(r.name)
    
# 反向
# 查询电话号码为130的作者姓名(一对一)
author_detail_obj = models.AuthorDetail.objects.filter(phone=130).first()
print(author_detail_obj.author.name)
# 查询东方出版社出版的书籍(一对多)
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set.all())
# 查询作者jason的所有书籍(多对多)
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set.all())

# 查询书籍id为1的作者的电话号码
book_obj = models.Book.objects.filter(pk=1).first()
author_list = book_obj.authors.all()
for author_obj in author_list:
    print(author_obj.author_detail.phone)

    
    
# 基于双下划线的跨表查询(连表操作),left join、inner join、right join、union

# 查询作者jason的年龄和手机号
print(models.Author.objects.filter(name='jason').values('age','author_detail__phone'))  # 正向
print(models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age'))  # 反向
# 查询手机号为130的作者年龄
print(models.Author.objects.filter(author_detail__phone=130).values('age'))  # 正向
print(models.AuthorDetail.objects.filter(phone=130).values('author__age'))  # 反向


# 查询书籍id为1的作者的电话号码
# models.Book.objects.filter(pk=1).values('外键字段1__外键字段2__外键字段3__普通字段'),只要表里有外键字段就可以无限制跨多张表
print(models.Book.objects.filter(pk=1).values('authors__author_detail__phone'))

# 查询北方出版社出版的价格大于19的书
print(models.Book.objects.filter(price__gt=19,publish__name='北方出版社').values('title','publish__name'))

聚合查询

# 聚合查询(aggregate) 利用聚合函数
from django.db.models import Max,Min,Count,Avg,Sum
print(models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Avg('price'),Sum('price')))  # {'price__avg': 13.233333}
print(models.Book.objects.aggregate(average_price=Avg('price')))  # {'average_price': 13.233333}

分组查询

# 分组查询(annotate)
# annotate分组依据他前面的字段,如果前面没有字段则默认按照ID分组
# annotate本身表示group by的作用,前面找寻分组依据,内部放置显示可能用到的聚合运算式,后面跟filter来增加限制条件,最后的value来表示分组后想要查找的字段值

# 统计每本书的作者个数
from django.db.models import Max, Min, Count, Avg, Sum
print(models.Book.objects.annotate(author_num = Count('authors')).values('title','author_num'))

# 统计每个出版社卖的最便宜的书的价格
# 方法一:
print(models.Publish.objects.annotate(min_price=Min("book__price").values('name','min_price'))
# 方法二:
print(models.Book.objects.values("publish__name").annotate(min_price=Min("price")))
      
# 统计不止一个作者的书
print(models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1))  # 只要是queryset对象就可以无限制调用queryset对象的方法,最常用的就是对一个已经filter过滤完的数据再进行更细化的筛选
      
# 根据一本图书作者数量的多少对查询集QuerySet进行排序
print(models.Book.objects.annotate(author_num=Count("author")).order_by("author_num"))
      
# 单表分组
print(models.Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg"))  # 原生SQL: select dept,AVG(salary) from employee group by dept;
      
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

F查询与Q查询

# F()的实例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值
"""
F可取到表中某个字段对应的值来当作筛选条件(filter),而不是自定义常量的条件,实现了动态比较的效果

Django支持F()对象之间以及F()对象和常数之间的加减乘除和取模的操作,基于此可以对表中的数值类型进行数学运算

修改char字段,需要对字符串进行拼接Concat操作,加上拼接值Value
Concat表示进行字符串的拼接操作,参数位置决定了是在头部拼接还是尾部拼接,Value是要新增的拼接值
"""
      
# 查询库存数大于卖出数的书籍
from django.db.models import F
print(models.Book.objects.filter(kucun__gt=F('maichu')))
# 将书籍库存数全部增加1000
models.Book.objects.update(kucun=F('kucun')+1000)
# 把所有书名后面加上'新款'
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('新款')))



# Q查询

from django.db.models import Q
# 查询书籍名称是三国演义或者价格是444.44
print(models.Book.objects.filter(title='三国演义',price=444.44))  # filter只支持and关系
print(models.Book.objects.filter(Q(title='三国演义'),Q(price=444)))  # 用逗号还是and关系
print(models.Book.objects.filter(Q(title='三国演义')&Q(price=444)))  # 对条件包裹一层Q时filter即可支持交叉并的比较符
print(models.Book.objects.filter(~Q(title='三国演义')|Q(price=444)))  # 名称不是三国演义或者价格是444.44,~表示取反
# Q高级用法
q = Q()
q.connector = 'or'  # 修改查询条件的关系,默认是and
q.children.append(('title__contains','三国演义'))  # 往列表中添加筛选条件
q.children.append(('price__gt',444))  # 往列表中添加筛选条件
print(models.Book.objects.filter(q))  # filter支持直接传q对象,但是默认还是and关系

事务

from django.db import transaction
try:
    with transaction.atomic():
        """
        数据库操作
        在该代码块中书写的操作同属于一个事务
        """
except BaseException as e:
    print(e)
print('出了代码块事务就结束')

Django终端打印SQL语句

在django项目的配置文件settings.py中,加上以下代码即可实现所有的orm在查询时自动打印对应的sql语句

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

补充:

还可以通过.query查看查询语句,只有querySet对象才可以直接.query查看内部对应的sql语句(querySet对象可以把它当做列表,可以通过索引取,只支持正数索引不支持负数,还可以切片,切出来的结果还是querySet对象,但不推荐),具体操作如下:

Django ORM执行原生SQL

比如:博客是按照年月来分档,而DateField时间格式是年月日,也就是说需要对从数据库拿到数据再进行一次处理拿到想要的时间格式,Django是没有提供方法的,需要自己去写处理语句

ORM 执行原生SQL的方法:

# 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 
        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
    """
	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'])



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

ORM查询优化

only与defer

# orm是惰性查询: 只会在需要数据时才会走数据库,单单只写orm语句不会走数据库,减轻数据库压力
# 这两种方式都不能通过 .字段 的方式获取值
res = models.Book.objects.all()
print(res)  # <QuerySet [{'title':'三国'},...]> 因为models类里定义了__str__()
res = models.Book.objects.values('title')
print(res)  # <QuerySet [{'title':'三国'},...]>

# only返回的是对象,即可以通过 对象.字段 操作获取字段值
res = models.Book.objects.only('title')
print(res)  # <QuerySet [<Book: 三国>,...]> 返回的是对象
for r in res:
    print(r.title)  # 是only指定的字段,只走一次数据库查询
    print(r.price)  # 不是only指定的字段,则会频繁走数据库查询

# defer返回的是也是对象,即可以通过 对象.字段 操作获取字段值,但是否走数据库查询跟only是相反的
res = models.Book.objects.defer('title')  
print(res)  # <QuerySet [<Book: 三国>,...]> 返回的是对象
for r in res1:  
    print(r.title)  # 是defer指定的字段,则会频繁走数据库查询
    print(r.price)  # 不是defer指定的字段,只走一次数据库查询
"""
select_related: 会将括号内外键字段所关联的表(可以一次性拿多张表)跟当前表做连表操作然后将数据封装到对象中,只走一次数据库查询
select_related括号内只能放外键字段(一对一或一对多)
多个外键字段: res = models.Book.objects.all().select_related('外键字段1,外键字段2,外键字段3,...')
跨外键字段: res = models.Book.objects.all().select_related('外键字段1__外键字段2__外键字段3__...')
"""

res = models.Book.objects.all()
for r in res:
    print(r.publish.name)  # 跨表查询,会频繁走数据库查询

res = models.Book.objects.all().select_related('publish')
print(res)  # <QuerySet [<Book: 三国>,...]> 返回的是对象
for r in res:  
    print(r.publish.name)  # 只走一次数据库查询



"""
多表连表操作时速度会慢,prefetch_related执行多次SQL查询再在Python代码中实现连表操作(先将book表中的publish_id全部拿出来,再去publish表中将publish_id对应的所有数据拿出来),prefetch_related括号内有几个外键字段就会走 几+1 次数据库查询操作
prefetch_related括号内只能放外键字段(一对多或多对多)
多个外键字段: res = models.Book.objects.all().prefetch_related('外键字段1,外键字段2,外键字段3,...') 
跨外键字段: res = models.Book.objects.all().prefetch_related('外键字段1__外键字段2__外键字段3__...') 
"""
# prefetch_related  不主动连表
res = models.Book.objects.prefetch_related('publish')
print(res)  # <QuerySer [<Book: 三国>,...]> 返回的是对象
for r in res:
	print(r.publish.name)  # 一个外键字段则会走两次数据库查询

bulk_create批量插入数据

bulk_create方法是django orm提供的批量操作数据库的方式,效率非常高(比models.Book.objects.create()高很多)

def book(request):
	l = []
	for i in range(10000):
    	l.append(models.Book(title='第%s本书'%i))
	models.Book.objects.bulk_create(l)  # 批量插入数据
	return render(request,'booklist.html',locals())

QuerySet方法大全

#################################
# 更改属性并返回新查询集的公共方法 #
#################################

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


############################
# 返回queryset子类的公共方法 #
############################

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 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对象


######################
# 执行数据库查询的方法 #
######################

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 in_bulk(self, id_list=None):
   # 根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

django admin后台管理

django admin后台管理,实现注册了的模型表数据的增删改查,必须是超级用户才能使用

使用:
在应用的admin.py文件中注册想要操作模型表表,再用超级用户账户登录admin后台进行数据操作

1.创建超级用户 createsuperuser

2.app01->admin.py中注册模型表

from app01 import models

admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Tag)
admin.site.register(models.Category)
admin.site.register(models.Article2Tag)
admin.site.register(models.Article)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)

3.登录django后台管理 http://127.0.0.1:8000/admin/

class UserInfo(AbstractUser):
    phone = models.BigIntegerField(null=True)
    avatar = models.FileField(upload_to='avatar/', default='avatar/default.webp')
    create_time = models.DateField(auto_now_add=True)
    blog = models.OneToOneField(to='Blog', null=True, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '用户表'  # 自定义admin后台管理表名展示

窥探admin内部源码(django admin会自动给注册了的模型表生成四条url(增删改查))
http://127.0.0.1:8000/admin/app01/userinfo/ 展示数据
http://127.0.0.1:8000/admin/app01/userinfo/add/ 添加数据
http://127.0.0.1:8000/admin/app01/userinfo/2/change/ 编辑数据
http://127.0.0.1:8000/admin/app01/userinfo/2/delete/ 删除数据

标签:__,objects,05,models,数据库,ORM,filter,Book,print
From: https://www.cnblogs.com/jlllog/p/17115174.html

相关文章