自定义Django管理员更改表单外键以包括查看记录

时间:2011-09-26 19:05:15

标签: django django-admin django-widget

在django管理员更改表单中选择外键时,我正在尝试添加一个href,可以查看添加记录的加号旁边的记录。

我试图让href渲染的是我将管理员def渲染到我自己的自定义窗口小部件文件中并将其添加到子类中并进行了子类化:

widgets.py

class RelatedFieldWidgetWrapperLink(RelatedFieldWidgetWrapper):

    def render(self, name, value, *args, **kwargs):
        rel_to = self.rel.to
        info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
        try:
            related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
        except NoReverseMatch:
            info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower())
            related_url = '%s%s/%s/add/' % info
        self.widget.choices = self.choices
        output = [self.widget.render(name, value, *args, **kwargs)]
        if self.can_add_related:
            # TODO: "id_" is hard-coded here. This should instead use the correct
            # API to determine the ID dynamically.
            output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
                (related_url, name))
            output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))
            output.append(u'<a href="%s" class="testing" id="add_id_%s" onclick="#"> ' % \        
                        (related_url, name))
        return mark_safe(u''.join(output))

和admin.py

formfield_overrides = {models.ForeignKey:{'widget':RelatedFieldWidgetWrapperLink}}

然而我得到以下错误:

类型错误 init ()至少需要4个参数(给定1个)

之前有没有人遇到过这个问题?

2 个答案:

答案 0 :(得分:9)

RelatedFieldWidgetWrapper小部件和您的子类不打算用作formfield_overrides中的小部件。 __init__方法具有不同的函数签名,因此TypeError

如果查看django.contrib.admin.options中的代码,可以看到RelatedFieldWidgetWrapper窗口小部件在模型管理员的formfield_for_dbfield方法中实例化,以便可以传递参数{{ 1}},reladmin_site

我认为您可能必须覆盖模型管理类“can_add_related方法”,并在那里使用自定义formfield_for_dbfield小部件。

RelatedFieldWidgetWrapperLink

其他方法

您可能会发现覆盖class YourModelAdmin(admin.ModelAdmin): def formfield_for_dbfield(self, db_field, **kwargs): <snip> # ForeignKey or ManyToManyFields if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)): # Combine the field kwargs with any options for formfield_overrides. # Make sure the passed in **kwargs override anything in # formfield_overrides because **kwargs is more specific, and should # always win. if db_field.__class__ in self.formfield_overrides: kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs) # Get the correct formfield. if isinstance(db_field, models.ForeignKey): formfield = self.formfield_for_foreignkey(db_field, request, **kwargs) elif isinstance(db_field, models.ManyToManyField): formfield = self.formfield_for_manytomany(db_field, request, **kwargs) # For non-raw_id fields, wrap the widget with a wrapper that adds # extra HTML -- the "add other" interface -- to the end of the # rendered output. formfield can be None if it came from a # OneToOneField with parent_link=True or a M2M intermediary. if formfield and db_field.name not in self.raw_id_fields: related_modeladmin = self.admin_site._registry.get( db_field.rel.to) can_add_related = bool(related_modeladmin and related_modeladmin.has_add_permission(request)) # use your custom widget formfield.widget = RelatedFieldWidgetWrapperLink( formfield.widget, db_field.rel, self.admin_site, can_add_related=can_add_related) return formfield <snip> 方法比formfield_for_foreignkey更清晰。

您可以将formfield_for_dbfield窗口小部件子类化,并在其渲染方法中添加链接。然后,您的自定义选择窗口小部件将由Select包装。但是,我不确定您是否可以在RelatedFieldWidgetWrapper方法的范围内生成view_url

render

答案 1 :(得分:0)

我改进了@Alasdair解决方案:

from django.contrib.admin.templatetags import admin_static
from django.core import urlresolvers
from django.utils import safestring
from django.utils.translation import ugettext_lazy as _

class LinkedSelect(widgets.Select):
    def render(self, name, value, attrs=None, *args, **kwargs):
        output = [super(LinkedSelect, self).render(name, value, attrs=attrs, *args, **kwargs)]

        model = self.choices.field.queryset.model
        try:
            obj = model.objects.get(id=value)
            change_url = urlresolvers.reverse('admin:%s_%s_change' % (obj._meta.app_label, obj._meta.object_name.lower()), args=(obj.pk,))
            output.append(u'<a href="%s" class="change-object" id="change_id_%s"> ' % (change_url, name))
            output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>' % (admin_static.static('admin/img/icon_changelink.gif'), _('Change Object')))
        except (model.DoesNotExist, urlresolvers.NoReverseMatch):
            pass

        return safestring.mark_safe(u''.join(output))

class YourModelAdmin(admin.ModelAdmin):
    formfield_overrides = {models.ForeignKey: {'widget': LinkedSelect}}

它使用与RelatedFieldWidgetWrapper相同的代码结构和样式。此外,它使用“更改”图标而不是字符串。当外键关键点无处或外键指向未定义管理接口的模型时,它会优雅地存活。