假设我在Django 1.4中有一个简单的博客应用程序:
class Post(models.Model):
title = …
published_on = …
tags = models.ManyToManyField('Tag')
class Tag(models.Model):
name = …
即。帖子有很多标签。在Django管理员上,如果我在<select multi>
tags
中包含fields
,我会得到一个不错的小PostAdmin
。是否有一种简单的方法可以在<select multi>
中添加帖子列表(简单TagAdmin
)?我尝试将fields = ['name', 'posts']
放入TagAdmin
并收到ImproperlyConfigured
错误。 (post_set
)的结果相同。
我对Django很好,所以可以提供一个合适的AdminForm和Admin对象,但我希望有一个Right Way™来做它。
答案 0 :(得分:27)
这可以使用自定义表单。
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
。
答案 1 :(得分:12)
派对有点晚了,但这是适合我的解决方案(没有魔法):
# 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
答案 2 :(得分:4)
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'])
答案 3 :(得分:0)
修改模型以添加反向字段:
# 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)
答案 4 :(得分:0)
在那边查看我的答案,以双向显示ManyToMany管理小部件:
答案 5 :(得分:0)
您可以通过这种方式添加对称的多对多过滤器。
贷方转到 https://gist.github.com/Grokzen/a64321dd69339c42a184
from django.db import models
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping, related_name='pizzas')
class Topping(models.Model):
name = models.CharField(max_length=50)
### pizza/admin.py ###
from django import forms
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin.widgets import FilteredSelectMultiple
from .models import Pizza, Topping
class PizzaAdmin(admin.ModelAdmin):
filter_horizonal = ('toppings',)
class ToppingAdminForm(forms.ModelForm):
pizzas = forms.ModelMultipleChoiceField(
queryset=Pizza.objects.all(),
required=False,
widget=FilteredSelectMultiple(
verbose_name=_('Pizzas'),
is_stacked=False
)
)
class Meta:
model = Topping
def __init__(self, *args, **kwargs):
super(ToppingAdminForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
self.fields['pizzas'].initial = self.instance.pizzas.all()
def save(self, commit=True):
topping = super(ToppingAdminForm, self).save(commit=False)
if commit:
topping.save()
if topping.pk:
topping.pizzas = self.cleaned_data['pizzas']
self.save_m2m()
return topping
class ToppingAdmin(admin.ModelAdmin):
form = ToppingAdminForm
admin.site.register(Pizza, PizzaAdmin)
admin.site.register(Topping, ToppingAdmin)