Может ли администратор Django обрабатывать отношения "один ко многим" через related_name?

Администратор 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.)

Ответ 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

Ответ 3

Это может оказаться одним из этих ограничений. Вот что мне удалось придумать. Можно создать пользовательскую форму и передать ее сайту администратора, который будет использоваться для рендеринга страницы. Однако то, что я не мог получить, состояло в том, чтобы правильно правильно отобразить начальное значение. Возможно, кто-то лучше в Формах может прийти с некоторым API-интерфейсом uber-cool-secret-meta, о котором я не знаю и не знаю.

models.py

class Person(models.Model):
    # ...
    manager = models.ForeignKey('self', related_name='staff', null=True, blank=True)

    def manager_staff(self):
        return self.manager.staff.all()

admin.py

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)

Ответ 4

Вот, что я придумал, основываясь на ответах Т. Стоуна и никохечаниза. Исходные данные предоставляются во многом так же, как это делает 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