Django: "limit_choices_to" не работает на ManyToManyField
Я использую 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) такое же, как " заблокировать "пользователя" объекта (также объект "Пользователь").
Я думал, что следовал документации 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'} --- это работает, но это бесполезно, я просто вручную задаю имя пользователя
Как я могу вместо этого получить пользователя из текущего объекта "lock" и отфильтровать свойство Member "Photo" этим образом?
Спасибо всем, кто может помочь.
рукав моря
3 ответа
Я нашел ответ, который достигает именно того, что я хотел, по этой ссылке: 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)
Все, что вам нужно изменить, это:
- название вашей модели (в приведенном выше примере это "замок")
- имя поля ManyToManyField в вашей модели (в приведенном выше примере это "unlocked_photos")
- название связанной модели (в приведенном выше примере это "MemberPhoto")
- имя поля, по которому вы хотите фильтровать связанные объекты (в приведенном выше примере это "member")
- значение поля, которое вы хотите использовать для фильтрации связанных объектов (оно будет начинаться с "self.instance.", а затем будет именем поля, в приведенном выше примере это "пользователь")
- И наконец, убедитесь, что имена ваших классов для пользовательской формы администратора и модели администратора совпадают.
Надеюсь, это кому-нибудь поможет!
Чтобы добавить к ответу Кайла,
Создание пользовательской формы - единственный способ настроить поле "многие ко многим" таким образом. Но, как я обнаружил, этот метод работает, только если вы меняете экземпляр этой модели. (по крайней мере, в 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.
Надеюсь это поможет! (Если это совершенно неправильно, пожалуйста, поправьте меня! Мне тоже нужно учиться.)
-Спасибо
Я потратил несколько часов, пытаясь заставить это работать для Django 3. Вставив это здесь для других людей Django. Часть, которая нуждалась в обновлении, была частью "def init ".
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['unlocked_photos'].queryset = MemberPhoto.objects.filter(member=self.instance.user)