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中文乱码的解决方法
Nov 04 Python
一个月入门Python爬虫学习,轻松爬取大规模数据
Jan 03 Python
简单实现python收发邮件功能
Jan 05 Python
Python zip()函数用法实例分析
Mar 17 Python
查找python项目依赖并生成requirements.txt的方法
Jul 10 Python
python2和python3在处理字符串上的区别详解
May 29 Python
使用python画社交网络图实例代码
Jul 10 Python
详解Python 4.0 预计推出的新功能
Jul 26 Python
详解pycharm连接不上mysql数据库的解决办法
Jan 10 Python
Python tkinter实现简单加法计算器代码实例
May 13 Python
PyQt5实现简单的计算器
May 30 Python
Python matplotlib绘制雷达图
Apr 13 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
IP攻击升级,程序改进以对付新的攻击
2010/11/23 PHP
php中随机函数mt_rand()与rand()性能对比分析
2014/12/01 PHP
php实现的验证码文件类实例
2015/06/18 PHP
php session实现多级目录存放实现代码
2016/02/03 PHP
Javascript 的addEventListener()及attachEvent()区别分析
2009/05/21 Javascript
google地图的路线实现代码
2009/08/20 Javascript
JS控制图片等比例缩放的示例代码
2013/12/24 Javascript
jquery绑定事件不生效的解决方法
2014/02/11 Javascript
jQuery如何取id有.的值一般的方法是取不到的
2014/04/18 Javascript
jQuery中index()方法用法实例
2014/12/27 Javascript
浅析JavaScript回调函数应用
2016/05/22 Javascript
jquery实现的回旋滚动效果完整实例【附demo源码下载】
2016/09/20 Javascript
Javascript Function.prototype.bind详细分析
2016/12/29 Javascript
Vue-router 中hash模式和history模式的区别
2018/07/24 Javascript
小程序分页实践之编写可复用分页组件
2019/07/18 Javascript
jQuery实现高度灵活的表单验证功能示例【无UI】
2020/04/30 jQuery
javascript实现前端成语点击验证优化
2020/06/24 Javascript
[27:02]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS LGD第三场
2014/05/24 DOTA
Python获取linux主机ip的简单实现方法
2016/04/18 Python
深度定制Python的Flask框架开发环境的一些技巧总结
2016/07/12 Python
python3实现多线程聊天室
2018/12/12 Python
python实践项目之监控当前联网状态详情
2019/05/23 Python
python 实现GUI(图形用户界面)编程详解
2019/07/17 Python
python orm 框架中sqlalchemy用法实例详解
2020/02/02 Python
Python双链表原理与实现方法详解
2020/02/22 Python
pymysql 插入数据 转义处理方式
2020/03/02 Python
python绘制高斯曲线
2021/02/19 Python
纯css3实现的竖形无限级导航
2014/12/10 HTML / CSS
CSS3绘制有活力的链接下划线
2016/07/14 HTML / CSS
纯CSS3制作的鼠标悬停时边框旋转
2017/01/03 HTML / CSS
美国马匹用品和骑马配件购物网站:Horse.com
2018/01/08 全球购物
美国最大的电子宠物训练产品制造商:PetSafe
2018/10/12 全球购物
2014年中学生检讨书大全
2014/10/09 职场文书
2015年小学实验室工作总结
2015/07/28 职场文书
pytorch中的model.eval()和BN层的使用
2021/05/22 Python
动作冒险《Hell Is Us》将采用虚幻5 消灭怪物探索王国
2022/04/13 其他游戏