【问题标题】:Pass argument to __init__ of object model class in iterate queryset将参数传递给迭代查询集中对象模型类的 __init__
【发布时间】:2017-09-01 07:50:41
【问题描述】:

我有一个覆盖__init__ 方法的模型,如下所示:

class MyModel(models.Model):
    ...

    def __init__(self, *args, **kwargs):
        if not kwargs.get('skip', False):
            do_something()
        super().__init__(*args, **kwargs) 

当我迭代查询集时,如何将skip 参数传递给__init__

data = [obj for obj in MyModel.objects.all()]

我想通过自定义管理器中的方法来实现这个,使用这样的东西:queryset.with_skip()

【问题讨论】:

  • do_something() 是做什么的?可能有更好的方法来覆盖__init__
  • 您可以编写自己的经理并将do_something() 重新定位到您的新默认经理中。
  • @Alasdair,是的,我相信它会更好,我在Django Documentation 中读到了这个,覆盖__init__ 方法不是一个好主意。但是在这种情况下,它为模型对象添加了属性,应该在对象更改之前使用。 __init__ 方法中的 do_something 位置确保了这一点。
  • 我仍然无法想象 do_something() 的角色在 __init__ 之前可能有用,如果它设计得好的话。是外部交互(如pre_init 信号或日志记录)还是数据解析? “跳过”真的是数据的一部分还是一种优化,你想有时用跳过从数据库中读取同一个对象,有时用做某事?你考虑过考试吗?你能在测试中安全地做一些事情还是必须跳过?您是否需要通过一个查询集一起读取有和没有跳过的对象?
  • 非常感谢,@hynekcer!我为我的解决方案使用了代理模型。 do_something 中的内容实际上并不重要,因为我无法触及那段代码。我在我的块中制作了该模型的代理,并以这种方式简单地覆盖了__init__ 方法def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs, skip=True) 就是这样!

标签: django django-models django-queryset


【解决方案1】:

我看到您在将参数 skipkwargs 传递给 super().__init__ 之前没有删除它。这意味着“skip”是字段的名称,否则你会得到异常TypeError("'skip' is an invalid keyword argument for this function")

如果您在创建对象之前确实需要do_something(),然后再如此不可或缺地使用,以至于没有人应该忘记避免所有不受支持的方式(??),那么自定义管理器等是不够的。

您的问题是 models.Model.__init__(...) 支持 *args**kwargs 参数非常完美,它们应该可以互换。你打破了它,如果位置参数的完整元组传递了“跳过”,你忽略它。也就是说,如果对象是从数据库创建的。阅读文档Customizing model loading

...如果模型的所有字段都存在,那么值保证按照__init__() 期望的顺序排列。即实例可以通过cls(*values)...
创建 .
| @classmethod
| def from_db(cls, db, field_names, values):
| ...
| instance = cls(*values)
| ...

解决此问题的一种简单方法是在super().__init__ 之后调用do_something() 并读取self.skip,而不是同时解析kwargs 和args。

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs) 
        if not self.skip:
            do_something()

如果需要,问题可能是在super().__init__ 末尾发送的信号“post_init”。

最后一种可能性是支持*args(很老套,但仍以某种方式使用记录在案的名称):

    def __init__(self, *args, **kwargs):
        if kwargs:
            skip = kwargs.get('skip', False)
        else:
            # check "val is True" in order to skip if val is DEFERRED
            skip = any(field.name == 'skip' and val is True
                       for val, field in zip(args, self._meta.concrete_fields)
                       )
        if not skip:
            do_something()
        super().__init__(*args, **kwargs)

编辑:也许你不需要你想要的东西,而 Proxy model 有时可以在同一个数据库表中相同数据的基本模型上做一些额外的事情是正确的解决方案. (“Skip”看起来不像是描述对象数据的名称,而是描述对象创建模式的名称。比内部神秘的开关更容易测试和维护子类。)

【讨论】:

    猜你喜欢
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    • 2020-12-26
    • 2013-10-22
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    相关资源
    最近更新 更多