【问题标题】:How to restrict the possible choices in the form for a many-to-many field in Django?如何限制 Django 中多对多字段的表单中可能的选择?
【发布时间】:2017-06-06 16:34:07
【问题描述】:

这是我在一个描述图表的应用程序中的模型(准确地说是DAG):

class Node(models.Model):
    name = models.CharField(max_length=120, blank=True, null=True)
    parents = models.ManyToManyField('self', blank=True, symmetrical=False)

    def list_parents(self):
        return self.parents.all()

    def list_children(self):
        return Node.objects.filter(parents=self.id)

    def list_withindirect(self, arg):
        direct = getattr(self, arg)()
        withindirect = set(direct)
        for d in direct:
             withindirect |= set(d.list_withindirect(arg))
        return list(withindirect)

    def list_ancestors(self):
         return self.list_withindirect('list_parents')

    def list_descendants(self):
         return self.list_withindirect('list_children')

    def list_of_allowed_parents(self):
        return list(
            set(Node.objects.all()) - set(self.list_descendants()) - {self}
        )

    def __str__(self):
        return self.name

每个节点可以有许多其他节点作为父节点。这里的要点是,给定节点具有一组特定的允许父节点。这就是list_of_allowed_parents 方法的用途。但是我怎样才能让表单在下拉列表中只显示这些节点?

目前是这种形式:

class NodeForm(forms.ModelForm):
    class Meta:
        model = Node
        fields = ['name', 'parents']

我向管理员注册:

class NodeAdmin(admin.ModelAdmin):
    list_display = ['id', 'name', 'list_parents', 'list_children']
    form = NodeForm

admin.site.register(Node, NodeAdmin)

我想我将不得不使用类似的东西:

parents = forms.ModelMultipleChoiceField(queryset=node.list_of_allowed_parents())

但是我如何将特定的 node 传递给 forms.py 中的定义?

理想情况下,此约束将添加到模型中。 ManyToManyField提供了choiceslimit_choices_to的参数,但是这里好像没有办法添加self.list_of_allowed_parents

【问题讨论】:

    标签: django-models django-forms many-to-many directed-acyclic-graphs


    【解决方案1】:

    您正在寻找可调用的limit_choices_to many2manyField 参数。

    在此讨论:https://stackoverflow.com/a/252087/842935

    【讨论】:

    • 我的问题与那里提到的一样:“我在这里看不到访问'self'的方法。”
    【解决方案2】:

    你可以试试这个

    views.py

    如果您使用基于类的视图..

     def get_form_kwargs(self): 
        """Returns the keyword arguments for instantiating the form.""" 
        kwargs = super(YourViewName, self).get_form_kwargs() 
        node = get_object_or_404(Node, pk=self.kwargs['node_id'])
        kwargs['node'] = node
        return kwargs
    

    如果您使用的是基于功能的视图..

    node = get_object_or_404(Node, pk=node_id)
    form = NodeForm(node=node)
    

    【讨论】:

      【解决方案3】:

      您可以调用表单__init__ 方法来更新父查询集。 node 可以通过更新表单 kwargs 从视图中发送,或者如果更新相同的对象,您可以使用 instance

      forms.py

      class NodeForm(forms.ModelForm):
          def __init__(self, *args, **kwargs):
              node = kwargs.pop('node', None)
              # node = kwargs.pop('instance', None)
      
              super(NodeForm, self).__init__(*args, **kwargs) 
              self.fields['parents'].queryset = node.list_of_allowed_parents()
      
          class Meta: 
              model = Node 
              fields = ['name', 'parents']
      

      你可以试试这个

      views.py

      如果您使用基于类的视图..

       def get_form_kwargs(self): 
          """Returns the keyword arguments for instantiating the form.""" 
          kwargs = super(YourViewName, self).get_form_kwargs() 
          node = get_object_or_404(Node, pk=self.kwargs['node_id'])
          kwargs['node'] = node
          return kwargs
      

      如果您使用的是基于功能的视图..

      node = get_object_or_404(Node, pk=node_id)
      form = NodeForm(node=node)
      

      【讨论】:

      • 这是“旧风格”。现在limit_choices_to 可用:)
      • // 应该做什么?当我在__init__ 中留下第二行时,我收到错误'list' object has no attribute 'iterator',当我删除它时,第一个错误是'NoneType' object has no attribute 'list_of_allowed_parents'
      • 其实并不清楚如何在表单中传递节点对象..所以无论是从节点键还是实例键都显示两种方式,以便您可以根据需要使用。
      • 我会使用任何有效的方法。目前我不知道我应该如何获取 forms.py 的节点键或实例。
      • 那么你能告诉我你 view.py 你在哪里使用那个表格吗..这样我就可以找出你的问题。
      猜你喜欢
      • 1970-01-01
      • 2015-11-08
      • 2012-04-02
      • 1970-01-01
      • 2011-08-23
      • 2011-02-01
      • 2010-12-16
      • 1970-01-01
      • 2018-04-21
      相关资源
      最近更新 更多