首页 > 解决方案 > 来自实例的 Django ModelChoiceField 动态查询集

问题描述

我有一个数据集,用户可能需要在其中编辑旧记录,但也需要将选择限制为一组“活动”值。This means the legacy choices need to be added to the queryset of the ModelChoiceField when an instance containing a non-active value is selected.

在我看来,理想的方法是对 ModelChoiceField 进行子类化,但是我似乎无法在 ModelChoiceField 中找到实例数据的插入点。我可以通过在 forms.py 中设置查询集来使其工作,但我有大量字段需要这些字段,并且真的希望有一个更 Pythonic/DRY 的解决方案。

例如:

模型.py

class ActiveChoiceModel(models.Model):
    name = models.CharField(max_length=10)
    active = models.NullBooleanField(default=False)

class MyModel(models.Model):
   fk_activechoicemodel = models.ForeignKey(to='mydb.ActiveChoiceModel')

ModelChoiceField 的查询集应该是:

    ActiveChoiceModel.objects.filter(active=True) | ActiveChoiceModel.objects.filter(pk=instance.fk_activechoicemodel.id)

这可以在 forms.py 中实现,如下所示:

 Class MyForm(forms.ModelForm):
     def __init__(self, *args, **kwargs):
         super(MyForm, self).__init__(*args, **kwargs)
         if self.instance.fk_activechoicemodel:
             self.fields['fk_activechoicemodel'].queryset = ActiveChoiceModel.objects.filter(active=True) | ActiveChoiceModel.objects.filter(pk=instance.fk_activechoicemodel.id)

关于如何为 10 或 100 的“ActiveChoiceModels”做到这一点的清洁和干燥有什么想法吗?

标签: pythondjango

解决方案


选项1

我认为最好的解决方案是自定义 models.Manager来获取特殊的查询集。但是,真正的工作是使用Q 对象完成的,因此如果您不必在其他任何地方拉取此查询集,则可能只使用 Q 对象。没有你的代码我没有测试过,但它是从我正在运行的自定义管理器修改的,加上这个答案

# models.py
from django.db import models
from django.db.models import Q

class AvailableChoiceManager(models.Manager):
    """Active choices + existing choice even if it's now inactive.
    """
    def get_queryset(self, pk=None):
        qs = super().get_queryset()  # get every possible choice
        current_choice = ChoiceModel.objects.get(pk=pk):
        return qs.filter((Q(pk=pk) | Q(active=True))
         

在您的模型定义中,您需要添加此管理器的一个实例:

    # models.ActiveChoiceModel
    AvailableChoices=AvailableChoiceManager()
    

调用:

qs = AvailableChoiceModel.AvailableChoices(pk=pk)

选项 2。

获取活动选项列表:

  choice_list = list(Choices.objects.filter(active=True).values_list('id', 'slug'))
  if fk.pk not in choice_list:      
      choice_list.insert(0, (fk.pk, fk.slug))

将小部件的选择属性设置为choice_list

self.fields['fk'].widget = forms.Select(
            choices=choice_list,
            attrs={'pk': 'lug', 'size': 7, 'style': "width:100%"})

推荐阅读