连接MySQL数据库
提前创建好数据库
数据库相关配置
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day59',
'USER': 'root',
'PASSWORD': '123',
'HOST': '127.0.0.1',
'PORT': 3306,
'CHARSET': 'utf8'
}
}
定义模型类
# models.py
from django.db import models
# Create your models here.
class User(models.Model):
name = models.CharField(max_length=32, verbose_name='用户名')
age = models.IntegerField(verbose_name='年龄')
in_time = models.DateTimeField(auto_now_add=True, verbose_name='注册时间')
日期字段的参数auto_now是每次操作数据并保存的时候会自动更新当前时间,auto_now_add只有在创建数据时会自动添加当前时间,后续没有人为修改就不会改变。
执行数据库迁移命令
python manage.py makemigrations
python manage.py migrate
测试环境准备
方式一:
# tests.py
from django.test import TestCase
# Create your tests here.
import os
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
import django
django.setup()
from app01 import models
# 插入数据
models.User.objects.create(name='jasper', age=18)
models.User.objects.create(name='jason', age=19)
models.User.objects.create(name='jack', age=20)
models.User.objects.create(name='tom', age=21)
models.User.objects.create(name='lili', age=22)
if __name__ == '__main__':
main()
方式二:在pycharm的Python Consele控制台操作。
all():
返回当前 QuerySet (或 QuerySet 子类)的副本。
print(models.User.objects.all())
结果:
, , , , ]>
filter():filter(*args, **kwargs)
返回一个新的 QuerySet,其中包含与给定查找参数相匹配的对象。
print(models.User.objects.filter(pk=1))
结果:
]>
get():get(*args, **kwargs)
返回与给定的查找参数相匹配的对象,你应该使用保证唯一的查询,比如主键或唯一约束中的字段。例如:
print(models.User.objects.filter(pk=1))
结果:
]>
values():values(*fields, **expressions)
返回一个 QuerySet,当用作可迭代对象时,返回字典,而不是模型实例。
其中每一个字典都代表一个对象,键与模型对象的属性名相对应。
print(models.User.objects.values('name'))
values_list():values_list(*fields, flat=False, named=False)
这与 values() 类似,只是在迭代时不返回字典,而是返回元组。每一个元组都包含了传入 values_list() 调用的各个字段或表达式的值——所以第一项是第一个字段。
print(models.User.objects.values_list('name', 'age'))
order_by():order_by(*fields)
字段前面加负号表示降序。升序是隐含的。要随机排序,使用 “?”。
print(models.User.objects.order_by('-age'))
print(models.User.objects.order_by('?'))
, , , , ]>
, , , , ]>
reverse():
使用 reverse() 方法来反向返回查询集元素的顺序。第二次调用 reverse() 会将顺序恢复到正常方向。
print(models.User.objects.order_by('-age').reverse())
print(models.User.objects.order_by('-age').reverse().reverse())
, , , , ]>
, , , , ]>
请注意,reverse() 一般只应在有定义顺序的 QuerySet 上调用(例如,当对定义了默认顺序的模型进行查询时,或者当使用 order_by() 时)。如果没有为一个给定的 QuerySet 定义这样的排序,对它调用 reverse() 没有实际效果(在调用 reverse() 之前,排序是未定义的,之后也将保持未定义)。
distinct():distinct(*fields)
返回一个新的 QuerySet,在其 SQL 查询中使用 SELECT DISTINCT。这将消除查询结果中的重复记录。
models.User.objects.create(name='jasper', age=18)
print(models.User.objects.values('name').distinct())
如果想用distinct的话,在distinct前面加上values或values_list
count():
返回一个整数,表示数据库中与 QuerySet 匹配的对象数量。
print(models.User.objects.count())
6
count() 调用在幕后执行 SELECT COUNT(*),所以你应该总是使用 count() 而不是将所有的记录加载到 Python 对象中,然后在结果上调用 len() (除非你需要将对象加载到内存中,在这种情况下 len() 会更快)。
请注意,如果你想知道 QuerySet 中的项数,并且也要从它中检索模型实例(例如,通过迭代它),那么使用 len(queryset) 可能更有效,因为它不会像 count() 那样引起额外的数据库查询
如果查询集已经被完全检索到,count() 将使用该长度,而不是执行额外的数据库查询。
first():
返回查询集匹配的第一个对象,如果没有匹配的对象,则返回 None。如果 QuerySet 没有定义排序,那么查询集自动按主键排序。
print(models.User.objects.first())
User object (1)
last():
与 first() 工作原理相同,但返回的是查询集中的最后一个对象。
exists()
如果 QuerySet 包含任何结果,则返回 True,如果不包含,则返回 False。
print(models.User.objects.filter(name='xxx').exists())
print(models.User.objects.filter(name='jasper').exists())
False
True
exclude():exclude(*args, **kwargs)
返回一个新的 QuerySet,其中包含与给定查找参数不匹配的对象。
print(models.User.objects.exclude(name='jasper'))
, , , ]>
raw():raw(raw_query, params=(), translations=None, using=None)
获取一个原始 SQL 查询,执行它,并返回一个 django.db.models.query.RawQuerySet 实例。这个 RawQuerySet 实例可以像普通的 QuerySet 一样进行迭代,提供对象实例。
print(list(models.User.objects.raw("select * from app01_user")))
[, , , , , ]
使用原生sql的方式二:
from django.db import connection
cursor = connection.cursor()
cursor.execute("select * from app01_user where id < 2")
print(cursor.fetchall())
((1, 'jasper', 18, datetime.datetime(2022, 9, 5, 8, 32, 36, 835704)),)
总结:
官方文档:https://docs.djangoproject.com/zh-hans/4.0/ref/models/querysets/
print(models.User.objects.filter(id__gt=5))
print(models.User.objects.filter(id__lt=5))
print(models.User.objects.filter(id__gte=5))
print(models.User.objects.filter(id__lte=5))
]>
, , , ]>
, ]>
, , , , ]>
print(models.User.objects.filter(name__in=('jasper', 'jack')))
, , ]>
print(models.User.objects.filter(id__range=(1, 4)))
, , , ]>
print(models.User.objects.filter(name__contains=('j')))
, , , ]>
print(models.User.objects.filter(in_time__day=5))
print(models.User.objects.filter(in_time__month=8))
, , , , , ]>
方式一:如果要QuerySet对象,直接点query即可查看对应的sql语句。
方式二:在setting.py文件中配置,拷贝下边代码即可。
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level': 'DEBUG',
},
}
}
# 创建表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.FloatField()
# 书籍和出版社是一对多关系 外键建立多的一方 需要指定on_delete参数
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
# 数据和作者之间是多对多关系 外键建立在查询频率较高的一方
auther = models.ManyToManyField(to='Auther')
class Publish(models.Model):
p_name = models.CharField(max_length=32)
class Auther(models.Model):
a_name = models.CharField(max_length=32)
# 作者与作者详情表之间是一对一关系 外键字段建立在查询频率较高的一方 需要指定on_delete参数
auther_info = models.OneToOneField(to='AutherInfo', on_delete=models.CASCADE)
class AutherInfo(models.Model):
age = models.IntegerField()
address = models.CharField(max_length=32)
phone_number = models.CharField(max_length=32)
# 插入数据
models.AutherInfo.objects.create(age=18, address='西安', phone_number='110')
models.AutherInfo.objects.create(age=20, address='北京', phone_number='120')
models.AutherInfo.objects.create(age=30, address='上海', phone_number='130')
models.AutherInfo.objects.create(age=49, address='广州', phone_number='140')
models.AutherInfo.objects.create(age=34, address='深圳', phone_number='150')
models.Auther.objects.create(a_name='瑞文', auther_info_id=1)
models.Auther.objects.create(a_name='剑姬', auther_info_id=5)
models.Auther.objects.create(a_name='猴子', auther_info_id=3)
models.Auther.objects.create(a_name='剑魔', auther_info_id=4)
models.Auther.objects.create(a_name='瑟提', auther_info_id=2)
models.Publish.objects.create(p_name='东方出版社')
models.Publish.objects.create(p_name='西方出版社')
models.Publish.objects.create(p_name='南方出版社')
models.Publish.objects.create(p_name='北方出版社')
add():
# 书籍添加 外键值可以传真正的出版社id 也可以传出版社对象
models.Book.objects.create(title='鲁滨逊漂流记', price=10.9, publish_id=1)
models.Book.objects.create(title='牛虻', price=20.9, publish_id=3)
pub_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='安徒生童话', price=30.9, publish=pub_obj)
pub_obj1 = models.Publish.objects.filter(pk=4).first()
models.Book.objects.create(title='我要做个好孩子', price=50.9, publish=pub_obj1)
# 第三张虚拟表外键字段的添加 先求出书籍对象 用书籍对象点多对多外键的名字 就相当于进入第三张表中
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.auther.add(1)
book_obj2 = models.Book.objects.filter(pk=2).first()
book_obj2.auther.add(1, 2, 3)
book_obj3 = models.Book.objects.filter(pk=3).first()
book_obj3.auther.add(2, 4, 5)
book_obj4 = models.Book.objects.filter(pk=4).first()
book_obj4.auther.add(5)
# 可以传集体的id值 也可以传对象 支持传多个
remove():
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.auther.remove(2)
# 可以传主键值 也可以传对象 也支持传多个 用逗号隔开
set():
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.auther.set((3, 4, 5))
# set必须接收一个可迭代对象 可以是数字 也可以是对象 其实是删除加新增
clear():
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.auther.clear()
# 第三张表中清空关系
外键字段如果在我手上,我查你就是正向,比如由书名查出版社,外键字段在书这,所以是正向。反之则是反向。
口诀:正向查询按字段,反向查询按表名小写
# 1 查询书籍主键值为1的出版社
# 1.1 由书籍主键值拿到书籍对象
book_obj = models.Book.objects.filter(pk=1).first()
# 1.2 判断正反向查询
p_obj = book_obj.publish
# 2 查询书籍主键值为2的作者
book_obj = models.Book.objects.filter(pk=2).first()
au_obj = book_obj.auther.all()
# , , ]>
# 3 查询作者猴子的地址
au_obj = models.Auther.objects.filter(a_name='猴子').first()
add = au_obj.auther_info.address # 上海
# 4. 查询出版社是东方出版社出版的书
pub_obj = models.Publish.objects.filter(p_name='东方出版社').first()
# 反向
print(pub_obj.book_set.all()) # ]>
# 5. 查询作者是剑姬写过的书
au_obj = models.Auther.objects.filter(a_name='剑姬').first()
print(au_obj.book_set.all()) # ]>
# 查询手机号是130的作者
au_info_obj = models.AutherInfo.objects.filter(phone_number='130').first()
print(au_info_obj.auther) # Auther object (3)
总结:正向时当查询的结果可能有多个时就需要加all(),如果是一个,则直接拿到对象。反向时当结果有多个时需要加_set.all(),只有一个就不需要加。
# 基于双下划线的多表查询
# 1 查询猴子的手机号
# 正向
res = models.Auther.objects.filter(a_name='猴子').values('auther_info__phone_number')
print(res) #
# 反向
r_res = models.AutherInfo.objects.filter(auther__a_name='猴子').values('phone_number')
print(r_res)
# 2 查询书籍主键为2的出版社名和书名
# 正向
res1 = models.Book.objects.filter(pk=2).values('publish__p_name', 'title')
print(res1) #
# 反向
r_res1 = models.Publish.objects.filter(book__pk=2).values('p_name', 'book__title')
print(r_res1)
# 3 查询书籍主键为3的作者姓名
# 正向
res2 = models.Book.objects.filter(pk=3).values('auther__a_name')
print(res2)
# < QuerySet[{'auther__a_name': '剑姬'}, {'auther__a_name': '剑魔'}, {'auther__a_name': '瑟提'}] >
# 反向
r_res2 = models.Auther.objects.filter(book__pk=3).values('a_name')
print(r_res2)
# 4 查询书籍主键是3的作者手机号
res = models.Book.objects.filter(pk=3).values('auther__auther_info__phone_number')
print(res)
#
# {'auther__auther_info__phone_number': '140'}, {'auther__auther_info__phone_number': '120'}]>
# 1.查询主键为1的书籍对应的出版社名称及书名
# res = models.Publish.objects.filter(book__pk=1).values('p_name', 'book__title')
# print(res) #
# 2.查询主键为3的书籍对应的作者姓名及书名
res = models.Auther.objects.filter(book__pk=3).values('a_name', 'book__title')
# 3.查询剑姬的作者的电话号码和地址
res = models.AutherInfo.objects.filter(auther__a_name='剑姬').values('phone_number', 'address')
# 4.查询南方出版社出版的书籍名称和价格
res = models.Book.objects.filter(publish__p_name='南方出版社').values('title', 'price')
# 5.查询猴子写过的书的名称和价格
res = models.Book.objects.filter(auther__a_name='猴子').values('title', 'price')
# 6.查询电话是110的作者姓名和年龄
res = models.Auther.objects.filter(auther_info__phone_number='110').values('a_name', 'auther_info__age')
# 7.查询主键为2的书籍对应的作者电话号码
res = models.Auther.objects.filter(book__pk=2).values('auther_info__phone_number')
print(res)
聚合函数:max、min、sum、avg、count
聚合查询:在没有分组前使用聚合函数需要关键字aggregate
from django.db.models import Max, Min, Sum, Avg, Count
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res) # {'price__max': 50.9, 'price__min': 10.9, 'price__sum': 113.6, 'price__avg': 28.4, 'pk__count': 4}
分组之后,默认只能获取分组之后的字段,获取其他字段需要使用方法。
我们也可以忽略掉此特性,将sql_mode中的only_full_group_by配置移除即可。
按照整条数据分组:models.Book.objects.annotate()
按照表中某个字段分组:models.Book.objects.values(‘name’).annotate()
# 示例1:统计每一本书的作者个数
res = models.Book.objects.annotate(count_num=Count('auther__pk')).values('count_num')
print(res) #
# 示例2:统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('min_price', 'p_name')
print(res)
# 示例3:统计不止一个作者的图书
res = models.Book.objects.annotate(num=Count('auther__pk')).filter(num__gt=1).values('title', 'num')
print(res)
# 示例4:查询各个作者出的书的总价格
res = models.Auther.objects.annotate(num=Sum('book__price')).values('a_name', 'num')
print(res)
F查询:查询的条件不是自定义的而是来自于表中的其他字段。
# 手动book表中添加数据
store = models.IntegerField(default=1)
sale = models.IntegerField(default=1)
# 1.查询库存数大于卖出数的书籍
from django.db.models import F, Q
res = models.Book.objects.filter(store__gt=F('sale')).values('title')
print(res)
# 2.将所有书籍的价格上涨1000块
models.Book.objects.update(price=F('price') + 1000)
# 3.将所有书籍名称加上爆款后缀
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.filter(pk=2).update(title=Concat(F('title'), Value('_爆款')))
Q查询:filter()等方法中逗号隔开的条件是AND关系。如果你需要进行更加复杂的查询(例如OR),你可以使用Q对象。
from django.db.models import Q, F
# 示例1: 查询卖出数大于100或者价格小于100块的
res = models.Book.objects.filter(Q(sale__gt=100) | Q(price__lt=100)).values('title')
print(res)
# 查询 库存数是100 并且 卖出数不是0 的产品
res = models.Book.objects.filter(Q(sale=100) & ~Q(store=0)).values('title')
print(res)
我们可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。
同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
Django ORM默认是惰性查询,当ORM语句在后续的代码中真正需要使用的时候才会执行。
如果你明确不需要这个数据库列(或在大部分情况里不需要),使用 defer() 和 only() 来避免加载它们。
obj = models.Book.objects.only('title', 'price')
for i in obj:
print(i.title)
print(i.price)
print(i.sale
only会查询括号内的字段并封装成一个对象返回,使用该对象不会在通过数据库查询。但是查询括号里没有的字段,还是会走数据库。
obj = models.Book.objects.defer('title', 'price')
for i in obj:
print(i.title)
print(i.price)
print(i.sale)
而defer和only相反。
select_related()
select_related(*fields)
返回一个 QuerySet,它将“跟随”外键关系,在执行查询时选择额外的相关对象数据。这是一个性能提升器,它导致一个更复杂的单一查询,但意味着以后使用外键关系将不需要数据库查询。
b = models.Book.objects.select_related('publish') # 会拼表
for i in b:
print(i.title)
print(i.price)
print(i.sale)
c = models.Auther.objects.select_related('auther_info')
print(c) # 只支持一对一和一对多关系
prefetch_related()
prefetch_related(*lookups)
返回一个 QuerySet,它将在一个批次中自动检索每个指定查询的相关对象。
这与 select_related 有类似的目的,二者都是为了阻止因访问相关对象而引起的数据库查询潮,但策略却完全不同。
select_related 的工作方式是创建一个 SQL 连接,并在 SELECT 语句中包含相关对象的字段。出于这个原因,select_related 在同一个数据库查询中得到相关对象。然而,为了避免因跨越“many”关系进行连接而产生更大的结果集,select_related 仅限于单值关系——外键和一对一。
prefetch_related 则对每个关系进行单独的查找,并在 Python 中进行“joining”。这使得它除了支持 select_related 的外键和一对一关系外,还可以预取多对多和多对一的对象,这是用 select_related 无法做到的。
Django 默认的事务行为:Django 默认的事务行为是自动提交。除非事务正在执行,每个查询将会马上自动提交到数据库。
Django 自动使用事务或还原点,以确保需多次查询的 ORM 操作的一致性,特别是 delete() 和 update() 操作。
AutoField 一个 IntegerField,根据可用的 ID 自动递增。你通常不需要直接使用它;如果你没有指定,主键字段会自动添加到你的模型中
BigAutoField 一个 64 位整数,与 AutoField 很相似,但保证适合 1 到 9223372036854775807 的数字。
BigIntegerField 一个 64 位的整数,和 IntegerField 很像,只是它保证适合从 -9223372036854775808 到 9223372036854775807 的数字。该字段的默认表单部件是一个 NumberInput。
BinaryField 一个用于存储原始二进制数据的字段。
BooleanField 一个 true/false 字段。
CharField 一个字符串字段,适用于小到大的字符串。
DateField 一个日期,在 Python 中用一个 datetime.date 实例表示。有一些额外的、可选的参数。
DateTimeField 一个日期和时间,在 Python 中用一个 datetime.datetime 实例表示。与 DateField 一样,使用相同的额外参数。
DecimalField 一个固定精度的十进制数,在 Python 中用一个 Decimal 实例来表示。它使用 DecimalValidator 验证输入。
DurationField 一个用于存储时间段的字段——在 Python 中用 timedelta 建模。当在 PostgreSQL 上使用时,使用的数据类型是 interval,在 Oracle 上使用的数据类型是 INTERVAL DAY(9) TO SECOND(6)。否则使用微秒的 bigint。
EmailField 一个 CharField,使用 EmailValidator 来检查该值是否为有效的电子邮件地址。
FileField 一个文件上传字段
FilePathField 一个 CharField,其选择仅限于文件系统中某个目录下的文件名。有一些特殊的参数,其中第一个参数是 必须的。
FloatField 在 Python 中用一个 float 实例表示的浮点数。
IntegerField 一个整数。从 -2147483648 到 2147483647 的值在 Django 支持的所有数据库中都是安全的。
JSONField 一个用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本地格式表示:字典、列表、字符串、数字、布尔值和 None。
PositiveBigIntegerField 就像一个 PositiveIntegerField,但只允许在某一特定点下的值(依赖于数据库)。0 到 9223372036854775807 的值在 Django 支持的所有数据库中都是安全的。
PositiveIntegerField 就像 IntegerField 一样,但必须是正值或零( 0 )。从 0 到 2147483647 的值在 Django 支持的所有数据库中都是安全的。出于向后兼容的原因,接受 0 的值。
PositiveSmallIntegerField 就像一个 PositiveIntegerField,但只允许在某一特定(数据库依赖的)点下取值。0 到 32767 的值在 Django 支持的所有数据库中都是安全的。
SlugField Slug 是一个报纸术语。slug 是一个简短的标签,只包含字母、数字、下划线或连字符。它们一般用于 URL 中。
SmallAutoField 就像一个 AutoField,但只允许值在一定(依赖于数据库)的限制下。1 到 32767 的值在 Django 支持的所有数据库中都是安全的。
SmallIntegerField 就像一个 IntegerField,但只允许在某一特定(依赖于数据库的)点下取值。从 -32768 到 32767 的值在 Django 支持的所有数据库中都是安全的
TextField 一个大的文本字段。该字段的默认表单部件是一个 Textarea。
TimeField 一个时间,在 Python 中用 datetime.time 实例表示。接受与 DateField 相同的自动填充选项。
URLField 该字段的默认表单部件是一个 URLInput。
UUIDField 一个用于存储通用唯一标识符的字段。使用 Python 的 UUID 类。当在 PostgreSQL 上使用时,它存储在一个 uuid 的数据类型中,否则存储在一个 char(32) 中。
以下参数对所以字段类型均有效,且是可选的。
class UserInfo(models.Model):
username = models.CharField(max_length=32)
gender_choice = (
(1, '男性'),
(2, '女性'),
)
gender = models.IntegerField(choices=gender_choice)
关系字段:
on_delete:
官方文档:https://docs.djangoproject.com/zh-hans/4.1/ref/models/fields/#module-django.db.models.fields
class Book(models.Model):
pass
class Author(models.Model):
pass
class Book2Author(models.Model):
book_id = models.ForeignKey(to="Book")
author_id = models.ForeognKey(to="Author")
class Book(models.Model):
authors = models.ForeignKey(to='Author', through='Book2Author', through_fields=('book_id', 'author_id'))
class Author(models.Model):
pass
class Book2Author(models.Model):
book_id = models.ForeignKey(to="Book")
author_id = models.ForeignKey(to="Author")