Как получить доступ к обоим направлениям ManyToManyField в Django Admin? - PullRequest
11 голосов
/ 30 ноября 2010

Настройка администратора Django filter_horizontal предоставляет хороший виджет для редактирования отношения многие ко многим. Но это особая настройка, которая требует список полей, поэтому она доступна только в модели (admin для), которая определяет ManyToManyField; Как я могу получить тот же виджет (* для администратора) другая модель, читая отношения в обратном направлении?

Мои модели выглядят так (не стесняйтесь игнорировать сложность User / UserProfile; хотя это реальный вариант использования):

class Site(models.Model):
    pass
class UserProfile(models.Model):
    user = models.OneToOneField(to=User,unique=True)
    sites = models.ManyToManyField(Site,blank=True)

Я могу получить красивый виджет в форме администратора для UserProfile с

filter_horizontal = ('sites',)

но не вижу, как получить эквивалент на Site admin.

Я также могу получить неполный путь, добавив строку к SiteAdmin, определенную как:

class SiteAccessInline(admin_module.TabularInline):
    model = UserProfile.sites.through

Это окольный и неудобный, хотя; виджет совсем не интуитивен для простого управления отношением «многие ко многим».

Наконец, здесь описан трюк , который включает определение другого ManyToManyField на Site и проверку того, что он указывает на ту же таблицу базы данных (и прыжок через некоторые обручи, потому что Django на самом деле не предназначен иметь разные поля на разных моделях, описывающих одни и те же данные). Я надеюсь, что кто-то может показать мне что-нибудь почище.

1 Ответ

7 голосов
/ 01 декабря 2010

Вот (более или менее) аккуратное решение, благодаря http://blog.abiss.gr/mgogoulos/entry/many_to_many_relationships_and и исправлению ошибки Django, взятой из http://code.djangoproject.com/ticket/5247

from django.contrib import admin as admin_module

class SiteForm(ModelForm):
    user_profiles = forms.ModelMultipleChoiceField(
        label='Users granted access',
        queryset=UserProfile.objects.all(),
        required=False,
        help_text='Admin users (who can access everything) not listed separately',
        widget=admin_module.widgets.FilteredSelectMultiple('user profiles', False))

class SiteAdmin(admin_module.ModelAdmin):
    fields = ('user_profiles',)

    def save_model(self, request, obj, form, change):
        # save without m2m field (can't save them until obj has id)
        super(SiteAdmin, self).save_model(request, obj, form, change) 
        # if that worked, deal with m2m field
        obj.user_profiles.clear()
        for user_profile in form.cleaned_data['user_profiles']:
             obj.user_profiles.add(user_profile)

    def get_form(self, request, obj=None, **kwargs):
        if obj:
            self.form.base_fields['user_profiles'].initial = [ o.pk for o in obj.userprofile_set.all() ]
        else:
            self.form.base_fields['user_profiles'].initial = []
        return super(SiteAdmin, self).get_form(request, obj, **kwargs)

При этом используется тот же виджет, что иfilter_horizontal настройка, но жестко запрограммирована в форме.

...