首页 > 解决方案 > Django (DRF) 多对多字段选择/限制

问题描述

使用 Django REST Framework 我想知道是否可以将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 或管理员时显示的选项。

标签: djangodjango-rest-framework

解决方案


好吧,ManyToManyField返回相关对象和文档状态

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

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

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

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

当您进行查询并through在您ManyToManyFieldthrough. 我在文档中找不到它,但我记得看过几次。

substitute_case并且substitute_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 哲学中设计的。


推荐阅读