Django多层嵌套ManyToMany字段ORM操作详解


Posted in Python onMay 19, 2020

在用django写项目时,遇到了许多场景,关于ORM操作获取数据的,但是不好描述出来,百度搜索关键词都不知道该怎么搜,导致一个人鼓捣了好久。这里细化下问题,还原场景,记录踩下的坑

首先先列举model,我举些生活中的例子,更方便理解问题

# 习题
class Problem(models.Model):
  desc = models.CharField()
  answer = models.TextField()
  is_pass = models.BooleanField(default=False, verbose_name="是否通过")

# 章节
class Chapter(models.Model):
  _id = models.IntegerField(verbose_name="编号")
  title = models.CharField()
  problem = models.ManyToManyField(Problem)
  pass_rate = models.IntegerField(verbose_name="通关率")

# 书籍  
class Book(models.Model):
  title = models.CharField()
  desc = models.TextField()
  chapter = models.ManyToManyField(Chapter,verbose_name="章节")
  speed = models.IntegerField(verbose_name="学习进度", default=0)

假设是一本数学书,有5个章节,每个章节里有数量不等的习题,

即book与chapter是多对多,chapter与problem也是多对多

场景一: 书籍下的所有习题

# 按我的理解是取问题非空的章节数
# 类似于问爷爷有几个孙子,没办法跨辈,就按一个孙子对应一个爸爸来取(有重复)
book.chapter.filter(problem___id__isnull=False).count()

场景二:书籍下所有通过的习题

book.chapter.filter(problem__is_pass=True).count()

场景三: 判断某个问题是否在这本书里

def problem_in_ladder(book, problem):
    for i in book.chapter.all():
      if problem in i.problem.all():
        return True
    return False

尽可能的减少view中对models的取值操作,所以把上面几个场景方法写在models类中

最终的models

# 习题
class Problem(models.Model):
  desc = models.CharField()
  answer = models.TextField()
  is_pass = models.BooleanField(default=False, verbose_name="是否通过")

# 章节
class Chapter(models.Model):
  _id = models.IntegerField(verbose_name="编号")
  title = models.CharField()
  problem = models.ManyToManyField(Problem)
  pass_rate = models.IntegerField(verbose_name="通关率")
 
  @property
  def items(self):
    return self.problem.count()

  @property
  def pass_problem(self):
    return self.problem.filter(is_pass=True).count()
  
# 书籍  
class Book(models.Model):
  title = models.CharField()
  desc = models.TextField()
  chapter = models.ManyToManyField(Chapter,verbose_name="章节")
  speed = models.IntegerField(verbose_name="学习进度", default=0)
  
  @property
  def chapters(self):
    return self.chapter.count()

  @property
  def pass_count(self):
    return self.chapter.filter(problem__is_pass=True).count()

  @property
  def items(self):
    return self.chapter.filter(problem___id__isnull=False).count()

补充知识:django中当model设置了ordering后,使用distinct()和annotate()问题记录

model类如下,我在class Meta中设置了ordering = ['-date_create'],即模型对象返回的记录结果集是按照这个字段排序的。

class SystemUserPushHistory(models.Model):
 
  id = models.UUIDField(default=uuid.uuid4, primary_key=True)
  host_name = models.CharField(max_length=128, null=False)
  system_username = models.CharField(max_length=128, null=False)
  method = models.CharField(max_length=32, null=False)
  is_success = models.BooleanField(default=False)
  date_create = models.DateTimeField(auto_now_add=True, editable=False)
  message = models.CharField(max_length=4096, null=True)
 
  class Meta:
    db_table = "assets_systemuser_push_history"
    ordering = ['-date_create']
 
  def __str__(self):
    ret = self.system_username + " => " + self.host_name
    return ret

当业务有需求如对host_name进行分组显示,在代码中用到了annotate,如下。

>>> from django.db.models import Count 
>>> from assets.models import SystemUserPushHistory
>>> p = SystemUserPushHistory.objects.values("host_name").annotate(dcount=Count(1))
>>> p
<QuerySet [{'host_name': '点2', 'dcount': 1}, {'host_name': '点3', 'dcount': 2}, {'host_name': '点2', 'dcount': 1}, {'host_name': '点3', 'dcount': 1}]>
>>> print(p.query)
SELECT `assets_systemuser_push_history`.`host_name`, COUNT(1) AS `dcount` FROM `assets_systemuser_push_history` GROUP BY `assets_systemuser_push_history`.`host_name`, `assets_systemuser_push_history`.`date_create` ORDER BY `assets_systemuser_push_history`.`date_create` DESC

可以看到,所得到的结果并不像我们预期的一样,之后把执行的sql输出出来可以看到在group by的时候是对host_name和date_create进行分组,原因就是因为我们在model类中设置了ordering,去掉之后代码运行正常。

使用distinct和上面的情况类似,就不列出来了。

以上这篇Django多层嵌套ManyToMany字段ORM操作详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python单链表的简单实现方法
Sep 23 Python
浅谈Python中的数据类型
May 05 Python
Python发送form-data请求及拼接form-data内容的方法
Mar 05 Python
Python面向对象编程基础解析(一)
Oct 26 Python
基于Django的ModelForm组件(详解)
Dec 07 Python
numpy向空的二维数组中添加元素的方法
Nov 01 Python
Python中的上下文管理器相关知识详解
Sep 19 Python
Python综合应用名片管理系统案例详解
Jan 03 Python
python实现单目标、多目标、多尺度、自定义特征的KCF跟踪算法(实例代码)
Jan 08 Python
Python守护进程实现过程详解
Feb 10 Python
10行Python代码实现Web自动化管控的示例代码
Aug 14 Python
教你如何使用Python实现二叉树结构及三种遍历
Jun 18 Python
django ORM之values和annotate使用详解
May 19 #Python
基于python实现地址和经纬度转换
May 19 #Python
Python Django form 组件动态从数据库取choices数据实例
May 19 #Python
Django自关联实现多级联动查询实例
May 19 #Python
Python的Django框架实现数据库查询(不返回QuerySet的方法)
May 19 #Python
django 数据库返回queryset实现封装为字典
May 19 #Python
使用PyQt的QLabel组件实现选定目标框功能的方法示例
May 19 #Python
You might like
PHP中isset()和unset()函数的用法小结
2014/03/11 PHP
Laravel框架学习笔记(二)项目实战之模型(Models)
2014/10/15 PHP
PHP中的类型约束介绍
2015/05/11 PHP
PHP哈希表实现算法原理解析
2020/12/11 PHP
JQuery autocomplete 使用手册
2010/04/01 Javascript
JavaScript—window对象使用示例
2013/12/09 Javascript
简单谈谈javascript中的变量、作用域和内存问题
2015/08/30 Javascript
学习JavaScript设计模式之中介者模式
2016/01/14 Javascript
jQuery select自动选中功能实现方法分析
2016/11/28 Javascript
javascript中this用法实例详解
2017/04/06 Javascript
ES6下React组件的写法示例代码
2017/05/04 Javascript
详解刷新页面vuex数据不消失和不跳转页面的解决
2018/01/30 Javascript
详解如何创建并发布一个 vue 组件
2018/11/08 Javascript
Vue 实现拨打电话操作
2020/11/16 Javascript
一则python3的简单爬虫代码
2014/05/26 Python
Python中使用urllib2防止302跳转的代码例子
2014/07/07 Python
Python双精度浮点数运算并分行显示操作示例
2017/07/21 Python
详解python中@的用法
2019/03/27 Python
Python中字符串与编码示例代码
2019/05/20 Python
window环境pip切换国内源(pip安装异常缓慢的问题)
2019/12/31 Python
Python3 全自动更新已安装的模块实现
2020/01/06 Python
如何使用 Python 读取文件和照片的创建日期
2020/09/05 Python
用Python进行websocket接口测试
2020/10/16 Python
利用纯html5绘制出来的一款非常漂亮的时钟
2015/01/04 HTML / CSS
英国儿童家具专卖店:GLTC
2016/09/24 全球购物
社区党员先进事迹
2014/01/22 职场文书
中学自我评价
2014/01/31 职场文书
给公司的建议书范文
2014/05/13 职场文书
乳制品整治工作方案
2014/05/29 职场文书
班级课外活动总结
2014/07/09 职场文书
大学生敬老院活动总结
2015/05/07 职场文书
功夫熊猫观后感
2015/06/10 职场文书
2016年社区创先争优活动总结
2016/04/05 职场文书
2019客服个人年终工作总结范文
2019/07/08 职场文书
python 中的@运算符使用
2021/05/26 Python
python中对列表的删除和添加方法详解
2022/02/24 Python