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

Cra*_*oli 3 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))
Run Code Online (Sandbox Code Playgroud)

并在admin.py

formfield_overrides = {models.ForeignKey:{'widget':RelatedFieldWidgetWrapperLink}}
Run Code Online (Sandbox Code Playgroud)

但是我得到以下错误:

TypeError init()至少需要4个参数(给定1个)

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

Ala*_*air 9

RelatedFieldWidgetWrapper窗口小部件,和你的子类,是注定不会被用作小部件formfield_overrides.这些__init__方法具有不同的功能签名,因此TypeError.

如果查看代码django.contrib.admin.options,可以看到RelatedFieldWidgetWrapper小部件在模型管理员的formfield_for_dbfield方法中实例化,以便可以传递参数rel,admin_sitecan_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>
Run Code Online (Sandbox Code Playgroud)

其他方法

您可能会发现覆盖formfield_for_foreignkey方法比干净更干净formfield_for_dbfield.

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

from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.forms.widgets import Select

def get_admin_change_url(obj):
    ct = ContentType.objects.get_for_model(obj)
    change_url_name = 'admin:%s_%s_change' % (ct.app_label, ct.model)
    return reverse(change_url_name, args=(obj.id,))

class LinkedSelect(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:
            id = int(value)
            obj = model.objects.get(id=id)
            view_url = get_admin_change_url(obj)
            output += mark_safe('&nbsp;<a href="%s" target="_blank">view</a>&nbsp;' % (view_url,))
        except model.DoesNotExist:
            pass
        return output

class YourModelAdmin(admin.ModelAdmin):
    formfield_overrides = {models.ForeignKey:{'widget':LinkedSelect}}
Run Code Online (Sandbox Code Playgroud)