码迷,mamicode.com
首页 > 其他好文 > 详细

ORM之基础操作进阶

时间:2018-11-10 16:45:56      阅读:185      评论:0      收藏:0      [点我收藏+]

标签:清空   定时   res   email   基础   handle   userinfo   values   har   

一、外键自关联(一对多)
1、建表
# 评论表
class Comment(models.Model):
    id = models.AutoField(primary_key=True)
    content = models.CharField(max_length=255)
    push_time = models.DateTimeField(auto_now_add=True)
    # 父评论:自关联,一个评论可以没有父评论所以null=True
    pcomment = models.ForeignKey(to=self, null=True)

    def __str__(self):
        return self.content

2、表数据
技术分享图片
2、操作 1.找到id等于2的那个评论的所有子评论(跨表查询) ret = Comment.objects.filter(pcomment__id=2) print(ret) 2.方法2(直接查询) ret = Comment.objects.filter(pcomment_id=2) print(ret) 3.方法3 ret = Comment.objects.filter(id=2).values(comment__id) print(ret) 二、多对多自关联 1、建表 class Person(models.Model): name = models.CharField(max_length=12) friends = models.ManyToManyField(to=self, symmetrical=False) def __str__(self): return self.name 2、注意 在多对多的自关联时,如果需要反向查找,则需要添加symmetrical这个字段, 指定内部是否创建反向操作的字段,默认为True,需要反向查找则改成False 3、操作 1.找到小明的朋友(正向找) ret = Person.objects.filter(name=小明).values(friends__name) print(ret) 2.朋友是小明的那个人(反向找) ret = Person.objects.get(name=小明).person_set.all() print(ret) 或者 ret = Person.objects.filter(name=小明).values(person__name) print(ret) 三、补充的几个方法 1、这几个方法适用于一对多和多对多的情况, 且只是对象才能使用,就是obj.get()、obj.first()、obj.last()才能使用 它存在于下面两种情况: 外键关系的反向查询 多对多关联关系 简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法 2、方法 1.create(关键字参数) 创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。 2.add(位置参数) 把指定的model对象添加到关联对象集中。 但是一对多中add里面的实参(位置参数)是对象 而多对多中add里面的实参(位置参数)可以是id 3.set([列表]) 更新model对象的关联对象。 但是一对多中set里面的实参(列表)是对象 而多对多中add里面的实参(列表)可以是id 4.remove() 从关联对象集中移除执行的model对象 5.clear() 从关联对象集中移除一切对象。 注意: 对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。 举个例子: ForeignKey字段没设置null=True时, class Book(models.Model): title = models.CharField(max_length=32) publisher = models.ForeignKey(to=Publisher) 没有clear()和remove()方法 >>> models.Publisher.objects.first().book_set.clear() Traceback (most recent call last): File "<input>", line 1, in <module> AttributeError: RelatedManager object has no attribute clear 当ForeignKey字段设置null=True时, class Book(models.Model): name = models.CharField(max_length=32) publisher = models.ForeignKey(to=Class, null=True) 此时就有clear()和remove()方法 >>> models.Publisher.objects.first().book_set.clear() 6.注意 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。 7.建表 class MyClass(models.Model): cname = models.CharField(max_length=12) class Student(models.Model): sname = models.CharField(max_length=12) myclass = models.ForeignKey(to=MyClass) def __str__(self): return self.sname class Teacher(models.Model): tname = models.CharField(max_length=12) myclass = models.ManyToManyField(to=MyClass) 8.例子 1.一对多 # 查询id=2的班级的所有学生 ret = MyClass.objects.get(id=2).student_set.all() print(ret) # id=2的班级的所有老师 ret = MyClass.objects.get(id=2).teacher_set.all() print(ret) # 给id=1的班级添加一个id=4的学生 student_obj = Student.objects.get(id=4) MyClass.objects.get(id=1).student_set.add(student_obj) ret = MyClass.objects.get(id=1).student_set.all() print(ret) # 把所有学生都绑定到id=1的班级中 MyClass.objects.get(id=1).student_set.set(Student.objects.all()) ret = MyClass.objects.get(id=1).student_set.all() print(ret) 2.多对多 # 给id=2的老师添加一个id=4的班级 Teacher.objects.get(id=2).myclass.add(4) # 从id=2的老师的中移除2班 Teacher.objects.get(id=2).myclass.remove(2) # 清空id=2的老师的班级 Teacher.objects.get(id=2).myclass.clear() # 给id=2的老师新增一门课:生理课 Teacher.objects.get(id=2).myclass.create(cname=生理课) 四、如何在Django终端打印SQL语句 在Django项目的settings.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, }, } } 即为你的Django项目配置上一个名为django.db.backends的logger实例即可查看翻译后的SQL语句。 五、基于对象的跟新(save)和基于QuerySet的update跟新的区别 # 把南山出版社的ceo改成小红 # 1. 基于对象的修改(会跟新所有字段,效率慢) publisher_obj = Publisher.objects.get(name=南山出版社) publisher_obj.ceo = 小红 publisher_obj.save() # 2. 基于QuerySet的update跟新(只跟新指定的字段,效率快于基于对象的修改) Publisher.objects.filter(name=南山出版社).update(ceo=小勾) 六、聚合查询 1、介绍 aggregate()是QuerySet的一个终止子句,意思是说,它返回一个包含一些键值对的字典。 键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。 用到的内置函数: from django.db.models import Avg, Sum, Max, Min, Count 2、例子 from django.db.models import Avg, Sum, Max, Min, Count # 求所有书中价格最高的书 ret = Book.objects.all().aggregate(Max(price)) print(ret) # 为聚合值指定一个名称,返回指定的key值 ret = Book.objects.all().aggregate(max_price=Max(price)) print(ret) # 生成不止一个聚合,求所有书的平均价格和最小的价格 ret = Book.objects.all().aggregate(avg_price=Avg(price), min_price=Min(price)) print(ret) 七、分组查询 1、介绍 values/values_list 对应 SQL语句 select 部分 filter 对应 SQL语句 where 部分 2、ORM中分组使用annotate 1. annotate前面查询(values)的是什么,就按什么分组,没有values就默认按id分组,而id是唯一的,因此不写values等于没有分组 2. annotate中要写上分组之后要做的事情 3、建表 class Employee(models.Model): name = models.CharField(max_length=12) age = models.IntegerField() salary = models.IntegerField() province = models.CharField(max_length=12) dept = models.CharField(max_length=12) 4、例子 from django.db.models import Avg, Sum, Max, Min, Count # 使用ORM查询每个部门的平均工资 ret = Employee.objects.values(dept).annotate(avg=Avg(salary)) print(ret) ret = Employee.objects.annotate(avg=Avg(salary)).values(dept, avg) print(ret) # 等于没有分组 # 每个部门的平均年龄 ret = Employee.objects.values(dept).annotate(Avg(age)).values_list(dept, avg_age) print(ret) 4、多表使用分组 class Publisher(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) addr = models.TextField() # 成立日期:对应Python中的datetime.date类型 date = models.DateField() def __str__(self): return self.name class Book(models.Model): # 书名 title = models.CharField(max_length=16) # 价格:最多显示6个数字,小数位有2个 price = models.DecimalField(max_digits=6, decimal_places=2) # ISBN:书籍的唯一编号 isbn = models.CharField(max_length=20, unique=True) # 外键关联出版社,db_constraint=False不在数据库中建立约束 publisher = models.ForeignKey(to=Publisher, on_delete=models.CASCADE, related_name=books) def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=12) # 性别,choice选项用的是本模块定义的常量,默认选保密 gender = models.SmallIntegerField(choices=((1, ), (2, ), (3, 保密)), default=3) # 手机号,唯一约束 phone = models.CharField(max_length=11, unique=True) # 邮箱 email = models.EmailField() # 多对多关联书籍 books = models.ManyToManyField(to=Book, related_name=authors) 例子: from django.db.models import Avg, Sum, Max, Min, Count # 求每个出版社出版的书的平均价格 ret = Book.objects.values(publisher_id).annotate(avg_price=Avg(price)).values_list(publisher__name, avg_price) print(ret) # 求每个出版社出版的书的数量 ret = Book.objects.values(publisher_id).annotate(count=Count(id)).values_list(publisher__name, count) print(ret) 八、Q查询 filter()等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。 同样是上面书籍出版社那个数据库 例如: 1、或 | from django.db.models import Q # 查询书的价格大于100或者作者是小明的书 ret = Book.objects.filter(Q(price__gt=100) | Q(authors__name=小明)) print(ret) 2、且 & # 原生的 且 # 查询书的价格大于100且作者是小明的书 ret = Book.objects.filter(price__gt=100, authors__name=小明) print(ret) # Q对象的 且 from django.db.models import Q ret = Book.objects.filter(Q(price__gt=100) & Q(authors__name=小明)) print(ret) 3、非 ~ from django.db.models import Q ret = Book.objects.filter(Q(price__gt=100) & ~Q(authors__name=小明)) print(ret) 4、混合使用: 查询函数可以混合使用Q对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q对象)都将进行END运算, 但是,如果出现Q对象,它必须位于所有关键字参数的前面。 # 查询出版社id是1或者2,且作者名字中有小的书 ret = Book.objects.filter(Q(publisher_id=1) | Q(publisher_id=2), authors__name__contains=) print(ret) 九、F查询 1、F()主要用于修改字段的值(数字) 例如: from django.db.models import F # F用来在字段原来的基础上进行操作 Book.objects.all().update(price=F(price)+20) 进行了上面的操作后,数据库中book表所有的price字段的值全部加了20, 比如本来90的,现在就110 2、拓展:修改字段的值(字符串) from django.db.models import F # 给每本书的名字加‘新款‘(注意:一般在实际中不应该这么做,这里只是用于了解F对象) from django.db.models.functions import Concat # 字符串拼接 from django.db.models import Value # 把字符串转变成F对象可以操作的变量 Book.objects.all().update(title=Concat(F(title), Value(新款))) 十、事务 在事务代码块内的代码,全部都执行成功后,数据库中才会进行修改 只要存在一句错误的语句,全部代码都不会生效 一般使用在安全性需要很高的场景,比如,银行的转账, 为了避免一方转账后,中途出现断电,服务器异常等情况,导致另一方没收到转账, 而转账方的钱却减少了 from django.db import transaction with transaction.atomic(): # 创建一本书 Book.objects.create(title=嘿嘿书, price=10.99, isbn=213dsxc, publisher_id=1) # 把id=100的书名修改为哈哈书 book_obj = Book.objects.get(id=100) # 没有id=100的书,这里会报错,结果是整体都不生效,数据库不会有任何修改 book_obj.title = 哈哈书 book_obj.save() 十一、去重distinct ret = Book.objects.all().values(publisher__name).distinct() print(ret) 十二、Django ORM执行原生SQL(拓展了解) 1、raw 1. 查询自己这个表 ret = Book.objects.raw(select * from app04_book) # raw里面写原生的SQL语句 print(ret) for i in ret: print(i.title, i.price) 2. 查询别的表 在Book表查询publisher表,select必须要写上publisher的主键,能使用的也只有主键,如果要使用其他字段,就在主键后写上其他字段 ret = Book.objects.raw(select id, name from app04_publisher) for i in ret: print(i.id, i.name) 拓展知识:在ORM中,使用的是懒查询,就是当你没有使用这个结果的时候,它是不会去数据库帮你查询的, 比如上面的 ret = Book.objects.raw(select * from app04_book) 当你print这个结果的时候,并不会有内容的 因为ORM没有看到你使用这个ret,它不会去数据库帮你查询的,而当你把ret进行循环的时候,ORM知道你要使用了,它才会 真正地去数据库帮你进行操作并返回结果给你。 2、直接执行自定义SQL 有时候raw()方法并不十分好用,很多情况下我们不需要将查询结果映射成模型,或者我们需要执行DELETE、 INSERT以及UPDATE操作。在这些情况下,我们可以直接访问数据库,完全避开模型层。 我们可以直接从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库。 from django.db import connection, connections connection获取默认的数据库 connections当有多个数据库的时候,使用关键字获取指定的数据库 connections[default] 例如: from django.db import connection, connections cursor = connection.cursor() cursor.execute(select * from app04_book) ret = cursor.fetchone() print(ret) 十三、QuerySet方法大全
技术分享图片
def all(self)
    # 获取所有的数据对象

def filter(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def select_related(self, *fields)
    性能相关:表之间进行join连表操作,一次性获取关联的数据。

    总结:
    1. select_related主要针一对一和多对一关系进行优化。
    2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。

    总结:
    1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
    2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。

def annotate(self, *args, **kwargs)
    # 用于实现聚合group by查询

    from django.db.models import Count, Avg, Max, Min, Sum

    v = models.UserInfo.objects.values(u_id).annotate(uid=Count(u_id))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

    v = models.UserInfo.objects.values(u_id).annotate(uid=Count(u_id)).filter(uid__gt=1)
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    v = models.UserInfo.objects.values(u_id).annotate(uid=Count(u_id,distinct=True)).filter(uid__gt=1)
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    # 用于distinct去重
    models.UserInfo.objects.values(nid).distinct()
    # select distinct nid from userinfo

    注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
    # 用于排序
    models.UserInfo.objects.all().order_by(-id,age)

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    # 构造额外的查询条件或者映射,如:子查询

    Entry.objects.extra(select={new_id: "select col from sometable where othercol > %s"}, select_params=(1,))
    Entry.objects.extra(where=[headline=%s], params=[Lennon])
    Entry.objects.extra(where=["foo=‘a‘ OR bar = ‘a‘", "baz = ‘a‘"])
    Entry.objects.extra(select={new_id: "select id from tb where id > %s"}, select_params=(1,), order_by=[-nid])

 def reverse(self):
    # 倒序
    models.UserInfo.objects.all().order_by(-nid).reverse()
    # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


 def defer(self, *fields):
    models.UserInfo.objects.defer(username,id)
    或
    models.UserInfo.objects.filter(...).defer(username,id)
    #映射中排除某列数据

 def only(self, *fields):
    #仅取某个表中的数据
     models.UserInfo.objects.only(username,id)
     或
     models.UserInfo.objects.filter(...).only(username,id)

 def using(self, alias):
     指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

def raw(self, raw_query, params=None, translations=None, using=None):
    # 执行原生SQL
    models.UserInfo.objects.raw(select * from userinfo)

    # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
    models.UserInfo.objects.raw(select id as nid from 其他表)

    # 为原生SQL设置参数
    models.UserInfo.objects.raw(select id as nid from userinfo where nid>%s, params=[12,])

    # 将获取的到列名转换为指定列名
    name_map = {first: first_name, last: last_name, bd: birth_date, pk: id}
    Person.objects.raw(SELECT * FROM some_other_table, translations=name_map)

    # 指定数据库
    models.UserInfo.objects.raw(select * from userinfo, using="default")

    ################### 原生SQL ###################
    from django.db import connection, connections
    cursor = connection.cursor()  # cursor = connections[‘default‘].cursor()
    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    row = cursor.fetchone() # fetchall()/fetchmany(..)


def values(self, *fields):
    # 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
    # 获取每行数据为元祖

def dates(self, field_name, kind, order=ASC):
    # 根据时间进行某一部分进行去重查找并截取指定内容
    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    # order只能是:"ASC"  "DESC"
    # 并获取转换后的时间
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日

    models.DatePlus.objects.dates(ctime,day,DESC)

def datetimes(self, field_name, kind, order=ASC, tzinfo=None):
    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    # kind只能是 "year", "month", "day", "hour", "minute", "second"
    # order只能是:"ASC"  "DESC"
    # tzinfo时区对象
    models.DDD.objects.datetimes(ctime,hour,tzinfo=pytz.UTC)
    models.DDD.objects.datetimes(ctime,hour,tzinfo=pytz.timezone(Asia/Shanghai))

    """
    pip3 install pytz
    import pytz
    pytz.all_timezones
    pytz.timezone(‘Asia/Shanghai’)
    """

def none(self):
    # 空QuerySet对象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
   # 聚合函数,获取字典类型聚合结果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count(u_id, distinct=True), n=Count(nid))
   ===> {k: 3, n: 4}

def count(self):
   # 获取个数

def get(self, *args, **kwargs):
   # 获取单个对象

def create(self, **kwargs):
   # 创建对象

def bulk_create(self, objs, batch_size=None):
    # 批量插入
    # batch_size表示一次插入的个数
    objs = [
        models.DDD(name=r11),
        models.DDD(name=r22)
    ]
    models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
    # 如果存在,则获取,否则,创建
    # defaults 指定创建时,其他字段的值
    obj, created = models.UserInfo.objects.get_or_create(username=root1, defaults={email: 1111111,u_id: 2, t_id: 2})

def update_or_create(self, defaults=None, **kwargs):
    # 如果存在,则更新,否则,创建
    # defaults 指定创建时或更新时的其他字段
    obj, created = models.UserInfo.objects.update_or_create(username=root1, defaults={email: 1111111,u_id: 2, t_id: 1})

def first(self):
   # 获取第一个

def last(self):
   # 获取最后一个

def in_bulk(self, id_list=None):
   # 根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
   # 删除

def update(self, **kwargs):
    # 更新

def exists(self):
   # 是否有结果
方法大全


举几个例子:
# only:将指定的字段查询加载出来,后续再访问指定的字段就不需要再查询数据库
ret = Book.objects.all().only(title)  # 拿到所有书的对象列表
for i in ret:
    print(i.title)  # 访问指定的字段title不需要再去查询数据库

for i in ret:
    print(i.price)  # 访问不是指定的字段,每一次都去查一次数据库



# defer:将除了指定的字段查询加载出来,后续再访问指定的字段就不需要再查询数据库(only的反义词)
ret = Book.objects.all().defer(title)
for i in ret:
    print(i.title)  # 访问指定的字段title,每一次都需要去查询数据库

for i in ret:
    print(i.price)  # 访问不是指定的字段,不需要再查询数据库


# bulk_create:一次SQL语句批量创建
from datetime import date
obj = (Publisher(name=第%s出版社 %(i), addr=中国, date=date.today()) for i in range(100))
Publisher.objects.bulk_create(obj)


# select_related:把id=2的书的信息和它关联的出版社的信息一起查询出来。
ret = Book.objects.filter(id=2).select_related(publisher)
print(ret[0].publisher.name)

 

ORM之基础操作进阶

标签:清空   定时   res   email   基础   handle   userinfo   values   har   

原文地址:https://www.cnblogs.com/Zzbj/p/9939608.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!