【问题标题】:Django (DRF) ManyToMany field choices / limitDjango (DRF) 多对多字段选择/限制
【发布时间】:2021-01-29 11:24:55
【问题描述】:

使用 Django REST 框架我想知道是否可以将模型上 ManyToMany 字段的选项/选项限制为特定的 QuerySet

使用下面的模型(向下滚动查看模型),我很好奇是否可以在模型定义中定义此限制,以实现以下目标:

# Having the following Employee instance
emp = Employee(...)

# Should return only the instances with value 'case' in EmployeeSubstitute.type field
emp.substitute_case.all()

# Should return only the instances with value 'phone' in EmployeeSubstitute.type field
emp.substitute_phone.all()

型号:

class Employee(models.Model):
    substitute_case = models.ManyToMany(through=EmployeeSubstitute, ...)
    substitute_phone = models.ManyToMany(through=EmployeeSubstitute, ...)
class EmployeeSubstitute(models.Model):
    from = models.ForeignKey(Employee, ...)
    to = models.ForeignKey(Employee, ...)
    type = models.CharField(choices=..., ...)  # choose between type 'case' and 'phone'

我看到有 limit_choices_to 参数,但这不是我想要的,因为它只会影响使用 ModelForm 或管理员时显示的选项。

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    嗯,ManyToManyField 返回相关对象并作为docs 状态

    默认情况下,Django 使用 Model._base_manager 管理器的实例 访问相关对象(即choice.question)时的类,而不是 _default_manager 在相关对象上。这是因为 Django 需要能够检索相关对象,即使它本来可以 被默认管理器过滤掉(因此无法访问)。

    如果正常的基础管理器类 (django.db.models.Manager) 不是 适合你的情况,你可以告诉 Django 哪个类 通过设置 Meta.base_manager_name 使用。

    查询相关模型时不使用基础管理器,或者 访问一对多或多对多的关系。例如,如果 本教程中的问题模型有一个已删除的字段和一个基础 过滤掉已删除实例的管理器=True,查询集如 Choice.objects.filter(question__name__startswith='What') 将包括 与已删除问题相关的选项。

    所以如果我没看错的话,不,这是不可能的。

    当您进行查询并在您的ManyToManyField 中有through 时,Django 抱怨您应该在您的through 模型上运行这些查询,而不是“父”模型。我在文档中找不到它,但我记得看过几次。

    substitute_casesubstitute_phone 是属于替代品的东西,它是它的类型。因此,只需这样做,而不是在 Employee 中创建这些列。

    from django.db import models
    
    
    class SubstituteTypes(models.TextChoices):
        case = "case", "case"
        phone = "phone", "phone"
    
    
    class EmployeeSubstituteQueryset(models.QuerySet):
        def from_employee(self, e):
            return self.filter(_from=e)
    
        def case(self):
            return self.filter(type=SubstituteTypes.case)
    
        def phone(self):
            return self.filter(type=SubstituteTypes.phone)
    
    
    class Employee(models.Model):
        substitute = models.ManyToManyField(through='EmployeeSubstitute', to='self')
    
    
    class EmployeeSubstitute(models.Model):
        _from = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='a')
        to = models.ForeignKey(Employee, on_delete=models.PROTECT, related_name='b')
        type = models.CharField(choices=SubstituteTypes.choices, max_length=5, db_index=True)
    
        objects = EmployeeSubstituteQueryset.as_manager()
    

    然后,一旦你得到你的 emp 对象(或只有它的 id),你就可以做

    EmployeeSubstitute.objects.from_employee(emp).case().all()
    

    这是按照 Django 哲学设计的。

    【讨论】:

    • 谢谢。我意识到我应该学会与模型经理一起工作。阅读您引用的部分,我认为我最初试图创建的内容可能是不可能的。
    猜你喜欢
    • 1970-01-01
    • 2011-05-19
    • 2017-06-06
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    相关资源
    最近更新 更多