单表查询
QuerySet的查询方法
<1> all(): 查询所有结果 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 <3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。(源码就去搂一眼~诠释为何只能是一个对象) <4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 <5> order_by(*field): 对查询结果排序('-id')/('price') <6> reverse(): 对查询结果反向排序 >>>前面要先有排序才能反向 <7> count(): 返回数据库中匹配查询(QuerySet)的对象数量。 <8> first(): 返回第一条记录 <9> last(): 返回最后一条记录 <10> exists(): 如果QuerySet包含数据,就返回True,否则返回False
values 与 values_list Queryset的每个元素都变为字典或元组
distinct(): id不相同 所以无法排除 需要搭配values 与 values_list 使用
QuerySet的添加数据方法
user_obj =models.User.objects.create(name='tank',age=73,register_time='2019-2-14') user_obj = models.User(name='kevin',age=30,register_time='2019-1-1') user_obj.save()
QuerySet的修改数据方法
bookquery=models.Book.objects.all().first() 方式一:取出对象后修改属性,再save() bookquery.name="hahahah" bookquery.save() 方式二:把所有queryset对象.update(k=v)的形式统一修改 bookquery=models.Book.objects.all().update(name="egon")
QuerySet的删除数据方法
delete()对对象和清空queryset均适用 queryset models.User.objects.filter(name='egon').delete() 对象 user_obj.delete()
QuerySet中神奇双下划线
字段__gt 大于xx的 res = models.User.objects.filter(age__gt=44) 筛选 字段__lt 小于xx的 res = models.User.objects.filter(age__gt=44) 筛选 字段__gte大于等于XXX的 字段__lte小于等于XXX的 字段__in 字段值在某个列表里的 age__in=[44,22,73] 是442273的 age__range=[22,44] 在22到44范围 字段__(day或year或者mon) 比如 date__year=2019 筛选出date字段中年为多少的 字段__contains=“n” 字 符串里有n的 区分大小写 字段__icontains="N" 符串里有n或N的 不区分大小写 startswith='j' 以j开头的所有.. __endswith='n' 以n结尾的所有...
多表查询
Query对象与Queryset的跨表查询
原则:正向查字段 反向差表名小写_set 一对一只需要表名小写
正向查询
反向查询
基于Queryset双下划线查询
Query对象的增加
queryset与query的修改
queryset修改 models.Book.objects.all().update(publish_id=3) #所有图书图书馆都为3 models.Book.objects.filter(pk=1).update(publish=publish_obj)#传对象 会自动绑定 对象修改 book_obj = models.Book.objects.filter(pk=1).first() # book_obj.publish_id = 3 # 点表中真实存在的字段名 # book_obj.save() ublish_obj = models.Publish.objects.filter(pk=2).first() book_obj.publish = publish_obj # 点orm中字段名 传该字段对应的表的数据对象 book_obj.save()
book_obj.authors.set((1,)) #属性必须是多的
book_obj.authors.set((1,2,3)) #属性是一对多或多对多
删除某个绑定关系
Book_obj.author.remove( 1,2 ) 一个个 不能使用元祖或者列表
清空 clear() 全清
清空的是你当前这个表记录对应的绑定关系
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()
queryset与query的删除
级联删除
关联的对象删除 自己也删除
ManyToMany与ForeignKey的操
ForeignKey
增 models.Book.objects.create(publish=publish_obj) models.Book.objects.create(publish_id=1) 改 models.Book.objects.filter(pk=1).update(publish_id=1) models.Book.objects.filter(pk=1).update(publish=publish_obj) book_obj.publish_id = 3 # 点表中真实存在的字段名 book_obj.save() 删 删除外键对象,并删除自己
ManyToMany
增 add(*args) #也可以是传 *obj_list 改 set(tuble) set必须接收一个可迭代对象 删remove(*args)# 上面三个方法都可以支持传多个数字或对象 clear()清除所有绑定 外键中 删除对方对自己的绑定,对方允许null才行 多对多 清除所有的字段 第三张表少了这个记录
聚合查询 aggregate 分组查询 annotate
跨表查询不需要 表名_set
使用之前先导入:
rom django.db.models import Avg, Sum, Max, Min, Count
aggregate聚合
from django.db.models import Avg, Sum, Max, Min, Count res=models.Book.objects.all().aggregate(Avg("price")) print(res) #不指定别名返回字典{'price__avg': 13.233333} res=models.Book.objects.all().aggregate(price_avg=Avg("price")) print(res) #指定返回值13.233333
annotate分组
u_qset=Author.objects.all().annotate(age_avg=Avg("age")) print(u_qset) #QuerySet 分组之后没影响 u_obj=Author.objects.all().annotate(age_avg=Avg("book__price")) print(u_obj.values("age_avg")) #可以转为age_avg
Django ORM 常用字段和参数
1.AutoField
手动指定递增列 自动递增的int,primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列,有了这个就不会创建
2.IntegerField
整型-2147483648 to 2147483647
3.CharField
参数中必须有max_length
4.DateField
日期格式 YYYY-MM-DD
5.DateTimeField
YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
字段的参数
1.null
是否为空,values bool类型
2.unique
是否唯一,设置后,不能再重复
3.db_index
设置该字段为索引
4.default
默认值
以下是事件字段独有
5.auto_now_add
设置True后,插入数据会自动添加当前时间
6.auto_now
更新的事件
讲一下choise,用户只能多选一
在models.py中
class User(models.Model):
choices = ((1,'重点大学'),(2,'普通本科'),(3,'专科'),(4,'其他'))
education = models.IntegerField(choices=choices)
在对象查找属性中
对象.字段拿到的是值
print(u_obj.sex) #1
如果希望是实际意义上的内容
u_obj.get_字段_display() print(u_obj.get_sex_display()) #男
Django框架中的logging使用
BASE_LOG_DIR = os.path.join(BASE_DIR, "log") LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' '[%(levelname)s][%(message)s]' }, 'simple': { 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' }, 'collect': { 'format': '%(message)s' } }, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'filters': ['require_debug_true'], # 只有在Django debug为True时才在屏幕打印日志 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'SF': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,根据文件大小自动切 'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 3, # 备份数为3 xx.log --> xx.log.1 --> xx.log.2 --> xx.log.3 'formatter': 'standard', 'encoding': 'utf-8', }, 'TF': { 'level': 'INFO', 'class': 'logging.handlers.TimedRotatingFileHandler', # 保存到文件,根据时间自动切 'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件 'backupCount': 3, # 备份数为3 xx.log --> xx.log.2018-08-23_00-00-00 --> xx.log.2018-08-24_00-00-00 --> ... 'when': 'D', # 每天一切, 可选值有S/秒 M/分 H/小时 D/天 W0-W6/周(0=周一) midnight/如果没指定时间就默认在午夜 'formatter': 'standard', 'encoding': 'utf-8', }, 'error': { 'level': 'ERROR', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"), # 日志文件 'maxBytes': 1024 * 1024 * 5, # 日志大小 50M 'backupCount': 5, 'formatter': 'standard', 'encoding': 'utf-8', }, 'collect': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"), 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 5, 'formatter': 'collect', 'encoding': "utf-8" } }, 'loggers': { '': { # 默认的logger应用如下配置 'handlers': ['SF', 'console', 'error'], # 上线之后可以把'console'移除 'level': 'DEBUG', 'propagate': True, }, 'collect': { # 名为 'collect'的logger还单独处理 'handlers': ['console', 'collect'], 'level': 'INFO', } }, }
解决字段参数无法筛选-F与Q查询
F
适用之前from django.db.models import F
我们的过滤器只能筛选某个值,现在想字段值进行比较
筛选出卖出大于库存的字段 res=models.SP.object.filter(maichu__get=F("kucun")) 把自己的价格+10 res=models.SP.object.all().update(price=F("price")+50) 所有的商品名称后面添加“NB” #不能直接拼接 from django.db.models.functions import Concat from django.db.models import Value ret3=models.Product.objects.update(name=Concat(F('name'),Value('nb')))
Q
如果和普通的对比 Q必须放在前面
from django.models import Q 筛选出id大于3 或者年龄小于10 models.Book.object.all().filter(Q(id__gt=3)|Q(age__lt=10)) ~取反
名字不等于egon的
models.Book.object.all().filter(Q(id__gt=3)|~Q(name="egon")
如果和普通的对比 Q必须放在前面
models.Book.object.all().filter(Q(id__gt=3),name="egon"
如果字段名只有字符串怎么办?
q=Q()
q.connector="and"
q.children.append("字段名","字段值")
res=models.biao.object.filter(q)
事物
原子性操作和mysql一样
from django.db import transaction with transaction.atomic(): # 创建一条订单数据 # 能执行成功 #钱+100 #钱-100 可以防止执行到一半,后面代码无法执行造成的后果 要么都成功,要都失败
QuerySet有哪些方法
all filter exclude annotate distinct order_by 1.select_related方法 OneToOneField 和外键均可以适用 Django会获取相应外键对应的对象,使用该字段不会查找数据库 citys = models.city.objects.select_related('province').all() citys.province #不会走数据库 2.select_related() ManyToManyField和外键字段,可以使用prefetch_related()来进行优化。
牺牲效率优化内存-defer 和 only
我们查询出来的Queryset里的对象拥有全部属性,而有些属性我们不会经常使用
此时就可以用defer和only优化我们的“坦克”变身小刺客。等我们需要用到偏门的字段 再去数据库查询
节约了我们内存的使用,但是牺牲了效率
value #查出来的元素是一个字典 only #规定只有某个属性 only('id','name')#取的对象,只有id和name属性 defer('id','name')#相反,字段除了id和name都有 上述两个拿到了不需要的字段 才会去数据库查
自定义字段
用途不多说,直接干
class MyCharField(models.Field): # 数据库中Char类型需要指定长度,所以要传max_length def __init__(self,max_length,*args,**kwargs): self.max_length = max_length # 调用父类init,一定要写关键字传参,因为Field的init参数很多,可以看一下它的源码 super().__init__(max_length=max_length,*args,**kwargs) # 该方法也不能少 def db_type(self, connection): return 'char(%s)'%self.max_length
其他边边角角
建立第三张表的三种方式
1.ManyTOMany 全自动
1.好处 不需要自己关心第三张表
2.坏处 创表时无法自己手动关联其他字段
class Book(models.Model): name = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
2.手动创建第三张表关联两个外键 纯手动
1.好处 可以添加其他字段
2.坏处 跨表查询不够方便
class Book(models.Model): name = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32)
3.手动创建第三张表,仍使用ManyToMany
1.好处 可以添加其他字段 够方便
2.就是在使用的时候,不能用set、add、remove的方法,需要自己使用第三张表进行操作
伪代码 class 作者表(model.Modes): ...字段 modes.ManyToMany(to="书籍",through="第三张表",through_fields=("表3字段1",“表3字段2”)) class 书籍(models.Model): ....字段 class 第三张表 book = models.ForeignKey(to='Book') #id author = models.ForeignKey(to='Author') #id ....其他字段信息
在使用的时候,不能用set、add、remove的方法,需要自己跨到第三张表进行操作
models.User2Bumen.objects.create(user_id=1,bumen_id=2)
ContentType编码
网页是别人写的,django也是别人写的,我们在前后端数据交互的时候
要告诉服务器,我们的数据是哪些需要哪些编码格式,服务器才好区分 常见的有以下三种
urlencoded
对应数据格式键值对 name=123,后端会根据数据格式放在request.POST里
formdate
表单编码格式
比如普通键值对仍然在request.POST中,但是文件的话会丢到reque.Files
application/json
ajax发送json格式数据
jason的内容在request.body里 是二进制
Ajax
可以发起一个异步请求,服务器会给我们一个返回结果
我们可以拿到数据进行替换
我们来做一个在线上传头像,并替换Img的例子
需要实例化一个Datefrom 对象
processData 和 contentType 都变为False,不处理Date和使用默认编码
$("#hread").on("click",function () {
let touxiang_obj=$("img");
let touxiang_img=$("#touxiang");
let formdata =new FormData(); /* 实例化一个form表单 */
{#formdata.datamyfile=touxiang_img[0].files[0]; /* 为表单添加 file和文件内容,并且jquery不能使用files所有取索引0 添加获取files,取第一个 */#}
formdata.append("myfile",touxiang_img[0].files[0]);
{#console.log(formdata);#}
{#console.log(touxiang_img[0].files[0]);#}
$.ajax(
{# ajax事件 #}
{
url:"/test/",
type:"post",
data:formdata,
processData: false,
contentType:false,
success:function (data) {
leturl="/".concat(data);
touxiang_obj[1].src=leturl;
console.log(touxiang_obj[1]);
{#var res=encodeURI(data);#}
}
})
})
ajax发送json
需要指定改为contentType:pplication/json
数据需要转为JSON.stringify
$('#d1').click(function () {
$.ajax({
url:'', // url参数可以不写,默认就是当前页面打开的地址
type:'post',
contentType:'application/json',
data:JSON.stringify({'name':'jason','hobby':'study'}),
success:function (data) {
{#alert(data)#}
{#$('#i3').val(data)#}
}
})
});
form表单与ajax异同点
1.form表单不支持异步提交局部刷新
2.form表单不支持传输json格式数据
3.form表单与ajax默认传输数据的编码格式都是urlencoded
批量提交
服务器每次需要操作大量数据,操作完才能渲染页面,过程太久
我们先让把实例化对象存在容器里,通过bulk_create(容器)创建
l=[]
for i in range(10000):
l.append(models.Book(name="书{}".format(i)))
models.Book.objects.bulk_create(l)
分页
class Pagination(object): def __init__(self, current_page, all_count, per_page_num=2, pager_count=11): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param pager_count: 最多显示的页码个数 用法: queryset = model.objects.all() page_obj = Pagination(current_page,all_count) page_data = queryset[page_obj.start:page_obj.end] 获取数据用page_data而不再使用原始的queryset 获取前端分页样式用page_obj.page_html """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count self.pager_count_half = int((pager_count - 1) / 2) @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示11/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_end = self.all_pager + 1 pager_start = self.all_pager - self.pager_count + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] # 添加前面的nav和ul标签 page_html_list.append(''' <nav aria-label='Page navigation>' <ul class='pagination'> ''') first_page = '<li><a href="?page=%s">首页</a></li>' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' else: prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) for i in range(pager_start, pager_end): if i == self.current_page: temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) else: temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled"><a href="#">下一页</a></li>' else: next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) page_html_list.append(last_page) # 尾部添加标签 page_html_list.append(''' </nav> </ul> ''') return ''.join(page_html_list)
使用
page_obj = my_page.Pagination(current_page=current_page,all_count=all_count) # 对总数据进行切片
page_queryset = book_list[page_obj.start:page_obj.end]
前端
{{ page_obj.page_html|safe }}
聚合合并
from django.db.models import Aggregate, CharField class Concat(Aggregate): """ORM用来分组显示其他字段 相当于group_concat""" function = 'GROUP_CONCAT' template = '%(function)s(%(distinct)s%(expressions)s)' def __init__(self, expression, distinct=False, **extra): super(Concat, self).__init__( expression, distinct='DISTINCT ' if distinct else '', output_field=CharField(), **extra) #使用 WhiteList.objects.values('ip').annotate(id=Concat('id'))
from django.db.models import Aggregate, CharField
class Concat(Aggregate):
"""ORM用来分组显示其他字段 相当于group_concat"""
function = 'GROUP_CONCAT'
template = '%(function)s(%(distinct)s%(expressions)s)'
def __init__(self, expression, distinct=False, **extra):
super(Concat, self).__init__(
expression,
distinct='DISTINCT ' if distinct else '',
output_field=CharField(),
**extra)