python - 使用 Model @property 装饰器来检查 manytomany 的计数
问题描述
你如何设置模型装饰器(在 Django 中)来计算 ManyToManyFields(和其他可验证的条件)?
姜戈模型:
class Cake(models.Model):
cake_layer = models.ManyToManyField(CakeLayer, related_name="cake_layer")
cream_layer = models.ManyToManyField(CreamLayer, related_name="cream_layer")
@property
def at_least_one_cake_layer(self):
if self.cake_layer_set.count = 0:
raise AssertionError("At last one cake layer is needed to be a cake")
@property
def at_least_two_layers(self):
total_layer_count = 0
total_layer_count += self.cream_layer_set.count
total_layer_count += self.cream_layer_set.count
if total_layer_count <= 1:
raise AssertionError("A proper Cake needs two layers, even if they are just two cake layers!")
是否像能够在模型本身中进行查询一样简单?(我只在模板和视图中使用查询,如果它们是另一个模型,我不确定如何正确(如果有的话)访问模型的属性。
请注意:如果此代码可以工作,请告诉我,但你知道我来自哪里:
- 我正在根据我所学的内容重新编写应用程序架构,并尝试抽象一些东西以消除重复,并进入通用外键(GFK)/多表继承/多对多架构,并尝试不同的事情,并且不确定我想尝试的方法是否可行
- 正如阅读本文所建议的那样,我试图避免使用 GFK
pythonic
我假设使用装饰器可以使用一些数据库级逻辑(尽管我也会提供视图级业务逻辑),尽管在处理高度抽象/复杂的相关类时不确定这是否是好的架构
随时澄清我的假设:) 谢谢!
解决方案
一些明显的错误和一些改进建议:
class Cake(models.Model):
# 1. change related_name to sth that makes semantic sense!
# I'd also suggest plural names for m2m fields. Makes code more readable.
cake_layers = models.ManyToManyField(CakeLayer, related_name="cakes")
cream_layers = models.ManyToManyField(CreamLayer, related_name="cakes")
def at_least_one_cake_layer(self):
# 2. no _set suffix with forward m2m rels, just use the name of the field
# 3. count is a method, needs parentheses to be actually called
# 4. "=" is an assignment, use "==" for comparisons
if self.cake_layers.count() == 0:
raise AssertionError("At last one cake layer is needed to be a cake")
# 5. a property suggests to the caller that they are accessing a simple attribute which
# imho should not raise an exception. A boolean return value seems better.
# if you want to raise an error, I'd keep it a method.
@property
def at_least_two_layers(self):
# 6. seems you mean to add both counts here, so one should be cake_layers
# minor: adding two ints should not deserve 3 lines of code
total_layer_count = self.cake_layers.count() + self.cream_layers.count()
if total_layer_count < 2:
# minor: make code match output ;)
# even if <=1 and <2 are equivalent here, it is just good practice
# and you avoid pitfalls in more complicated cases, e.g. with floats
raise AssertionError("A proper Cake needs two layers, even if they are just two cake layers!")
就装饰器而言,它们只是应用于它们装饰的函数/类对象的普通 Python 函数,并且受到与任何其他函数(例如范围)相同的限制。你想用它们做什么是有意义的还是 Pythonic 取决于具体情况。
您可以在装饰器中执行数据库操作,但将其限制为装饰器返回的函数。装饰器函数本身(在大多数情况下,例如函数内部的类定义除外)将在模块加载时调用,此时模型可能尚未加载。您应该避免模块级代码中的数据库命中。
此外,您count()
用于检查是否存在。当您不使用实际数字时,使用exists()
哪个性能更高:
if not self.cake_layers.exists():
# raise hell / return False
if not (self.cake_layers.exists() and self.cream_layers.exists()):
# raise hell / return False
推荐阅读
- html - 实现 Bootstrap 4 数据表
- c# - 为For循环内对象数组的属性赋值
- java - 有没有办法为同一平台上的多个目标的 java 应用程序构建安装程序?
- c - 为什么 fgets 和 gets 无法将字符串读入结构?
- python - Python 中的依赖类型和多态性与 mypy
- php - symfony 4 学说 createQueryBuilder with andWhere
- jquery - 如何在 CSS 动画中设置可见性:隐藏然后可见性:可见?
- javascript - 如何在 JavaScript 中修复数组
- c - C中两个矩阵的和和乘积(带函数)
- facebook-graph-api - 从 Facebook Graph API 中提取我的时间线帖子