Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为Django中的多对多字段自定义相关对象_Python_Django_Django Admin_Many To Many - Fatal编程技术网

Python 为Django中的多对多字段自定义相关对象

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

有多个游戏实体,每个都可以有一个或多个平台。此外,每个游戏可以有一个或多个相关游戏的链接(使用自己的平台)。在这里,它看起来像是在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.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)