【问题标题】:Subclass a Django ManyRelatedManager a.k.a. ManyToManyField子类化 Django ManyRelatedManager a.k.a. ManyToManyField
【发布时间】:2015-08-27 20:38:46
【问题描述】:

有没有办法对 Django ManyRelatedManager a.k.a. ManyToManyField 进行子类化

目标是在通过deleted=None 标志调用ManyRelatedManager 时预先过滤所有相关模型。如果deleted=None 则它是一个有效的Model

到目前为止,这是代码,但它似乎不起作用。

class ExcludeDeletedManyToManyField(models.ManyToManyField):

    def get_queryset(self):
        qs = super(ExcludeDeletedManyToManyField, self).get_queryset()
        return qs.filter(deleted__isnull=True)


class SelfRefrencingModel(models.Model):

    children = ExcludeDeletedManyToManyField('self', blank=True,
        symmetrical=False, related_name='parents')

【问题讨论】:

    标签: python django


    【解决方案1】:

    您可以创建proxy modelSelfRefrencingModel 并覆盖默认的manager。然后在ManyToManyField中使用这个代理模型。

    子类化 ManyToManyField 对您没有帮助,因为对于生成的查询集 ManyRelatedManger 负责。

    代理模型方法:

    from django.db import models
    
    class A(models.Model):
        children = models.ManyToManyField('AProxy')
        name = models.TextField()
        deleted = models.NullBooleanField(null=True)
    
    
    class FilterDeletedManager(models.Manager):
        def get_queryset(self):
            qs = super(FilterDeletedManager, self).get_query_set()
            return qs.filter(deleted__isnull=True)
    
    
    class AProxy(A):
        objects = FilterDeletedManager()
        class Meta:
            proxy = True
    

    使用这种方法需要注意的是,现在 django 预计 AProxy 字段的 AProxy 实例。

    所以也许更好的可读性和可维护性的方法是在__init__ 中添加另一个属性。

    from django.db import models
    
    class A(models.Model):
        children = models.ManyToManyField('self')
        name = models.TextField()
        deleted = models.NullBooleanField(null=True)
    
        def __init__(self, *args, **kwargs):
            super(A, self).__init__(*args, **kwargs)
            self.deleted_null_children = self.children.filter(deleted__isnull=True)
    

    【讨论】:

    • Proxy Models 用于在模型上定义额外的行为,并且没有任何字段。我认为这行不通....你有一个让它工作的代码示例吗?
    • 这似乎不适用于 Django 1.9 或 Django 1.11。
    【解决方案2】:

    这是我的解决方案。 @beezz,使用代理模型来执行此操作可能是正确的,但我之前没有使用过代理模型来处理此模式,所以这就是我解决这个问题的方法:

    class SelfRefrencingQuerySet(models.query.QuerySet):
        pass
    
    class SelfRefrencingManager(BaseManager):
    
        def get_queryset(self):
            return SelfRefrencingQuerySet(self.model, self._db).filter(
                deleted__isnull=True)
    
    class SelfRefrencingBaseModel(models.Model):
    
        children = models.ManyToManyField('self', blank=True, symmetrical=False,
            related_name='parents')
    
        # Manager
        objects = SelfRefrencingManager()
        objects_all = models.Manager()     # So you still have acccess to the
                                           # default Manager
    

    【讨论】:

    • 但是通过覆盖默认管理器,无法使用deleted__isnull=False 获取模型。不仅适用于字段children,而且完全适用。
    • @beezz 同样会覆盖ManyToMany 字段以排除它们,对吗?使用我的解决方案,您仍然可以保留默认的 models.Manager 并让 Model 有 2 个经理。这就是我正在使用的模式。 99% 的时间我们都不想删除记录,所以这种模式很好用。谢谢。
    【解决方案3】:

    如果您打算将其与 Django Admin 或 ModelForm 一起使用;你不需要继承ManyToManyField。见django documentation

    class SelfRefrencingModel(models.Model):
    
        children = models.ManyToManyField('self', blank=True, symmetrical=False, 
            related_name='parents', limit_choices_to={'deleted': False}))
    

    注意:如果deletedBooleanField,它必须是TrueFalse。不能是None/NULL

    beezz 的使用代理模型的想法也不错。

    我有时做的是自定义默认管理器
    class MyModelManager(models.Manager):
        use_for_related_fields = True
    
        def get_queryset(self):
            qs = super(MyModelManager, self).get_queryset()
            return qs.filter(deleted=False)
    
    
    class MyModelManager(models.Model):
        objects = MyModelManager()
        _objects = models.Manger()
    
        deleted = models.BooleanField(default=False)
    

    默认情况下deleted 对象将被隐藏,但如果您的查询集中需要它们,您可以使用_objects

    【讨论】:

    • 我不确定limit_choices_to 参数。如 django 的文档中所述,它仅适用于 admin 和 ModelForm 但可能就是这种情况。
    • 例如,在调用 model.children.all() 时,我无法让 limit_choices_to 工作。
    • 我已更新答案以包含您的观察结果。
    猜你喜欢
    • 2012-03-15
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 2013-02-16
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多