Добавление 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 ™.
Ответы
Ответ 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/...