Django:“limit_choices_to”在ManyToManyField上不起作用

时间:2021-10-02 03:05:06

I am running Django 1.1 and cannot get the "limit_choices_to" option for my ManytoManyField to work.

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

I have two models:

我有两个型号:

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)

and

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()

in the second model, i want to make sure in Django's admin that the only "unlocked_photos" ("MemberPhoto" objects) presented in the multiple select field are those who have a "member" value (a User object) the same as the "lock" object's "user" (also a User object).

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

I thought I had followed the Django docs on this, but it doesn't work. I get the following error:

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

TemplateSyntaxError

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

I've tried changing the "limit_choices_to" to things like:

我已经尝试将“limit_choices_to”改为:

limit_choices_to = {'member': user} --- Doesnt work

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

limit_choices_to = {'member__username':'kyle'} --- this DOES work but it's useless, i'm just manually specifying a username

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

How can I instead get the user from the current "lock" object and filter the MemberPhoto "member" property by that?

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

Thanks to anybody who can help.

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

Kyle

2 个解决方案

#1


11  

I found an answer that achieves exactly what I wanted at this link: Django MTMField: limit_choices_to = other_ForeignKeyField_on_same_model?, and I'm posting my working code here for anybody having the same problem. It seems from looking around that "limit_choices_to" may simply not be able to achieve what I wanted, and that customizing the form used by the admin is the way to go:

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

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)

All you have to change is:

你需要改变的是:

  1. the name of your model (in the above example it's "lock")
  2. 您的模型的名称(在上面的示例中它是“锁定”)

  3. the name of the ManyToManyField field in your model (in the above example it's "unlocked_photos")
  4. 模型中ManyToManyField字段的名称(在上面的示例中为“unlocked_photos”)

  5. the name of the related model (in the above example it's "MemberPhoto")
  6. 相关模型的名称(在上面的例子中是“MemberPhoto”)

  7. the name of the field you want to filter related objects by (in the above example it's "member")
  8. 要过滤相关对象的字段的名称(在上面的示例中为“成员”)

  9. the value for the field you want to use to filter related objects by (it will start with "self.instance." and then be the name of the field, in the above example it's "user")
  10. 要用于过滤相关对象的字段的值(它将以“self.instance。”开头,然后是字段的名称,在上面的示例中为“user”)

  11. And finally make sure your class names for the custom admin form and admin model all match up.
  12. 最后确保自定义管理表单和管理模型的类名都匹配。

Hope this helps somebody!

希望这有助于某人!

#2


1  

To add to Kyle's answer,

要添加凯尔的答案,

Creating a custom form is the only way for to customize a many to many field in that way. But, like I discovered, that method only works if you are changing an instance of that model. (at least in Django 1.5)

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

This is because: self.instance will return Model object. (I know crazy concept) But if you are creating an instance of that model, since that model hasn't been created yet, the self.instance will return a DoesNotExist exception.

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

A way around this issue is create two forms:

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

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

Then inside admin.py we would create a separate fieldset for our creation form:

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

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)

Note that we had to override the get_form and get_fieldsets that if the obj is None (or in other words if the request is to add an instance of the model) it uses the MyModelCreationForm. This is the same method that the django developers use in django.contrib.auth.admin to get their custom UserCreation form and fieldsets. (Look in side the source code there for that example)

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

Finally the models would look something like this in model.py:

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

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 ***

The only reason I included the models is so you could see the many to many field definition in class MyModel. It doesn't have to have null and blank set to True. Just remember if you don't you will have to either assign them a default value in the definition or set them in the save() function in the MyModelCreationForm.

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

Hope this helps! (If it's utterly wrong please correct me! I need to learn too.)

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

-Thanks

#1


11  

I found an answer that achieves exactly what I wanted at this link: Django MTMField: limit_choices_to = other_ForeignKeyField_on_same_model?, and I'm posting my working code here for anybody having the same problem. It seems from looking around that "limit_choices_to" may simply not be able to achieve what I wanted, and that customizing the form used by the admin is the way to go:

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

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)

All you have to change is:

你需要改变的是:

  1. the name of your model (in the above example it's "lock")
  2. 您的模型的名称(在上面的示例中它是“锁定”)

  3. the name of the ManyToManyField field in your model (in the above example it's "unlocked_photos")
  4. 模型中ManyToManyField字段的名称(在上面的示例中为“unlocked_photos”)

  5. the name of the related model (in the above example it's "MemberPhoto")
  6. 相关模型的名称(在上面的例子中是“MemberPhoto”)

  7. the name of the field you want to filter related objects by (in the above example it's "member")
  8. 要过滤相关对象的字段的名称(在上面的示例中为“成员”)

  9. the value for the field you want to use to filter related objects by (it will start with "self.instance." and then be the name of the field, in the above example it's "user")
  10. 要用于过滤相关对象的字段的值(它将以“self.instance。”开头,然后是字段的名称,在上面的示例中为“user”)

  11. And finally make sure your class names for the custom admin form and admin model all match up.
  12. 最后确保自定义管理表单和管理模型的类名都匹配。

Hope this helps somebody!

希望这有助于某人!

#2


1  

To add to Kyle's answer,

要添加凯尔的答案,

Creating a custom form is the only way for to customize a many to many field in that way. But, like I discovered, that method only works if you are changing an instance of that model. (at least in Django 1.5)

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

This is because: self.instance will return Model object. (I know crazy concept) But if you are creating an instance of that model, since that model hasn't been created yet, the self.instance will return a DoesNotExist exception.

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

A way around this issue is create two forms:

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

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

Then inside admin.py we would create a separate fieldset for our creation form:

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

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)

Note that we had to override the get_form and get_fieldsets that if the obj is None (or in other words if the request is to add an instance of the model) it uses the MyModelCreationForm. This is the same method that the django developers use in django.contrib.auth.admin to get their custom UserCreation form and fieldsets. (Look in side the source code there for that example)

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

Finally the models would look something like this in model.py:

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

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 ***

The only reason I included the models is so you could see the many to many field definition in class MyModel. It doesn't have to have null and blank set to True. Just remember if you don't you will have to either assign them a default value in the definition or set them in the save() function in the MyModelCreationForm.

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

Hope this helps! (If it's utterly wrong please correct me! I need to learn too.)

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

-Thanks