使用Model @property装饰器来检查多方计数

时间:2018-07-08 18:20:30

标签: python django python-decorators

如何设置模型装饰器(在Django中)以计算ManyToManyFields(以及其他可验证的条件)?

Django模型:

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)/多表继承/ ManyToMany体系结构,并尝试发生了很多事情,并且不确定我想尝试的方法是否可行
  • 按照阅读this
  • 的建议,我正在尝试避免使用GFK。
  • 我假设使用装饰器可以使用一些数据库级逻辑(尽管我也将提供视图级业务逻辑),尽管在处理高度抽象/复杂的情况时不确定是pythonic还是好的架构相关课程

请随意阐明我的假设:)谢谢!

1 个答案:

答案 0 :(得分:1)

一些明显的错误和一些改进建议:

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