有游戏实体,每个游戏都可以有1个或更多平台。此外,每个游戏可以有1个或多个相关游戏的链接(使用自己的平台)。这里看起来像 models.py :
class Game(TimeStampedModel):
gid = models.CharField(max_length=38, blank=True, null=True)
name = models.CharField(max_length=512)
platforms = models.ManyToManyField(
Platform, blank=True, null=True)
...
#here is the self-referencing m2m field
related_games = models.ManyToManyField(
"self", related_name="related", blank=True)
此模型在 admin.py :
中提供此代码@admin.register(Game)
class GameAdmin(AdminImageMixin, reversion.VersionAdmin):
list_display = ("created", "name", "get_platforms"... )
list_filter = ("platforms", "year",)
#I'm interested in changing the field below
filter_horizontal = ("related_games",)
formfield_overrides = {
models.ManyToManyField: {"widget": CheckboxSelectMultiple},
}
def get_platforms(self, obj):
return ", ".join([p.name for p in obj.platforms.all()])
我需要扩展 filter_horizontal =(" related_games",)部分admin.py - 在相关游戏小部件中添加每个游戏的平台信息。它应该看起来像(游戏名称和平台列表):"虚拟战斗机(PS4,PSP,PS3)"。
该应用程序使用Django 1.7和Python 2.7
感谢您的关注。
答案 0 :(得分:1)
默认情况下,filter_horizontal
中每个项目显示的内容基于对象的__str__
或__unicode__
方法,因此您可以尝试以下内容:
class Game(TimeStampedModel):
# field definitions
# ...
def __unicode__(self):
return '{0} ({1})'.format(
self.name,
(', '.join(self.platforms.all()) if self.platforms.exists()
else 'none')
)
这将使每个游戏在列表中(以及其他任何地方)显示为"名称(平台)",例如" Crash Bandicoot(PS1,PS2)"或者"战场(无)"如果它没有任何平台
或者,如果您不想更改模型的__unicode__
方法,则需要将ModelAdmin
设置为使用自定义ModelForm
,指定related_games
字段应将自定义ModelMultipleChoiceField
与自定义FilteredSelectMultiple
小部件一起使用,您需要在其中覆盖render_options
方法。以下类应位于各自的单独文件中,但它们看起来像:
# admin.py
class GameAdmin(AdminImageMixin, reversion.VersionAdmin):
# ...
form = GameForm
# ...
# forms.py
from django import forms
class GameForm(forms.ModelForm):
related_games = RelatedGamesField()
class Meta:
fields = (
'gid',
'name',
'platforms',
'related_games',
)
# fields.py
from django.forms.models import ModelMultipleChoiceField
class RelatedGamesField(ModelMultipleChoiceField):
widget = RelatedGamesWidget()
# widgets.py
from django.contrib.admin.widgets import FilteredSelectMultiple
class RelatedGamesWidget(FilteredSelectMultiple):
def render_options(self, choices, selected_choices):
# slightly modified from Django source code
selected_choices = set(force_text(v) for v in selected_choices)
output = []
for option_value, option_label in chain(self.choices, choices):
if isinstance(option_label, (list, tuple)):
output.append(format_html(
'<optgroup label="{0}">',
# however you want to have the related games show up, eg.,
'{0} ({1})'.format(
option_value.name,
(', '.join(option_value.platforms.all())
if option_value.platforms.exists() else 'none')
)
))
for option in option_label:
output.append(self.render_option(selected_choices, *option))
output.append('</optgroup>')
else:
output.append(self.render_option(selected_choices, option_value, option_label))
return '\n'.join(output)