Python 为Django中的多对多字段自定义相关对象
有多个游戏实体,每个都可以有一个或多个平台。此外,每个游戏可以有一个或多个相关游戏的链接(使用自己的平台)。在这里,它看起来像是在models.py中:Python 为Django中的多对多字段自定义相关对象,python,django,django-admin,many-to-many,Python,Django,Django Admin,Many To Many,有多个游戏实体,每个都可以有一个或多个平台。此外,每个游戏可以有一个或多个相关游戏的链接(使用自己的平台)。在这里,它看起来像是在models.py中: class Game(TimeStampedModel): gid = models.CharField(max_length=38, blank=True, null=True) name = models.CharField(max_length=512) platforms = models.ManyToManyF
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.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()])
此模型在admin.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.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()])
我需要扩展admin.py的filter\u horizontal=(“related\u games”),以在related games小部件中添加每个游戏的平台信息。它应该看起来像(游戏名称和平台列表):“虚拟战斗机(PS4,PSP,PS3)”
该应用程序使用Django 1.7和Python 2.7
谢谢您的关注。默认情况下,水平
过滤器中每个项目显示的内容基于对象的\uuuu str\uuu
或\uuu unicode\uuu
方法,因此您可以尝试以下操作:
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)”或“Battley(none)”(如果没有任何平台)
或者,如果您不想更改模型的\uuuuunicode\uuuuu
方法,则需要将ModelAdmin
设置为使用自定义ModelForm
,指定相关的\u游戏
字段应使用自定义的modelmultipechoicefield
和自定义的FilteredSelectMultiple
小部件,您需要在其中覆盖render\u选项
方法。以下类应位于各自的单独文件中,但看起来类似:
# 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)
#admin.py
类GameAdmin(AdminImageMixin,reversion.VersionAdmin):
# ...
形式=游戏形式
# ...
#forms.py
来自django导入表单
类游戏形式(forms.ModelForm):
相关游戏=相关游戏场()
类元:
字段=(
“gid”,
“姓名”,
"平台",,
“相关游戏”,
)
#fields.py
从django.forms.models导入ModelMultipleChiiceField
类相关游戏字段(ModelMultipleChiceField):
widget=RelatedGamesWidget()
#widgets.py
从django.contrib.admin.widgets导入FilteredSelectMultiple
类相关GameSwidget(FilteredSelectMultiple):
def渲染选项(自身、选项、选定选项):
#从Django源代码稍加修改
所选的_选项=设置(在所选的_选项中为v强制_文本(v))
输出=[]
对于选项值,选项标签位于链中(self.choices,choices):
如果isinstance(选项_标签,(列表,元组)):
output.append(html格式)(
'',
#但是你想让相关的游戏出现,例如。,
“{0}({1})”格式(
选项_value.name,
(“,”.join(option_value.platforms.all())
如果选项_value.platforms.exists(),则为“无”)
)
))
对于选项标签中的选项:
output.append(self.render_选项(选定的_选项,*选项))
output.append(“”)
其他:
output.append(self.render_选项(所选_选项、选项_值、选项_标签))
返回'\n'。加入(输出)
如果这些功能只在管理部分需要,并且只与水平过滤器小部件相关,该怎么办?我是这个项目的新手,我想,更改unicode部分可能会破坏一些东西。我对答案进行了编辑,以包含一个替代选项,而不是覆盖\uuuuuuunicode\uuuuuu
,尽管它肯定没有unicode的第一个变体那么简洁。Exception连接方法需要字符串,对象除外。所以我是这样做的:'.join([unicode(p)表示self.platforms.all()中的p)