1、只要是queryset对象就可以无限制调用Queryset对象的方法:
res = models.User.objects.filter().filter().update()....
2、只要是queryset对象就可以通过下面的语句获得sql语句:
print(res.query)
3、如果不是queryset对象,也可以获得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',
},
}
}
当测试Django的时候,我们往往只需要测试部分功能,启动整个Django显得太慢,太大,因此我们可以书写测试脚本:
步骤如下:
1、在任意目录下创建一个py文件进行书写,或者直接在app下的tests.py
文件中书写以下代码:
# 以下代码可以去manage.py文件中复制
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings")
import django
django.setup()
2、示范,例如我想对模板层进行测试,代码如下:
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings")
import django
django.setup()
# 导入models
from app01 import models
# 调用models相关功能
models.Movie.objects.all()
在对models进行测试的时候,我们需要配置数据库的相关参数(settings.py)以及在__init__.py
中导入pymysql,在models.py文件中创建我们的模型表,步骤如下:
1、在models.py中创建模型表
class Movie(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
# auto_now_add=True 当对数据进行创建的时候会自动添加时间
publish_time = models.DateField(auto_now_add=True)
2、执行迁移数据库命令
python manage.py makemigrations
python manage.py migrate
from django.test import TestCase
# Create your tests here.
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mystile.settings")
import django
django.setup()
from test_func import models
from datetime import date
ctime = date.today()
# 还可以直接传入日期时间对象
# 1、create() 创建数据,得到的返回值就是创建数据的对象本身,可以直接使用res
res = models.Movie.objects.create(title='山楂树之恋', price=999, publish_time=ctime)
# 2、all() 查询表中的所数据,返回的是queryset对象, 类似列表套对象
res = models.Movie.objects.all()
print(res)
# 3、filter() 按照指定条件去查询,返回的是queryset对象,类似列表套对象
res = models.Movie.objects.filter(id=1)
print(res)
# 只要是queryset对象,就可以通过 点query 的方式查看sql语句
res = models.Movie.objects.filter(title='山楂树之恋')
print(res.query)
# 4、get() 返回的是对象本身,不推荐使用,因为当查询条件不存在的时候会报错
res = models.Movie.objects.get(pk=1)
print(res)
# 5、values() 获取指定字段对应的数据,列表套字典 QuerySet对象
res = models.Movie.objects.values('title', 'price', 'publish_time')
print(res)
# 6、values_list() 获取指定字段对应的数据,元组套字典 QuerySet对象
res = models.Movie.objects.values_list('title', 'price')
print(res)
# 7、first() 获取第一个元素对象,返回的是数据对象
res = models.Movie.objects.filter().first()
print(res)
# 8、last() 获取最后一个元素对象,返回的是数据对象
res = models.Movie.objects.filter().last()
print(res.title)
# 9、update() 更新数据 返回值是受影响的行数
res = models.Movie.objects.filter(pk=2).update(title='大鱼海棠')
print(res)
# 10、delete() 删除数据 返回值(1, {'app01.Movie': 1}) 受影响的表及行数
res = models.Movie.objects.filter(pk=2).delete()
print(res)
# 11、count() 统计数据条数
res = models.Movie.objects.count()
print(res)
res = models.Movie.objects.filter(pk=2).count()
print(res)
# 12、order_by() 按照指定字段排序
res = models.Movie.objects.order_by('price') # 默认升序
res1 = models.Movie.objects.order_by('-price') # 降序
print(res)
print(res1)
# 13、exclude() 排除什么什么之外的数据
res = models.Movie.objects.exclude(pk=1).first()
print(res.title)
# 14、exists() 判断查询是够有数据,返回bool值
res = models.Movie.objects.filter(pk=10000).exists()
print(res)
# 15、reverse() 反转
res = models.Movie.objects.order_by('price').reverse()
print(res)
# 16、distinct() 去重:去重的前提,必须是由完全一样的数据才能去重
# 因此,这里要注意将django自动创建的主键排除在外
res = models.Movie.objects.values('title', 'price').distinct()
print(res)
模糊查询:
年月日:
双下划线查询,一般用于括号内,对数据进一步进行限制,比如求商品的价格大于200的...
# 双下划线查询
# 1、查询价格大于200的电影
res = models.Movie.objects.filter(price__gt=200)
print(res)
# 2、查询价格小于500的电影
res = models.Movie.objects.filter(price__lt=500)
print(res)
# 3、查询价格大于等于200的电影
res = models.Movie.objects.filter(price__gte=200)
print(res)
# 4、查询价格小于等于400的电影
res = models.Movie.objects.filter(price__lte=500)
print(res)
# 5、查询价格在200到500之间的电影, 包括200到500
res = models.Movie.objects.filter(price__in=(200, 500))
# 6、查询价格是100,或66,或521
res = models.Movie.objects.filter(price__in=[100, 66, 521])
# 7、查询电影名字中包含字符“山楂树”的电影
'''
模糊查询:
关键字:like
关键符:%和-
'''
res = models.Movie.objects.filter(title__contains='山楂树') # 区分大小写
res = models.Movie.objects.filter(title__icontains='山楂树') # 多加i可以忽略大小写
print(res)
# 8、查询2020年出版的电影
res = models.Movie.objects.filter(publish_time__year=2020)
print(res)
# 9、查询是1月份出版的电影
res = models.Movie.objects.filter(publish_time__month=1)
print(res)
针对多表查询,我们需要添加更多的表,步骤如下:
1、models.py中创建模型表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
# 该字段新增数据自动添加 无需考虑
publish_time = models.DateField(auto_now_add=True)
# 出版社 一对多(一个出版社可以出版多个书,一本书不可以被多个出版社出版) 外键字段建在多的一方
publish = models.ForeignKey(to='Publish')
# 作者 多对多关系
# 一本书可以有多个作者,一个作者也可以写多本书
authors = models.ManyToManyField(to='Author')
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=255)
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# 作者详情 一对一关系
author_detail = models.OneToOneField(to='AuthorDetail')
class AuthorDetail(models.Model):
phone = models.BigIntegerField()
addr = models.CharField(max_length=255)
2、执行数据库迁移命令
python manage.py makemigrations
python manage.py migrate
# 1、增 直接写实际的表字段 publish_id
models.Book.objects.create(title='三国演义', price=123.23, publish_id=2)
# 2、增 先查出来要绑定的出版社对象
publish_obj = models.Publish.objects.get(pk=1)
# 将查出来的对象赋值给publish
models.Book.objects.create(title='水浒传', price=66.6, publish=publish_obj)
# 查
# 在下面的多表查询中
# 改
# 第一种方法
models.Book.objects.filter(pk=2).update(publish_id=3)
# 第二种方法,依旧是拿对象赋值
publish_obj = models.Publish.objects.get(pk=3)
models.Book.objects.filter(pk=2).update(publish=publish_obj)
# 删
'''
外键字段在1.X版本中默认就是级联更新级联删除的
2.X版本中,则需要你自己手动指定
百度一大堆
当没有级联删除和级联更新的情况下,只有删除关联表中的数据,才能删除和修改被关联表中的数据
当有级联删除和级联更新的情况下,修改和删除被关联表中的数据, 关联表中的数据也会跟着被删除和修改
'''
add 专门给多对多种的第三张虚拟表添加数据,括号内可以传数字,也可以穿对象,并且都支持传多个。
# 多对多
# 1、给书籍绑定作者关系 add
book_obj = models.Book.objects.filter(pk=2).first()
# 书籍和作者的关系是由第三张表决定,那就意味着需要操作第三张表
print(book_obj.authors) # 书籍对象.虚拟字段authors就类似已经跨到了书籍和作者的第三张表
book_obj.authors.add(1) # 给书籍绑定一个主键为1的作者
book_obj.authors.add(2, 3)
# 第二种方法,直接添加对象
book_obj = models.Book.objects.filter(pk=3).first()
author_obj = models.Author.objects.get(pk=1)
author_obj1 = models.Author.objects.get(pk=3)
book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj, author_obj1)
专门给第三张关系表移除数据,括号内既可以传数字也可以传对象,
并且都支持传多个。
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(1)
book_obj.authors.remove(2, 3)
author_obj = models.Author.objects.get(pk=1)
author_obj1 = models.Author.objects.get(pk=3)
book_obj.authors.remove(author_obj)
book_obj.authors.remove(author_obj, author_obj1)
修改书籍与作者的关系
括号内支持传数字和对象,但是需要是可迭代对象
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set((3,))
book_obj.authors.set((1, 2))
authors_obj = models.Author.objects.get(pk=1)
authors_obj1 = models.Author.objects.get(pk=2)
book_obj.authors.set((authors_obj,))
book_obj.authors.set([authors_obj, authors_obj1])
清空关系
不需要任何参数
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear() # 去第三张表中清空书籍为1的所有书籍
1、子查询
将一张表的查询结果当做另一张表的查询条件
2、链表查询
建议:在写sql语句或者orm语句的时候不要想着一次性将语句写完,一定要写一点查一点,再写一点
正向:
跨表查询的时候 外键字段是否在当前数据对象中 如果在查询另外一张关系表 叫正向
反向:
如果不在叫反向
口诀:
正向查询按外键字段
反向查询按表名小写
正向查询的时候,当外键字段对应的数据可以有多个的时候需要加.all()
否则点外键字典即可获取到对应的数据对象
# 1、查询书籍pk为1的出版社名称
book_obj = models.Book.objects.filter(pk=2).first()
print(book_obj.publish)
print(book_obj.publish.name)
print(book_obj.publish.addr)
# 2、查询书籍pk为2的所有作者的姓名
book_obj = models.Book.objects.filter(pk=3).first()
print(book_obj.authors) # test_func.Author.None
print(book_obj.authors.all())
author_list = book_obj.authors.all()
for author_obj in author_list:
print(author_obj.name)
# 3、查询作者pk为1的电话号码
author_obj = models.Author.objects.filter(pk=1).first()
print(author_obj.author_detail)
print(author_obj.author_detail.phone)
print(author_obj.author_detail.addr)
基于对象的反向查询 表名小写是需要加_set.all()
# 4、查询出版社名称为东方出版社出版过的书籍
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set) # test_func.Book.None
print(publish_obj.book_set.all())
# 5、查询作者为engon写过的书
author_obj = models.Author.objects.filter(name='engon').first()
print(author_obj.book_set) # app01.Book.None
print(author_obj.book_set.all())
# 6、查询手机号为120的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=120).first()
print(author_detail_obj.author)
print(author_detail_obj.author.name)
print(author_detail_obj.author.age)
只要表之间有关系,你就可以通过正向的外键字段或者反向的表名小写,连续跨表查询
正向查询:先获取有外键字段的对象,通过对象的values内,使用外键字段__
到另外一张表中取值
反向查询:先获取另一张表的对象,在filter括号内,通过表名小写__到另外一张表中,得到另外一张表中的字段加限制条件,在.value的括号内,可以直接取本对象的value,以及通过双下划线__
取到另外一张表的value。
# 1、查询书籍pk为2的出版社名称
# 正向
# 写外键字段,就意味着你已经在外键字段管理的那张表中
res = models.Book.objects.filter(pk=2).values('publish__name')
print(res)
# 反向
# 拿出版过pk为1的书籍对应的出版社
res = models.Publish.objects.filter(book__pk=2)
res = models.Publish.objects.filter(book__pk=2).values('name')
print(res)
# 2、查询书籍pk为2的作者姓名和年龄
# 正向
res = models.Book.objects.filter(pk=3).values('title', 'authors__name', 'authors__age')
print(res)
# 反向
res = models.Author.objects.filter(book__pk=2) # 拿出出版过书籍pk为1的作者
res = models.Author.objects.filter(book__pk=2).values('name', 'age', 'book__title')
print(res)
# 3、查询作者是engon的年龄和手机号
# 正向
res = models.Author.objects.filter(name='engon').values('age', 'author_detail__phone')
# 反向
res = models.AuthorDetail.objects.filter(author__name='engon').values('author__age', 'phone')
print(res)
# 4、查询书籍pk为的2的作者的手机
# 正向
res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone')
print(res)
# 反向
res = models.AuthorDetail.objects.filter(author__book__pk=2).values('phone')
print(res)
关键字:
aggregate
模块的导入:
from django.db.models import Max, Min, Avg, Count, Sum
范例:
from django.db.models import Max, Min, Avg, Count, Sum
# 查询所有书的平均价格
res = models.Book.objects.aggregate(avg_num=Avg('price'))
print(res)
# 查询价格最贵的书籍
res = models.Book.objects.aggregate(max_price=Max('price'))
print(res)
# 全部使用一遍
res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'), Count('pk'), Sum('price'))
print(res)
关键字:
annotate
模块导入:
from django.db.models import Max,Min,Sum,Count,Avg
# 1、统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
print(res)
# 2、统计出每一个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'book__title', 'book__price')
print(res)
# 3、统计不止一个作者的书
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title')
print(res)
# 4、查询各个作者出的书籍的总价格
res = models.Author.objects.annotate(author_num=Sum('book__price')).values('name', 'author_num')
print(res)
# 5、如何按照表中的某一指定字段分组
res = models.Book.objects.values('price').annotate() # 以价格字段进行分组
print(res)
需要导入模块
from django.db.models import F, Q
可以获取到表中的某个字段对应的值
1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(kucun__gt=F('maichu'))
print(res)
2.将所有书的价格提高100
res = models.Book.objects.update(price=F('price') + 100)
能够改变查询的条件:
Q能够改变查询的条件关系 and or not
1.查询书的名字是python入门或者价格是1000的书籍
res = models.Book.objects.filter(title='python入门',price=1000) # and关系
res = models.Book.objects.filter(Q(title='python入门'),Q(price=1000)) # 逗号是and关系
res = models.Book.objects.filter(Q(title='python入门')|Q(price=1000)) # |是or关系
res = models.Book.objects.filter(~Q(title='python入门')|Q(price=1000)) # ~是not关系
Q的高阶用法,可以以用户输入的字符串变成可以查询的限制条件,因此多用于搜索框功能。
# res = models.Book.objects.filter('title'='python入门')
q = Q()
q.connector = 'or' # q对象默认也是and关系 可以通过connector改变or
q.children.append(('title','python入门'))
q.children.append(('price',1000))
res = models.Book.objects.filter(q)
print(res)
AutoField(primary_key=True) 主键字段
CharField(max_length=32) varchar(32)
IntegerField() int
BigIntergerField() bigint
DecimalField() decimal
EmailField() varchart(254)
DateField() date
DateTimeField() datetime
auto_now:每次编辑数据的时候都会自动更新该字段时间
auto_now_add:创建数据的时候自动更新
BooleanField(Field)
给该字段传布尔值 会对应成 数字0/1
is_delete
is_status
is_vip
TextField(Field)
- 文本类型
存储大段文本
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录,只存文件路径
upload_to = '指定文件路径'
给该字段传文件对象 文件会自动保存到upload_to指定的文件夹下 然后该字段存文件的路径
django默认建的字符串类型都是varchar,想要建立char字段,需要自定义char类型的字段,方法如下:
# modles.py
from django.db.models import Field
class RealCharField(Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length # 拦截一个父类的方法 操作完之后 利用super调用父类的方法
super().__init__(max_length=max_length,*args,**kwargs)
def db_type(self, connection):
return 'char(%s)'%self.max_length
# 使用自定义的char类型字段
class Movie(models.Model):
textField = RealCharField(max_length=64)
null
null 用于表示某个字段可以为空
default
default 设置该字段的默认值
unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index
如果db_index=True 则代表着为此字段设置索引。
django 1.x默认就是级联更新级联删除 django2.x需要你自己手动指定
on_delete=models.CASCADE
db_contraints = True
#级联删除情况
class UserToken(models): #级联删除,用户删除,它也删除
user = models.OneToOneField(to='User', on_delete=models.CASCADE, to_field='id')
token = models.CharField(max_length=128, null=True)
#置空情况
class Server(models.Model):
server_type_choice = (
(1, "WEB"),
(2, "存储"),
(3, "缓存")
)
server_type = models.IntegerField(choices=server_type_choice)
hostname = models.CharField(max_length=32)
port = models.IntegerField()
business_unit = models.ForeignKey("BusinessUnit", on_delete= models.SET_NULL, null=True)
#设置默认值
user = models.ForeignKey("UserInfo", on_delete= models.SET_DEFAULT, default=0)
当你数据能够被你列举完全,可以考虑使用choices参数,用数字来代替字符串存入数据库,可以节省硬盘空间。
class Userinfo(models.Model):
username = models.CharField(max_length=32)
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他'),
)
gender = models.IntegerField(choices=gender_choices)
# 该字段还是存数字 并且可以匹配关系之外的数字
record_choices = (('checked', "已签到"),
('vacate', "请假"),
('late', "迟到"),
('noshow', "缺勤"),
('leave_early', "早退"),
)
record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
user_obj = models.Userinfo.objects.get(pk=1)
print(user_obj.username)
print(user_obj.gender)
# 针对choices参数字段 取值的时候 get_xxx_display()
print(user_obj.get_gender_display())
# 针对没有注释信息的数据 get_xxx_display()获取到的还是数字本身
user_obj = models.Userinfo.objects.get(pk=4)
print(user_obj.gender)
print(user_obj.get_gender_display())
django orm的所有查询都是惰性查询,也就意味着,不使用查询出来的结果,sql语句并不会执行,例如:
res = models.Book.objects.all()
当我不引用和使用res的时候,这条语句是不会执行。
only的作用:
括号内传字段,得到的结果是一个列表套数据对象,该内只含有括号内指定的字段属性
对象点该字段属性是不会走数据库的,但是你一旦点了非括号内的字段,也能够拿到数据,但是会走数据库查询
only将括号内的字段查询结果赋值给对象,对象点括号内的字段是不会走数据库查询的,超出括号内的字段,此时查询会走数据库。
res = models.Book.objects.only('title') # 這些對象内部只有title屬性
# print(res)
for r in res:
# print(r.title)
print(r.price)
defer的作用:
括号内传字段,得到的结果是一个列表套数据对象,对象点查询defer括号内的字段属性,该字段属性会重复的走数据库,但是查询defer括号内没有的字段,就不会走数据库。
res = models.Book.objects.defer('title')
for r in res:
print(r.title)
selected_related:
内部是连表操作,先将关系表全部连接起来,之后再一次性查询出来,封装到对象中,数据对象之后在获取任意表中数据的时候都不需要再走数据库了,因为全部封装成了对象的属性。
select_related括号内只能传外键字段,并且不能是多对多字段,只能是一对多和一对一
select_related(外键字段1__外键字段2__外键字段3.....)
res = models.Book.objects.select_related('authors')
for r in res:
print(r.publish.name)
print(r.publish.addr)
prefetch_related
看起来像是连表操作,但内部是子查询,内部通过子查询将外键关联表中的数据全部封装到对象中,之后点当前对象表或外键关联表中的字段也都不需要走数据库了。
prefetch_related
res = models.Book.objects.prefetch_related('publish')
# print(res)
for r in res:
print(r.publish.name)
优缺点比较:
select_related连表操作,好处在于只走一次sql查询,但缺点是耗时耗在 连接表的操作上。
prefetch_related子查询,走两次sql查询,耗时耗在查询次数上。
事务的四大特性(ACID):
原子性:一个事务是一个不可分割的工作单位,事务中的所有操作都执行成功,整个事务才算成功;只要有任何一个sql语句执行失败,那么就会回退到执行事务前的状态。
一致性:事务前后数据的完整性必须保持一致,例如银行转账,甲方转给乙方钱,不管事务成功还是失败,应该保证事务结束后,两者的存款总额为2000元。
隔离性:指的是在并发环境中,当不同的事务同时操作同一个数据时,每个事务都有各自的完整数据空间
持久性:指的是只要事务成功结束,它对数据库中数据的改变就是永久性的。
# django orm开启事务操作
from django.db import transaction
with transaction.atomic():
# 在with代码块中执行的orm语句同属于一个事务
pass
# 代码块运行结束 事务就结束了 事务相关的其他配置 你可以百度搜搜看
数据库的三大设计范式:
第一范式:要求属性具有原子性,不可以再分解;
不符合第一范式示例:
第二范式:2NF是对记录的唯一性,要求记录有唯一标识,即实体的唯一性,即不存在部分依赖。
表:学号、课程号、姓名、学分;
这个表明显说明了两个事务:学生信息, 课程信息;由于非主键字段必须依赖主键,这里学分依赖课程号,姓名依赖与学号,所以不符合二范式。
可能会存在问题:
数据冗余:
,每条记录都含有相同信息;删除异常:
删除所有学生成绩,就把课程信息全删除了;插入异常:
学生未选课,无法记录进数据库;更新异常:
调整课程学分,所有行都调整。正确做法:
学生:Student
(学号, 姓名);
课程:Course
(课程号, 学分);
选课关系:StudentCourse
(学号, 课程号, 成绩)。
第三范式:3NF是对字段的冗余性
,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖;
表: 学号, 姓名, 年龄, 学院名称, 学院电话
因为存在依赖传递: (学号) → (学生)→(所在学院) → (学院电话) 。
可能会存在问题:
数据冗余:
有重复值;更新异常:
有重复的冗余信息,修改时需要同时修改多条记录,否则会出现数据不一致的情况 。正确做法:
学生:(学号, 姓名, 年龄, 所在学院);
学院:(学院, 电话)。
范式化设计和反范式化设计的优缺点:
范式化
优点:
缺点:
反范式化
优点:
缺点:
# django orm开启事务操作
from django.db import transaction
with transaction.atomic():
# 在with代码块中执行的orm语句同属于一个事务
pass
# 代码块运行结束 事务就结束了 事务相关的其他配置 你可以百度搜搜看
MTV django号称是MTV框架
M:models
T:templates
V:views
MVC
M:models
V:views
C:contronnar 控制器(路由分发 urls.py)
本质:MTV本质也是MVC
原文:https://www.cnblogs.com/cnhyk/p/12173537.html