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根据经纬度计算距离示例
Feb 16 Python
python任务调度实例分析
May 19 Python
python计算auc指标实例
Jul 13 Python
python 移除字符串尾部的数字方法
Jul 17 Python
Python3实现爬虫爬取赶集网列表功能【基于request和BeautifulSoup模块】
Dec 05 Python
selenium+python自动化测试之鼠标和键盘事件
Jan 23 Python
python 下 CMake 安装配置 OPENCV 4.1.1的方法
Sep 30 Python
Python在终端通过pip安装好包以后在Pycharm中依然无法使用的问题(三种解决方案)
Mar 10 Python
Python判断字符串是否为空和null方法实例
Apr 26 Python
python3 使用openpyxl将mysql数据写入xlsx的操作
May 15 Python
Python建造者模式案例运行原理解析
Jun 29 Python
python 基于UDP协议套接字通信的实现
Jan 22 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 实现人民币小写转换成大写的方法及大小写转换函数
2017/11/17 PHP
PHP 判断字符串是中文还是英文, 或者是中英混合
2021/03/09 PHP
javascript 出生日期和身份证判断大全
2008/11/13 Javascript
jQuery 剧场版 你必须知道的javascript
2009/05/27 Javascript
jquery之超简单的div显示和隐藏特效demo(分享)
2013/07/09 Javascript
Jquery创建一个层当鼠标移动到层上面不消失效果
2013/12/12 Javascript
Javascript 赋值机制详解
2014/11/23 Javascript
JS实现单行文字不间断向上滚动的方法
2015/01/29 Javascript
jQuery使用hide方法隐藏元素自身用法实例
2015/03/30 Javascript
js实现的简单radio背景颜色选择器代码
2015/08/18 Javascript
JS+CSS实现经典的左侧竖向滑动菜单效果
2015/09/23 Javascript
基于jQuey实现鼠标滑过变色(整行变色)
2015/12/07 Javascript
微信小程序 配置文件详细介绍
2016/12/14 Javascript
js实现仿购物车加减效果
2017/03/01 Javascript
vue如何从接口请求数据
2017/06/22 Javascript
Vue v-bind动态绑定class实例方法
2020/01/15 Javascript
element日历calendar组件上月、今天、下月、日历块点击事件及模板源码
2020/07/27 Javascript
[30:55]完美世界DOTA2联赛PWL S2 Magma vs LBZS 第二场 11.18
2020/11/18 DOTA
python中的装饰器详解
2015/04/13 Python
举例讲解Python中的迭代器、生成器与列表解析用法
2016/03/20 Python
Python和C/C++交互的几种方法总结
2017/05/11 Python
python3+PyQt5泛型委托详解
2018/04/24 Python
python3 字符串/列表/元组(str/list/tuple)相互转换方法及join()函数的使用
2019/04/03 Python
Python:type、object、class与内置类型实例
2019/12/25 Python
Python图像处理库PIL的ImageDraw模块介绍详解
2020/02/26 Python
torchxrayvision包安装过程(附pytorch1.6cpu版安装)
2020/08/26 Python
python-图片流传输的思路及示例(url转换二维码)
2020/12/21 Python
CSS中几个与换行有关的属性简明总结
2014/04/15 HTML / CSS
美国背景检查、公共记录和人物搜索网站:BeenVerified
2018/02/25 全球购物
加大码胸罩、内裤和服装:Just My Size
2019/03/21 全球购物
阿里巴巴美国:Alibaba美国
2019/11/24 全球购物
JSP和EJB可以共享HttpSession么?EJB里面可以改变session里面的内容
2013/06/05 面试题
设备售后服务承诺书
2014/05/30 职场文书
五心教育心得体会
2014/09/04 职场文书
2015年工程部工作总结
2015/04/30 职场文书
公司董事任命书
2015/09/21 职场文书