Настройте Django Admin Change Form Foreignkey, чтобы включить View View - PullRequest
3 голосов
/ 26 сентября 2011

При выборе иностранного ключа в форме смены администратора django я пытаюсь добавить ссылку, которая может просматривать запись рядом с плюсом, который добавляет запись.

То, что я пытался просто получить ссылкудля рендеринга я скопировал админский рендер 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}}

однако я получаю следующую ошибку:

TypeError init () принимает как минимум 4 аргумента (1 дано)

Кто-нибудь сталкивался с этимпроблема раньше?

Ответы [ 2 ]

9 голосов
/ 27 сентября 2011

Виджет RelatedFieldWidgetWrapper и ваш подкласс не предназначены для использования в качестве виджета в formfield_overrides.Методы __init__ имеют разные сигнатуры функций, поэтому TypeError.

Если вы посмотрите на код в django.contrib.admin.options, вы увидите, что создан экземпляр виджета RelatedFieldWidgetWrapperв методе администратора модели formfield_for_dbfield, так что ему могут быть переданы аргументы rel, admin_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.

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}}
0 голосов
/ 26 февраля 2013

Я немного улучшил решение @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.Кроме того, он использует значок «изменить» вместо просто строки.Он изящно выживает, когда внешний ключ нигде не указывает или когда внешний ключ указывает на модель, для которой не определен интерфейс администратора.

...