如何在Django Admin中访问ManyToManyField的两个方向?

时间:2010-11-30 17:30:22

标签: django django-admin many-to-many

Django admin filter_horizontal设置为编辑多对多关系提供了一个很好的小部件。但这是一个特殊的设置,需要一个字段列表,所以它只能在定义ManyToManyField的(admin for the)模型上使用;如何在(管理员)其他模型上获取相同的小部件,向后阅读关系?

我的模型看起来像这样(可以忽略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

虽然它是环形的但是不方便;对于简单地管理多对多关系,小部件根本不直观。

最后,有一个技巧described here涉及在ManyToManyField上定义另一个Site并确保它指向同一个数据库表(并跳过一些箍,因为Django不是真的旨在在描述相同数据的不同模型上具有不同的字段。我希望有人能给我看清楚的东西。

1 个答案:

答案 0 :(得分:7)

这是一个(或多或少)整洁的解决方案,感谢http://blog.abiss.gr/mgogoulos/entry/many_to_many_relationships_and并修复了从http://code.djangoproject.com/ticket/5247获取的Django错误

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设置相同的小部件,但硬编码到表单中。