动态设置ModelMultipleChoiceField的queryset为自定义记录集

1 投票
3 回答
1747 浏览
提问于 2025-04-15 22:47

我看到很多关于如何设置ModelMultipleChoiceField来使用自定义查询集的教程,我试过了,它们都能用。不过,它们都用的是同一种方法:查询集只是同一类对象的一个过滤列表。

在我的情况下,我想让后台管理界面显示一个多选表单,而不是用用户名作为文本部分,我想用我账户类中的name字段。

这是我目前的代码:

# models.py
class Account(models.Model):
    name = models.CharField(max_length=128,help_text="A display name that people understand")
    user = models.ForeignKey(User, unique=True) # Tied to the User class in settings.py

class Organisation(models.Model):
    administrators = models.ManyToManyField(User)


# admin.py
from django.forms import ModelMultipleChoiceField
from django.contrib.auth.models import User

class OrganisationAdminForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        from ethico.accounts.models import Account
        self.base_fields["administrators"] = ModelMultipleChoiceField(
            queryset=User.objects.all(),
            required=False
        )
        super(OrganisationAdminForm, self).__init__(*args, **kwargs)

class Meta:
    model = Organisation

这个可以工作,但我想让上面的queryset显示一个选择框,里面的内容是Account.name属性和User.id属性。可是这样做没有成功:

queryset=Account.objects.all().order_by("name").values_list("user","name")

结果出现了这个错误:

'tuple' object has no attribute 'pk'

我本以为这很简单,但结果花了我几个小时都没找到解决办法。有人能帮我解答一下吗?

3 个回答

0

当你使用查询集时,它需要是一个查询集(QuerySet),而使用values_list会得到一个列表,所以这样是行不通的。

如果你想改变模型的默认显示方式,只需要重写一下__unicode__这个方法。你可以查看这个链接了解更多信息:http://docs.djangoproject.com/en/dev/ref/models/instances/#unicode

举个例子:

def __unicode__(self):
    return u"%s for %s" % (self.name, self.user)

每当你要求Django打印一个模型时,它都会使用__unicode__这个方法。你可以在命令行中加载一个模型,然后用print my_instance来测试一下。

0

我从sebpiq那里得到了灵感,终于搞明白了:

class OrganisationAdminForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):

        from django.forms import MultipleChoiceField
        from ethico.accounts.models import Account

        self.base_fields["administrators"] = MultipleChoiceField(
            choices=tuple([(a.user_id, a.name) for a in Account.objects.all().order_by("name")]),
            widget=forms.widgets.SelectMultiple,
            required=False
        )

        super(OrganisationAdminForm, self).__init__(*args, **kwargs)

    class Meta:
        model = Organisation


class OrganisationAdmin(admin.ModelAdmin):
    form = OrganisationAdminForm


admin.site.register(Organisation, OrganisationAdmin)

关键在于完全放弃使用查询集(queryset)。一旦我使用了固定的choices=参数,所有的东西就都正常运作了。谢谢大家!

1

你可以使用一个自定义的小部件,重写它的 render 方法。下面是我为一个文本框做的:

class UserToAccount(forms.widgets.TextInput):
    def render(self, name, value, attrs=None):
        if isinstance(value, User) :
            value = Account.objects.get(user=value).name
        return super (UserToAccount, self).render(name, value, attrs=None)        

然后,当然要在你的管理字段中使用 widget 参数,这样才能用上你自定义的小部件。我不确定这个方法是否可以用在 select 上,但你可以试试看。

撰写回答