Django ORM
Django ORM(对象关系映射)允许开发者用 Python 类和对象来操作数据库,而无需直接编写 SQL 语句。本文介绍 Django 连接 MySQL 的配置方法、模型定义、数据增删改查、多表关联,以及 F/Q 查询、聚合、分组等进阶用法。
Django连接MySQL
Django默认用的数据库是sqlite3
在setting.py中找到关于SQL的配置项:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
若要改为MySQL数据库,要先在配置文件中配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_base',
'USER': 'root',
'PASSWORD': 'your_password',
'HOST': '118.25.65.95',
'PORT': '3306',
'CHARSET': 'UTF8'
}
}
配置完成后,需要做一个声明:
django默认用的是mysqldb模块链接MySQL,但是该模块的兼容性不好,需要手动改为用pymysql连接
需要告诉django不要用默认的mysqldb 而是用pymysql
在项目名下的init或者任意的应用名下的init文件中书写以下代码都可以
import pymysql
pymysql.install_as_MySQLdb()app配置单独的数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'bms', # 要连接的数据库,连接前需要创建好
'USER':'root', # 连接数据库的用户名
'PASSWORD':'', # 连接数据库的密码
'HOST':'127.0.0.1', # 连接主机,默认本级
'PORT':3306 # 端口 默认3306
},
'app01': { #可以为每个app都配置自己的数据,并且数据库还可以指定别的,也就是不一定就是mysql,也可以指定sqlite等其他的数据库
'ENGINE': 'django.db.backends.mysql',
'NAME':'bms', # 要连接的数据库,连接前需要创建好
'USER':'root', # 连接数据库的用户名
'PASSWORD':'', # 连接数据库的密码
'HOST':'127.0.0.1', # 连接主机,默认本级
'PORT':3306 # 端口 默认3306
}
}ORM
关系映射表,Object Relational Mapping,简称ORM
作用:
能够让一个不用sql语句的小白也能够通过python 面向对象的代码简单快捷的操作数据库
不足之处:
封装程度太高 有时候sql语句的效率偏低 需要你自己写SQL语句
与MySQL对比:
类 表
对象 记录
对象属性 记录某个字段对应的值
如何创建表:
1、先去models.py中定义一个类
class User(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
password = models.IntegerField()
2、将数据同步到MySQL中
python3 manage.py makemigrations
将操作记录记录到小本本上(migrations文件夹)
相当于将ORM语句转换为SQL语句
python3 manage.py migrate
将操作真正的同步到数据库中
注:只要你修改了models.py中跟数据库相关的代码 就必须重新执行上述的两条命令
__init__.py 配置
import pymysql
pymysql.install_as_MySQLdb()
settings.py 配置
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django',
'USER': 'root',
'PASSWORD': 'iZ!?kSxAgfBI_muydeMjR|4}t<*JlU/r',
'HOST': '106.14.213.94',
'PORT': '3306',
'CHARSET': 'UTF-8'
}
models.py 配置
from django.db import models
class User(models.Model):
uid = models.AutoField(primary_key=True, verbose_name='主键')
username = models.CharField(max_length=32, verbose_name='姓名')
password = models.IntegerField(verbose_name='密码')创建表的注意事项
由于一张表中必须要有一个主键字段,并且一般情况下都叫ID字段,所以ORM当你不定义主键字段的时候,ORM会自动帮你创建一个名为ID的主键字段,也就意味着,后续我们在创建模型表的时候如果主键字段没有额外的叫法,那么主键字段可以省略不写。
ORM常用字段
常用字段对应表:
'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、自增长字段:
id = models.AutoField(primary_key=True) # 系统会默认添加此字段,无需用户自己特地添加
id = models.BigAutoField()
自增长字段的意思是,数据表中每增加一条记录,这个字段的值就会自动加1。字段的类型默认为Int整型。下面的BigAutoField字段可容纳比较大的数,比如说十亿。但是需要注意的一点是,其实我们在定义数据库表结构的时候并不需要特地定义这样一个字段,因为Django会在每个表中自动添加一个id字段,且这个字段的类型正是自增长型。
2、二进制字段:
Binary = models.BinaryField()
在某些特殊情况下,可以用此字段类型来插入二进制数据。
3、布尔型字段:
Boolean = models.BooleanField()
NullBoolean = models.NullBooleanField()
Django提供了两种布尔类型的字段,上面这种不能为空,下面这种的字段值可以为空。
4、整型字段:
PositiveSmallInteger = models.PositiveSmallIntegerField() # 5个字节 正数
SmallInteger = models.SmallIntegerField() # 6个字节 正负数
PositiveInteger = models.PositiveIntegerField() # 10个字节 正数
Integer = models.IntegerField() # 11个字节
BigInteger = models.BigIntegerField() # 20个字节
Django提供了5种不同的整型字段,可以按照两个标准来进行分类,一个是按正负数来分(Positive),一个是按数值大小来分(Small、Big)具体分类如上。
5、字符串类型:
Char = models.CharField() # varchar
Char = models.CharField(max_length=14,verbose_name='用户名')
Text = models.TextField() # longtext
字符串类型的字段分为两种,上面这种对应数据库中的varchar,需要在参数max_length中指定字符串的大小。下面这种对应数据库中的longtext类型,无需指定字符串长度,想写多长就写多长。
6、时间类型:
Date = models.DateField() # 年月日
DateTime = models.DateTimeField() # 年月日 时分秒
Duration = models.DurationField() # int, Python timedelta实现
时间类型分为三种,Date是年月日的形式,DateTime是年月日时分秒的形式,第三种表示一段时间,在数据表中是Int类型,它的底层是通过python的timedelta实现的。
7、浮点型:
Float = models.FloatField()
Decimal = models.DecimalField() # 11.22, 16.34
浮点型也有两种,其中第二种Decimal比较特殊,需要在参数中指定整数有多少位,小数有多少位。
8、其他类型:
Email = models.EmailField() # 邮箱
Image = models.ImageField() # 图片
File = models.FileField() # 文件
FilePath = models.FilePathField() # 文件路径
URL = models.URLField() # URL地址
UUID = models.UUIDField() # UID
GenericIPAddress = models.GenericIPAddressField() #IP地址,IPV4或者IPV6
9、关系型字段:
OneToOneField:一对一
一对一表关系放在任意一个字段都可以,但是建议放在常用的表中
to="AuthorDetail" 关联的表
to_field="nid" 关联哪个字段
on_delete=models.CASCADE 级联删除
ForeignKey:多对一
建立一对多的关系,外键字段建立在多的一方
to
设置要关联的表
to_field
设置要关联的表的字段
related_name
反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
related_query_name
反向查询操作时,使用的连接前缀,用于替换表名。
on_delete
当删除关联表中的数据时,也删除当前表与其关联的行的行为。
ManyToManyField:多对多
三种创建方式:
方式一:自行创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
# 自己创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
class Meta:
unique_together = ("author", "book")
方式二:通过ManyToManyField自动创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
# 通过ORM自带的ManyToManyField自动创建第三张表
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", related_name="authors") #自动生成的第三张表我们是没有办法添加其他字段的
方式三:设置ManyTomanyField并指定自行创建的第三张表(称为中介模型)
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
# 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", through="Author2Book",
through_fields=("author", "book"))
# through_fields接受一个2元组('field1','field2'):
# 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
#可以扩展其他的字段了
class Meta:
unique_together = ("author", "book")
创建多对多表的时候的一些参数:
多对多的参数:
to
设置要关联的表
related_name
同ForeignKey字段。
related_query_name
同ForeignKey字段。
through
在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过
through来指定第三张表的表名。
through_fields
设置关联的字段。
db_table
默认创建第三张表时,数据库中表的名称。
当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式,第三种方式还是可以使用多对多关联关系操作的接口(all、add、clear等等)
当我们使用第一种方式创建多对多关联关系时,就无法使用orm提供的set、add、remove、clear方法来管理多对多的关系了。字段参数
在原生SQL定义数据表的时候我们常常需要给字段设定一些参数,比如说是否为空,默认值等等。那么在Django的模型类中我们该如何设置呢?首先,我们要知道,Django字段的参数分如下三种情况:
1、所有字段都具有的参数
1.更改字段名:db_colum=” “
2.设置主键:primary_key=True,默认为False
3.给字段设置别名(备注):verbose_name=” “
4.字段的唯一键属性:unique=True,设置之后,这个字段的每一条记录的每个值是唯一的
5.允许字段为空:null=True(数据库中字段可以为空),blank=True(网页表单提交内容可以为空),切记不可以将null设置为Fasle的同时还把blank设置为True。会报错的。
6.给字段建立索引:db_index=True
7.在表单中显示说明:help_text=” “
8.字段值不允许更改:editable=False,默认是True,可以更改。
9.字段的默认值:default
可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
10.choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
11.auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
12.auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
2、个别字段才拥有的参数
1.CharField(max_length=100):字段长度为utf8编码的100个字符串
2.DateField(unique_for_date=True):这个字段的时间必须唯一
3.DateField(auto_now=True):对这条记录内容更新的时间
4.DateField(auto_now_add=True):插入这条记录的时间
5.DecimalField(max_digits=4, decimal_places=2):前者表示整数和小数总共多少数,后者表示小数点的位数
3、关系型字段的参数
on_delelte= 这个参数表示外键所关联的对象被删除的时候要进行什么操作,比如说一篇文章对应一个Python的分类,但是这个分类被你不小心删除了,那么这篇文章的分类应该会发生什么变化呢?主要有以下六种:
CASCADE:模拟SQL语言中的ON DELETE CASCADE约束,将定义有外键的模型对象同时删除!(该操作为当前Django版本的默认操作!)
PROTECT:阻止上面的删除操作,但是弹出ProtectedError异常
SET_NULL:将外键字段设为null,只有当字段设置了null=True时,方可使用该值。
SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用。
DO_NOTHING:什么也不做。
SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写。
4、自关联字段参数
需要在第一个参数中添加‘self’字符串,或写上它自己的表名(模型类名)。补充
(1)null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
(1)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
(2)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
(3)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
(4)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
(6)db_index
如果db_index=True 则代表着为此字段设置数据库索引。
DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
(7)auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
(8)auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
(9) choices属性
sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))
数据库里面存的是1或者2
通过model模型类对象.get_属性名称_display()可以获取到数字对应的文本内容
auto_now_add和auto_now参数
class t1(models.Model):
# defauit 在使用orm操作添加数据时生效.
name = models.CharField(max_length=12, default='张三')
sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))
d1 = models.DateTimeField(auto_now_add=True,null=True) #自动添加创建记录的时间
d2 = models.DateTimeField(auto_now=True,null=True) #自动添加创建记录的时间,更新记录是也能自动更新为最新时间
auto_now 自动更新时间只有在save更新时才生效,update不生效
所以如果要做更新时间的处理,那么最好手动获取当前时间并修改redirect括号内可以直接写URL,也可以直接写别名,但是如果别名需要额外给参数的话,那么就必须使用reverse解析字段的增删改查
字段的增加:
直接在文件中增加就可以了
字段的修改:
直接修改代码然后执行数据库同步的两条命令即可
字段的删除:
直接注释对应的字段然后执行数据库迁移的两条命令即可
注:执行完毕之后字段对应的数据也都没有了
因此,在操作models.py文件时候一定要仔细,执行迁移的命令之前,最好先检查一下自己的代码在增加字段的时候,若没有指定默认值,则会抛出异常:
You are trying to add a non-nullable field 'hobby' to user without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option:
解决方法有三种:
1、直接在终端中给出默认值
2、在增加字段的时候,指定该字段可以为空
info = models.CharField(max_length=32,verbose_name='个人简介',null=True)
3、在增加字段时候,直接设置默认值
hobby = models.CharField(max_length=32,verbose_name='兴趣爱好',default='study')数据的增加
方法一:
def index(request):
# 增加
obj = models.Book(
bid='1',
title='三国演义',
price=9.99,
pub_date=datetime.datetime.now(),
publish='红浪漫出版社',
)
obj.save()
方法二:
models.Book.objects.create(
bid='2',
title='红楼梦',
price=19.99,
pub_date=datetime.datetime.now(),
publish='新华出版社',
)
批量添加:
obj_list = []
for i in range(3,10):
obj = models.Book(
bid=i,
title='西游记'+ str(i),
price=99.99,
pub_date=datetime.datetime.now(),
publish='夕阳红出版社',
)
obj_list.append(obj)
models.Book.objects.bulk_create(obj_list)
数据的修改
方法一:批量更新,只修改被修改过的字段
models.Book.objects.filter(bid=4).update(
title = '水浒传',
price = 59.99
)
方法二:
obj = models.Book.objects.get(bid=6)
obj.price = 18
obj.save()
方式二:全部更新,从头到尾将数据的所有字段全部更新一边 无论该字段是否被修改
edit_obj.username = username
edit_obj.password= password
edit_obj.save()数据的删除
方式一:批量删除,只删除被修改过的字段
models.Book.objects.filter(title='三国演义').delete()
方式二:
models.Book.objects.get(id=3).delete()
query类型数据和模型类对象都可以调用delete方法来进行删除
在生产中,删除数据内部其实并不是真正的删除 我们会给数据添加一个标识字段用来表示当前数据是否被删除了,如果数据被删了仅仅只是将字段修改一个状态数据的查询
<1> all():
查询所有结果,结果是queryset类型
<2> filter(**kwargs):
它包含了与所给筛选条件相匹配的对象,结果也是queryset类型
Book.objects.filter(title='linux',price=100) # 里面的多个条件用逗号分开,并且这几个条件必须都成立,是and的关系,or关系的我们后面再学,直接在这里写是搞不定or的
<3> get(**kwargs):
返回与所给筛选条件相匹配的对象,不是queryset类型,是行记录对象,返回结果有且只有一个,
如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。
Book.objects.get(id=1)
<4> exclude(**kwargs):
排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作,用这个exclude,返回值是queryset类型Book.objects.exclude(id=6),返回id不等于6的所有的对象,或者在queryset基础上调用,Book.objects.all().exclude(id=6)
<5> order_by(*field):
queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按照字段降序排列,就写个负号就行了order_by('-price'),order_by('price','id')是多条件排序,按照price进行升序,price相同的数据,按照id进行升序
<6> reverse():
queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型
<7> count():
queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。
<8> first():
queryset类型的数据来调用,返回第一条记录
Book.objects.all()[0] = Book.objects.all().first() # 得到的都是model对象,不是queryset
<9> last():
queryset类型的数据来调用,返回最后一条记录
<10> exists():
queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False
空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits
例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据
<11> values(*field):
用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
<12> values_list(*field):
它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<13> distinct():
values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录基于双下划线的模糊查询
神奇的双下划线查询
# 神奇的双下划线查询
# 1 年龄大于35岁的数据
# res = models.User.objects.filter(age__gt=35)
# print(res)
# 2 年龄小于35岁的数据
# res = models.User.objects.filter(age__lt=35)
# print(res)
# 大于等于 小于等于
# res = models.User.objects.filter(age__gte=32)
# print(res)
# res = models.User.objects.filter(age__lte=32)
# print(res)
# 年龄是18 或者 32 或者40
# res = models.User.objects.filter(age__in=[18,32,40])
# print(res)
# 年龄在18到40岁之间的 首尾都要
# res = models.User.objects.filter(age__range=[18,40])
# print(res)
# 查询出名字里面含有s的数据 模糊查询
# res = models.User.objects.filter(name__contains='s')
# print(res)
#
# 是否区分大小写 查询出名字里面含有p的数据 区分大小写
# res = models.User.objects.filter(name__contains='p')
# print(res)
# 忽略大小写
# res = models.User.objects.filter(name__icontains='p')
# print(res)
# res = models.User.objects.filter(name__startswith='j')
# res1 = models.User.objects.filter(name__endswith='j')
#
# print(res,res1)
# 查询出注册时间是 2020 1月
# res = models.User.objects.filter(register_time__month='1')
# res = models.User.objects.filter(register_time__year='2020')表与表关系
表与表之间的关系:
一对多
多对多
一对一
没有关系
判断表关系的方法:换位思考
图书和出版社是一对多的关系 外键字段建在多的那一方 book
图书和作者是多对多的关系 需要创建第三张表来专门存储
作者与作者详情表是一对一
# 在django1.X版本中外键默认都是级联更新删除的
# 多对多的表关系可以有好几种创建方式 这里暂且先介绍一种
# 针对外键字段里面的其他参数 暂时不要考虑 如果感兴趣自己可以百度试试看单表操作
多表关系创建
from django.db import models
# Create your models here.
# 作者表
class Author(models.Model): #比较常用的信息放到这个表里面
name=models.CharField( max_length=32)
age=models.IntegerField() #int
# 与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
ad = models.OneToOneField(to="AuthorDetail", to_field='id', on_delete=models.CASCADE)
# authordetail_id
# foreign key + unique
# 作者详细信息表
class AuthorDetail(models.Model):#不常用的放到这个表里面
birthday=models.DateField()
telephone=models.CharField(max_length=11)
addr=models.CharField(max_length=64)
# 出版社表
class Publish(models.Model):
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
# 书籍表
class Book(models.Model):
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="id",on_delete=models.CASCADE)
publish=models.ForeignKey(to="Publish") #默认级联删除,默认关联的是另外一张表的id字段
authors=models.ManyToManyField(to='Author',) #自动创建第三张表,id author_id book_id,不会作为本表的字段出现一对一外键的增删改查
models.AuthorDetail.objects.create(
birthday='2018-01-01',
telephone='13800000000',
addr='北京'
)
ad_obj = models.AuthorDetail.objects.get(id=1)
models.Author.objects.create(
name='明皓',
age=38,
# ad=ad_obj,
ad_id=2,
)
ad_obj = models.AuthorDetail.objects.get(id=4)
obj= models.Author(
name='杨浩',
age=47,
ad=ad_obj,
# ad_id=3,
)
obj.save()一对多外键的增删改查
一对多外键增删改查
# 一对多外键增删改查
# 增
# 1 直接写实际字段 id
# models.Book.objects.create(title='论语',price=899.23,publish_id=1)
# models.Book.objects.create(title='聊斋',price=444.23,publish_id=2)
# models.Book.objects.create(title='老子',price=333.66,publish_id=1)
# 2 虚拟字段 对象
# publish_obj = models.Publish.objects.filter(pk=2).first()
# models.Book.objects.create(title='红楼梦',price=666.23,publish=publish_obj)
# 删
# models.Publish.objects.filter(pk=1).delete() # 级联删除
# 修改
# models.Book.objects.filter(pk=1).update(publish_id=2)
# publish_obj = models.Publish.objects.filter(pk=1).first()
# models.Book.objects.filter(pk=1).update(publish=publish_obj)
多对多外键的增删改查
# 如何给书籍添加作者?
book_obj = models.Book.objects.filter(pk=1).first()
# print(book_obj.authors) # 就类似于你已经到了第三张关系表了
# book_obj.authors.add(1) # 书籍id为1的书籍绑定一个主键为1 的作者
# book_obj.authors.add(2,3)
# 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给第三张关系表添加数据
括号内既可以传数字也可以传对象 并且都支持多个
"""
# 删
# book_obj.authors.remove(2)
# book_obj.authors.remove(1,3)
# author_obj = models.Author.objects.filter(pk=2).first()
# author_obj1 = models.Author.objects.filter(pk=3).first()
# book_obj.authors.remove(author_obj,author_obj1)
"""
remove
括号内既可以传数字也可以传对象 并且都支持多个
"""
# 修改
# book_obj.authors.set([1,2]) # 括号内必须给一个可迭代对象
# book_obj.authors.set([3]) # 括号内必须给一个可迭代对象
# author_obj = models.Author.objects.filter(pk=2).first()
# author_obj1 = models.Author.objects.filter(pk=3).first()
# book_obj.authors.set([author_obj,author_obj1]) # 括号内必须给一个可迭代对象
"""
set
括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个
"""
# 清空
# 在第三张关系表中清空某个书籍与作者的绑定关系
book_obj.authors.clear()
"""
clear
括号内不要加任何参数
"""批量插入数据
def ab_pl(request):
# 先给Book插入一万条数据
# for i in range(10000):
# models.Book.objects.create(title='第%s本书'%i)
# # 再将所有的数据查询并展示到前端页面
book_queryset = models.Book.objects.all()
# 批量插入
# book_list = []
# for i in range(100000):
# book_obj = models.Book(title='第%s本书'%i)
# book_list.append(book_obj)
# models.Book.objects.bulk_create(book_list)
"""
当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间
:param request:
:return:
"""
return render(request,'ab_pl.html',locals())自定义分页器
级联更新删除
一对一和一对多 ,基本和单表一样(级联删除)
models.Author.objects.get(id=1).delete()
models.AuthorDetail.objects.get(id=2).delete()
models.Book.objects.get(id=1).delete()Django测试脚本
"""
当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式
而是直接写一个测试脚本即可
脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以
"""
# 测试环境的准备 去manage.py中拷贝前四行代码 然后自己写两行
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
import django
django.setup()
# 在这个代码块的下面就可以测试django里面的单个py文件了
查看内部SQL语句的方式
# 方式1
res = models.User.objects.values_list('name','age') # <QuerySet [('jason', 18), ('egonPPP', 84)]>
print(res.query)
queryset对象才能够点击query查看内部的sql语句
# 方式2:所有的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',
},
}
}正向反向概念
正反向的概念:
# 正向
# 反向
外键字段在我手上那么,我查你就是正向
外键字段如果不在手上,我查你就是反向
book >>>外键字段在书那儿(正向)>>> publish
publish >>>外键字段在书那儿(反向)>>>book
一对一和多对多正反向的判断也是如此
"""
正向查询按字段
反向查询按表名小写
_set
...
"""
db_constraint=False多表查询
多表查询
子查询(基于对象的跨表查询)
# 1.查询书籍主键为1的出版社
# book_obj = models.Book.objects.filter(pk=1).first()
# # 书查出版社 正向
# res = book_obj.publish
# print(res)
# print(res.name)
# print(res.addr)
# 2.查询书籍主键为2的作者
# book_obj = models.Book.objects.filter(pk=2).first()
# # 书查作者 正向
# # res = book_obj.authors # app01.Author.None
# res = book_obj.authors.all() # <QuerySet [<Author: Author object>, <Author: Author object>]>
#
# print(res)
# 3.查询作者jason的电话号码
# author_obj = models.Author.objects.filter(name='jason').first()
# res = author_obj.author_detail
# print(res)
# print(res.phone)
# print(res.addr)
"""
在书写orm语句的时候跟写sql语句一样的
不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点
正向什么时候需要加.all()
当你的结果可能有多个的时候就需要加.all()
如果是一个则直接拿到数据对象
book_obj.publish
book_obj.authors.all()
author_obj.author_detail
"""
# 4.查询出版社是东方出版社出版的书
# publish_obj = models.Publish.objects.filter(name='东方出版社').first()
# 出版社查书 反向
# res = publish_obj.book_set # app01.Book.None
# res = publish_obj.book_set.all()
# print(res)
# 5.查询作者是jason写过的书
# author_obj = models.Author.objects.filter(name='jason').first()
# 作者查书 反向
# res = author_obj.book_set # app01.Book.None
# res = author_obj.book_set.all()
# print(res)
# 6.查询手机号是110的作者姓名
# author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
# res = author_detail_obj.author
# print(res.name)
"""
基于对象
反向查询的时候
当你的查询结果可以有多个的时候 就必须加_set.all()
当你的结果只有一个的时候 不需要加_set.all()
自己总结出 自己方便记忆的即可 每个人都可以不一样
"""联表查询
联表查询(基于双下划线的跨表查询)
# 基于双下划线的跨表查询
# 1.查询jason的手机号和作者姓名
# res = models.Author.objects.filter(name='jason').values('author_detail__phone','name')
# print(res)
# 反向
# res = models.AuthorDetail.objects.filter(author__name='jason') # 拿作者姓名是jason的作者详情
# res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name')
# print(res)
# 2.查询书籍主键为1的出版社名称和书的名称
# res = models.Book.objects.filter(pk=1).values('title','publish__name')
# print(res)
# 反向
# res = models.Publish.objects.filter(book__id=1).values('name','book__title')
# print(res)
# 3.查询书籍主键为1的作者姓名
# res = models.Book.objects.filter(pk=1).values('authors__name')
# print(res)
# 反向
# res = models.Author.objects.filter(book__id=1).values('name')
# print(res)
# 查询书籍主键是1的作者的手机号
# book author authordetail
# res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
# print(res)
"""
你只要掌握了正反向的概念
以及双下划线
那么你就可以无限制的跨表聚合查询
聚合查询:
# 聚合查询 aggregate
"""
聚合查询通常情况下都是配合分组一起使用的
只要是跟数据库相关的模块
基本上都在django.db.models里面
如果上述没有那么应该在django.db里面
"""
from app01 import models
from django.db.models import Max,Min,Sum,Count,Avg
# 1 所有书的平均价格
# res = models.Book.objects.aggregate(Avg('price'))
# print(res)
# 2.上述方法一次性使用
res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))
print(res)分组查询
分组查询
# 分组查询 annotate
"""
MySQL分组查询都有哪些特点
分组之后默认只能获取到分组的依据 组内其他字段都无法直接获取了
严格模式
ONLY_FULL_GROUP_BY
"""
from django.db.models import Max, Min, Sum, Count, Avg
# 1.统计每一本书的作者个数
# res = models.Book.objects.annotate() # models后面点什么 就是按什么分组
# res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num')
"""
author_num是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数
"""
# res1 = models.Book.objects.annotate(author_num=Count('authors__id')).values('title','author_num')
# print(res,res1)
"""
代码没有补全 不要怕 正常写
补全给你是pycharm给你的 到后面在服务器上直接书写代码 什么补全都没有 颜色提示也没有
"""
# 2.统计每个出版社卖的最便宜的书的价格(作业:复习原生SQL语句 写出来)
# res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
# print(res)
# 3.统计不止一个作者的图书
# 1.先按照图书分组 求每一本书对应的作者个数
# 2.过滤出不止一个作者的图书
# res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num')
# """
# 只要你的orm语句得出的结果还是一个queryset对象
# 那么它就可以继续无限制的点queryset对象封装的方法
#
# """
# print(res)
# 4.查询每个作者出的书的总价格
# res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
# print(res)
"""
如果我想按照指定的字段分组该如何处理呢?
models.Book.objects.values('price').annotate()
后续BBS作业会使用
你们的机器上如果出现分组查询报错的情况
你需要修改数据库严格模式
"""F与Q查询
# F查询
# 1.查询卖出数大于库存数的书籍
# F查询
"""
能够帮助你直接获取到表中某个字段对应的数据
"""
from django.db.models import F
# res = models.Book.objects.filter(maichu__gt=F('kucun'))
# print(res)
# 2.将所有书籍的价格提升500块
# models.Book.objects.update(price=F('price') + 500)
# 3.将所有书的名称后面加上爆款两个字
"""
在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
"""
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
# models.Book.objects.update(title=F('title') + '爆款') # 所有的名称会全部变成空白
# Q查询
# 1.查询卖出数大于100或者价格小于600的书籍
# res = models.Book.objects.filter(maichu__gt=100,price__lt=600)
"""filter括号内多个参数是and关系"""
from django.db.models import Q
# res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600)) # Q包裹逗号分割 还是and关系
# res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600)) # | or关系
# res = models.Book.objects.filter(~Q(maichu__gt=100)|Q(price__lt=600)) # ~ not关系
# print(res) # <QuerySet []>
# Q的高阶用法 能够将查询条件的左边也变成字符串的形式
q = Q()
q.connector = 'or'
q.children.append(('maichu__gt',100))
q.children.append(('price__lt',600))
res = models.Book.objects.filter(q) # 默认还是and关系
print(res)事务
"""
事务
ACID
原子性
不可分割的最小单位
一致性
跟原子性是相辅相成
隔离性
事务之间互相不干扰
持久性
事务一旦确认永久生效
事务的回滚
rollback
事务的确认
commit
"""
# 目前你只需要掌握Django中如何简单的开启事务
# 事务
from django.db import transaction
try:
with transaction.atomic():
# sql1
# sql2
...
# 在with代码快内书写的所有orm操作都是属于同一个事务
except Exception as e:
print(e)
print('执行其他操作')最后更新于