ORM相关操作(未完善)

单表查询

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
QuerySet的查询方法

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()
View Code

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")
View Code

QuerySet的删除数据方法

delete()对对象和清空queryset均适用

queryset
models.User.objects.filter(name='egon').delete()

对象
user_obj.delete()
View Code

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结尾的所有...
View Code

  

多表查询

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()
queryset与query的修改

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才行
多对多 清除所有的字段 第三张表少了这个记录
View Code

  


聚合查询  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]
ORM常用字段

字段的参数

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',
        }
    },
}
LOGING

解决字段参数无法筛选-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

可以防止执行到一半,后面代码无法执行造成的后果
要么都成功,要都失败
View Code

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()来进行优化。
View Code

牺牲效率优化内存-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)#}
           }
       })
    });
提交jason

 

 

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)
View Code

分页

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)
原文地址:https://www.cnblogs.com/xzqpy/p/10993753.html