Подтвердить что ты не робот

Добавление ManyToManyWidget в обратную сторону ManyToManyField в Django Admin

Скажем, у меня есть простое приложение для блога в Django 1.4:

class Post(models.Model):
    title = …
    published_on = …
    tags = models.ManyToManyField('Tag')

class Tag(models.Model):
    name = …

то есть. сообщение имеет много тегов. В admin Django я получаю приятный маленький <select multi>, если я включаю tags в fields для PostAdmin. Есть ли простой способ включить список сообщений (как простой <select multi>) в TagAdmin? Я попытался положить fields = ['name', 'posts'] в TagAdmin и получил ошибку ImproperlyConfigured. (тот же результат для post_set).

Я в порядке с Django, так что могу взломать правильный объект AdminForm и Admin, но я надеюсь, что для этого нужно Right Way ™.

4b9b3361

Ответ 1

Это можно сделать с пользовательской формой.

from django.contrib import admin
from django import forms

from models import Post, Tag

class PostAdminForm(forms.ModelForm):
    tags = forms.ModelMultipleChoiceField(
        Tag.objects.all(),
        widget=admin.widgets.FilteredSelectMultiple('Tags', False),
        required=False,
    )

    def __init__(self, *args, **kwargs):
        super(PostAdminForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.initial['tags'] = self.instance.tags.values_list('pk', flat=True)

    def save(self, *args, **kwargs):
        instance = super(PostAdminForm, self).save(*args, **kwargs)
        if instance.pk:
            instance.tags.clear()
            instance.tags.add(*self.cleaned_data['tags'])
        return instance

class PostAdmin(admin.ModelAdmin):
    form = PostAdminForm

admin.site.register(Post, PostAdmin)

В этом случае False можно заменить на True, если вы хотите вертикально укладывать виджет.

Ответ 2

Немного поздно для вечеринки, но это решение, которое работает для меня (без магии):

# admin.py

from django.contrib import admin
from models import Post

class TagPostInline(admin.TabularInline):
    model = Post.tags.through
    extra = 1

class PostAdmin(admin.ModelAdmin):
    inlines = [TagPostInline]

admin.site.register(Post, PostAdmin)

Ссылка: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models

Ответ 3

Решение Matthew не работало для меня (Django 1.7) при создании новой записи, поэтому мне пришлось немного изменить ее. Надеюсь, это полезно для кого-то:)

class PortfolioCategoriesForm(forms.ModelForm):
    items = forms.ModelMultipleChoiceField(
        PortfolioItem.objects.all(),
        widget=admin.widgets.FilteredSelectMultiple('Portfolio items', False),
        required=False
    )

    def __init__(self, *args, **kwargs):
        super(PortfolioCategoriesForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            initial_items = self.instance.items.values_list('pk', flat=True)
            self.initial['items'] = initial_items

    def save(self, *args, **kwargs):
        kwargs['commit'] = True
        return super(PortfolioCategoriesForm, self).save(*args, **kwargs)

    def save_m2m(self):
        self.instance.items.clear()
        self.instance.items.add(*self.cleaned_data['items'])

Ответ 4

Измените свои модели, чтобы добавить обратное поле:

# models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    published_on = models.DateTimeField()
    tags = models.ManyToManyField('Tag')

class Tag(models.Model):
    name = models.CharField(max_length=10)
    posts = models.ManyToManyField('blog.Post', through='blog.post_tags')

Затем стандартным образом добавьте поле в ModelAdmin:

#admin.py
from django.contrib import admin

class TagAdmin(admin.ModelAdmin):
    list_filter = ('posts', )

admin.site.register(Tag, TagAdmin)

Ответ 5

Проверьте мой ответ там для отображения виджета админки ManyToMany в обоих направлениях:

fooobar.com/info/14972688/...