Ответ 1
Решение для ManyToManyField: http://code.djangoproject.com/ticket/13369#comment:2
Администратор Django с радостью поддерживает отношения "многие-к-одному" и "многие-ко-многим" через HTML <SELECT> поле формы, позволяющее выбрать один или несколько вариантов соответственно. Там даже хороший виджет Javascript filter_horizontal
, чтобы помочь.
Я пытаюсь сделать то же самое со стороны "один-ко-многим" через related_name. Я не вижу, насколько сильно он отличается от многих-ко многим, отображая его в форме, мне просто нужен список SELECT с несколькими выборами. Но я не могу просто добавить значение related_name
в список полей ModelAdmin
-derived.
Поддерживает ли Django одно-много полей таким образом?
Моя модель Django что-то вроде этого (ухитрилась упростить пример):
class Person(models.Model):
...
manager = models.ForeignKey('self', related_name='staff',
null=True, blank=True, )
На странице администрирования Person я могу легко получить <SELECT> список всех возможных сотрудников, чтобы выбрать этого менеджера. Я также хочу отобразить множественный выбор <SELECT> список всех сотрудников менеджера.
Я не хочу использовать встроенные строки, так как я не хочу редактировать данные подчиненных; Я хочу иметь возможность добавлять/удалять людей из списка.
(Я пытаюсь использовать django-ajax-selects для замены виджета SELECT, но это by-by.)
Решение для ManyToManyField: http://code.djangoproject.com/ticket/13369#comment:2
вы можете установить начальные значения в методе init формы.
Обратитесь к ним как self.fields ['manager_staff']. initial.
Так же:
class PersonAdminForm(forms.ModelForm):
manager_staff = forms.ModelMultipleChoiceField(
queryset=Person.objects.all(),
required=False,
)
def __init__(self, *args, **kwargs):
super(PersonAdminForm, self).__init__(*args, **kwargs)
if self.instance.id is not None:
selected_items = [ values[0] for values in Person.objects.filter(
#whatever your filter criteria is
) ]
self.fields['manager_staff'].initial = selected_items
Это может оказаться одним из этих ограничений. Вот что мне удалось придумать. Можно создать пользовательскую форму и передать ее сайту администратора, который будет использоваться для рендеринга страницы. Однако то, что я не мог получить, состояло в том, чтобы правильно правильно отобразить начальное значение. Возможно, кто-то лучше в Формах может прийти с некоторым API-интерфейсом uber-cool-secret-meta, о котором я не знаю и не знаю.
class Person(models.Model):
# ...
manager = models.ForeignKey('self', related_name='staff', null=True, blank=True)
def manager_staff(self):
return self.manager.staff.all()
class PersonAdminForm(forms.ModelForm):
manager_staff = forms.ModelMultipleChoiceField(
initial='manager_staff', # Can't get this to work
queryset=Person.objects.all(),
required=False,
)
class Meta:
model = Person
class PersonAdmin(admin.ModelAdmin):
form = PersonAdminForm
def save_model(self, request, obj, form, change):
for id in form.data.getlist('manager_staff'):
# This is kind of a weak way to do this, but you get the idea...
p = Person.objects.get(id=id)
p.manager = obj.manager
p.save()
super(PersonAdmin, self).save_model(request, obj, form, change)
admin.site.register(Person, PersonAdmin)
Вот, что я придумал, основываясь на ответах Т. Стоуна и никохечаниза. Исходные данные предоставляются во многом так же, как это делает nicoechaniz. Для экономии вы должны быть осторожны, имеете дело со случаем совершенно нового Человека, а также для редактирования существующего Лица. То, что делает метод save()
сложным, в зависимости от неопубликованных частей API ModelForm. Действительно, это должно быть встроено в Django. Я предполагаю, что это обычная функция.
class PersonAdminForm(forms.ModelForm):
staff = forms.ModelMultipleChoiceField(
queryset=Person.objects.all(),
required=False,
)
class Meta:
model = Person
def __init__(self, *args, **kwargs):
super(PersonAdminForm, self).__init__(*args,**kwargs)
if self.instance.pk is not None:
self.initial['staff'] = [values[0] for values in self.instance.staff.values_list('pk')]
def save(self, commit=True):
instance = super(PersonAdminForm, self).save(commit)
def save_m2m():
instance.staff = self.cleaned_data['staff']
if commit:
save_m2m()
elif hasattr(self, 'save_m2m'):
save_old_m2m = self.save_m2m
def save_both():
save_old_m2m()
save_m2m()
self.save_m2m = save_both
else:
self.save_m2m = save_m2m
return instance
save.alters_data = True
class PersonAdmin(admin.ModelAdmin):
form = PersonAdminForm