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 23 Python
对pandas中apply函数的用法详解
Apr 10 Python
python中yaml配置文件模块的使用详解
Apr 27 Python
对Python中创建进程的两种方式以及进程池详解
Jan 14 Python
selenium跳过webdriver检测并模拟登录淘宝
Jun 12 Python
python开头的coding设置方法
Aug 08 Python
python二进制读写及特殊码同步实现详解
Oct 11 Python
python安装本地whl的实例步骤
Oct 12 Python
在python中计算ssim的方法(与Matlab结果一致)
Dec 19 Python
详解python tcp编程
Aug 24 Python
pyspark对Mysql数据库进行读写的实现
Dec 30 Python
matplotlib对象拾取事件处理的实现
Jan 14 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中使用DOM类读取XML文件的实现代码
2011/12/14 PHP
php 操作数组(合并,拆分,追加,查找,删除等)
2012/07/20 PHP
php开启openssl的方法
2014/05/15 PHP
2个Codeigniter文件批量上传控制器写法例子
2014/07/25 PHP
2014年最新推荐的10款 PHP 开发框架
2014/08/01 PHP
php实现mysql连接池效果实现代码
2018/01/25 PHP
PHP设计模式入门之迭代器模式原理与实现方法分析
2020/04/26 PHP
js 调整select 位置的函数
2008/02/21 Javascript
jquery获取焦点和失去焦点事件代码
2013/04/21 Javascript
$.getJSON在IE下失效的原因分析及解决方法
2013/06/16 Javascript
js获取location.href的参数实例代码
2013/08/02 Javascript
js 用CreateElement动态创建标签示例
2013/11/20 Javascript
js中把JSON字符串转换成JSON对象最好的方法
2014/03/21 Javascript
js截取中英文字符串、标点符号无乱码示例解读
2014/04/17 Javascript
jquery实现的点击翻书效果代码
2015/11/04 Javascript
JS实现的在线调色板实例(附demo源码下载)
2016/03/01 Javascript
详解js实现线段交点的三种算法
2016/08/09 Javascript
微信小程序 教程之小程序配置
2016/10/17 Javascript
BootStrap实现轮播图效果(收藏)
2016/12/30 Javascript
为JQuery EasyUI 表单组件增加焦点切换功能的方法
2017/04/13 jQuery
推荐三款不错的图片压缩上传插件(webuploader、localResizeIMG4、LUploader)
2017/04/21 Javascript
JS正则表达式常见用法实例详解
2018/06/19 Javascript
jQuery实现点击图标div循环放大缩小功能
2018/09/30 jQuery
mpvue全局引入sass文件的方法步骤
2019/03/06 Javascript
基于AngularJS拖拽插件ngDraggable.js实现拖拽排序功能
2019/04/02 Javascript
[01:36:57]【09DOTA2第一视角】小骷髅
2014/04/16 DOTA
python+opencv实现的简单人脸识别代码示例
2017/11/14 Python
python版飞机大战代码分享
2018/11/20 Python
python支付宝支付示例详解
2019/08/22 Python
Python tornado上传文件的功能
2020/03/26 Python
python实现密度聚类(模板代码+sklearn代码)
2020/04/27 Python
详解css3使用transform出现字体模糊的解决办法
2020/10/16 HTML / CSS
html5时钟实现代码
2010/10/22 HTML / CSS
英国家居用品和床上用品零售商:P&B Home
2020/01/16 全球购物
800字作文之大雪
2019/12/04 职场文书
使用canvas仿Echarts实现金字塔图的实例代码
2021/11/11 HTML / CSS