关于admin:Django:“limit_choices_to”不适用于ManyToManyField

Django: “limit_choices_to” doesn't work on ManyToManyField

我正在运行Django 1.1并且无法获取我的ManytoManyField的"limit_choices_to"选项。

我有两个型号:

1
2
3
4
5
6
7
8
class MemberPhoto(ImageModel):
    title       = models.CharField(_('title'), max_length=255, blank=True, null=True)
    caption     = models.CharField(_('caption'), max_length=255, blank=True, null=True)
    date_added  = models.DateTimeField(_('date added'), default=datetime.now, editable=False)
    member      = models.ForeignKey(User)

    def __unicode__(self):
        return u'%s (%s)' % (self.member.username, self.id)

1
2
3
4
5
class lock(models.Model):
    user = models.ForeignKey(User, related_name="owner")
    to_user = models.ForeignKey(User, related_name="to_user")
    unlocked_photos = models.ManyToManyField(MemberPhoto, blank=True, null=True, limit_choices_to = {'member':'user'})
    objects = locking_manager()

在第二个模型中,我想确保在Django的管理员中,在多个选择字段中呈现的唯一"unlocked_photos"("MemberPhoto"对象)是具有"成员"值(用户对象)的那些与"锁定"对象的"用户"(也是一个用户对象)。

我以为我已经遵循了Django文档,但它不起作用。我收到以下错误:

1
2
3
TemplateSyntaxError

Caught an exception while rendering: invalid input syntax for integer:"user"

我已经尝试将"limit_choices_to"改为:

limit_choices_to = {'member':user} ---不起作用

limit_choices_to = {'member__username':'kyle'} ---这可行,但它没用,我只是手动指定用户名

我怎样才能从当前的"锁定"对象中获取用户并按此过滤MemberPhoto"member"属性?

感谢任何可以提供帮助的人。

凯尔


我找到了一个答案,在这个链接上实现了我想要的:Django MTMField:limit_choices_to = other_ForeignKeyField_on_same_model ?,我在这里发布我的工作代码给任何有同样问题的人。看起来似乎"limit_choices_to"可能根本无法达到我想要的效果,并且自定义管理员使用的表单是要走的路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.contrib import admin
from django import forms
from gayhop.apps.locking.models import lock
from gayhop.apps.photos.models import MemberPhoto

class LockAdminForm(forms.ModelForm):
  class Meta:
    model = lock

  def __init__(self, *args, **kwargs):
    super(LockAdminForm, self).__init__(*args, **kwargs)
    self.fields['unlocked_photos'].queryset = MemberPhoto.objects.filter(member=self.instance.user)


class LockAdmin(admin.ModelAdmin):
  form = LockAdminForm
  filter_horizontal = ('unlocked_photos',)

django.contrib.admin.site.register(lock, LockAdmin)

你需要改变的是:

  • 您的模型的名称(在上面的示例中它是"锁定")
  • 模型中ManyToManyField字段的名称(在上面的示例中为"unlocked_photos")
  • 相关模型的名称(在上面的例子中是"MemberPhoto")
  • 要过滤相关对象的字段的名称(在上面的示例中为"成员")
  • 要用于过滤相关对象的字段的值(它将以"self.instance。"开头,然后是字段的名称,在上面的示例中为"user")
  • 最后确保自定义管理表单和管理模型的类名都匹配。
  • 希望这有助于某人!


    要添加凯尔的答案,

    创建自定义表单是以这种方式自定义多对多字段的唯一方法。但是,就像我发现的那样,只有在更改该模型的实例时,该方法才有效。 (至少在Django 1.5中)

    这是因为:self.instance将返回Model对象。 (我知道疯狂的概念)
    但是如果要创建该模型的实例,由于尚未创建该模型,self.instance将返回DoesNotExist异常。

    解决此问题的方法是创建两种形式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class MyModelChangeForm(forms.ModelForm):
        class Meta:
            model = MyModel

        def __init__(self, *args, **kwargs):
            super(MyModelChangeForm, self).__init__(*args, **kwargs)
            my_model = self.instance
            self.fields['fields'].queryset = OtherRelatedModel.objects.filter(other_id=my_model.other)


    class MyModelCreationForm(forms.ModelForm):
        class Meta:
            model = MyModel

        def save(self, commit=True):
            my_model = super(MyModelCreationForm, self).save(commit=False)
            *** Do other things with the my_model if you want ***
            my_model.save()
            return my_model

    然后在admin.py中我们将为我们的创建表单创建一个单独的字段集:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    class MyModelAdmin(admin.ModelAdmin):
        filter_horizontal = ('other')
        list_display = ('field1', 'field2' 'field3')

        fieldsets = (
            ("Model info:", {'fields': ("field1","field2","field3")}),
            ("More Model info:", {'fields': ("other",)}),
        )
        add_fieldsets = (
            ("Initial info:", {'fields': ("field1","field2","field3")}),
        )

        form = MyModelChangeForm
        add_form = MyModelCreationForm

        def get_fieldsets(self, request, obj=None):
            if not obj:
                return self.add_fieldsets
            return super(MyModelAdmin, self).get_fieldsets(request, obj)

        def get_form(self, request, obj=None, **kwargs):
           """
            Use special form during MyModel creation
           """
            defaults = {}
            if obj is None:
                defaults.update({
                    'form': self.add_form,
                    'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
                })
            defaults.update(kwargs)
            return super(MyModelAdmin, self).get_form(request, obj, **defaults)

    请注意,如果obj为None(或者换句话说,如果请求是添加模型的实例),我们必须覆盖get_form和get_fieldsets,它使用MyModelCreationForm。这与django开发人员在django.contrib.auth.admin中使用的方法相同,以获取其自定义UserCreation表单和字段集。 (在那里查看该示例的源代码)

    最后,模型在model.py中看起来像这样:

    1
    2
    3
    4
    5
    6
    7
    class MyModel(models.Model):
        *** field definitions ***
        other = models.ManytoManyField(OtherRelatedModel, null=True, blank=True)

    class OtherRelatedModel(models.Model):
        other_id = model.AutoField(primary_key=True)
        *** more field definitions ***

    我包含模型的唯一原因是,您可以在MyModel类中看到多对多的字段定义。它不必将null和空白设置为True。请记住,如果不这样做,则必须在定义中为它们分配默认值,或者在MyModelCreationForm中的save()函数中设置它们。

    希望这可以帮助! (如果完全错了,请纠正我!我也需要学习。)

    -谢谢