Django: «limit_choices_to» не работает на ManyToManyField - PullRequest
5 голосов
/ 20 августа 2011

Я использую Django 1.1 и не могу заставить работать опцию «limit_choices_to» для ManytoManyField.

У меня есть две модели:

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)

и

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" объекты), представленные в поле множественного выбора, это те, которые имеют значение "member" (объект User) такое же, какобъект пользователя «lock» (также объект User).

Мне показалось, что я следовал документам Django, но это не работает.Я получаю следующую ошибку:

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'} --- это работает, но бесполезно, я просто вручную задаю имя пользователя

Как я могу вместо этого получить пользователя из текущего "заблокировать "object" и отфильтровать свойство MemberPhoto "member" этим?

Спасибо всем, кто может помочь.

Kyle

Ответы [ 2 ]

11 голосов
/ 21 августа 2011

Я нашел ответ, который достигает именно того, что я хотел, по этой ссылке: 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)

Все, что вам нужно изменить, это:

  1. имя вашей модели (в приведенном выше примере это "lock")
  2. имя поля ManyToManyField в вашей модели (в приведенном выше примере это "unlocked_photos")
  3. имя связанной модели (в приведенном выше примере это «MemberPhoto»)
  4. имя поля, по которому вы хотите фильтровать связанные объекты (в приведенном выше примере это «член»)
  5. значение поля, которое вы хотите использовать для фильтрации связанных объектов (оно будет начинаться с «self.instance.», А затем будет именем поля, в приведенном выше примере это «пользователь»)
  6. И наконец, убедитесь, что имена ваших классов для пользовательской формы администратора и модели администратора совпадают.

Надеюсь, это кому-нибудь поможет!

1 голос
/ 01 августа 2013

Чтобы добавить к ответу Кайла,

Создание пользовательской формы - единственный способ настроить поле «многие ко многим» таким образом.Но, как я обнаружил, этот метод работает, только если вы изменяете экземпляр этой модели.(по крайней мере, в Django 1.5)

Это потому, что: self.instance вернет объект Model.(Я знаю сумасшедшую концепцию) Но если вы создаете экземпляр этой модели, поскольку эта модель еще не создана, self.instance вернет исключение DoesNotExist.

Чтобы обойти эту проблему, создайте две формы:

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 мы создадим отдельный набор полей для нашей формы создания:

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)

Обратите внимание, что нам пришлосьпереопределите get_form и get_fieldsets, что если obj имеет значение None (или, другими словами, если запрос должен добавить экземпляр модели), он использует MyModelCreationForm.Это тот же метод, который используют разработчики django в django.contrib.auth.admin для получения своей пользовательской формы UserCreation и наборов полей.(Посмотрите в сторону исходный код для этого примера)

Наконец, модели будут выглядеть примерно так в 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 ***

Единственная причина, по которой я включил модели, это то, что вымог видеть определение поля многие ко многим в классе MyModel.Для не нужно, чтобы имел пустое и пустое значение True.Просто помните, что если вы этого не сделаете, вам придется либо присвоить им значение по умолчанию в определении, либо установить их в функции save () в MyModelCreationForm.

Надеюсь, это поможет!(Если это совершенно неправильно, пожалуйста, поправьте меня! Мне тоже нужно учиться.)

-Спасибо

...