【问题标题】:Using a Model @property decorator to check the count of manytomany使用 Model @property 装饰器来检查 manytomany 的计数
【发布时间】:2018-12-16 12:10:30
【问题描述】:

如何设置模型装饰器(在 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)/多表继承/多对多架构,并且正在尝试不同的东西,我不确定我想尝试的方法是否可行
  • 按照阅读this 的建议,我正在尝试避免使用 GFK
  • 我假设使用装饰器可以使用一些数据库级别的逻辑,(尽管我也会提供视图级别的业务逻辑),虽然不确定这是pythonic 还是处理高度抽象/复杂相关类时的良好架构

请随时澄清我的假设 :) 谢谢!

【问题讨论】:

    标签: python django python-decorators


    【解决方案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
    

    【讨论】:

    • 感谢您抽出宝贵时间。花了我几次阅读以了解所有内容,但彻底性有很大帮助:)
    • def at_least_one_cake_layer(self): 也应该有一个@proprty decrator 吗?
    • 我就是这么想的。只是想确认一下,因为我现在认为我需要将clean() /full_clean() 添加到save() 中,并且我也在努力理解这一点:) 非常感谢!
    猜你喜欢
    • 2018-05-16
    • 2016-04-02
    • 2011-03-09
    • 2021-11-27
    • 1970-01-01
    • 2017-01-25
    • 2020-09-23
    相关资源
    最近更新 更多