3

django admin 変更フォームで外部キーを選択すると、レコードを追加するプラスの横にレコードを表示できる href を追加しようとしています。

href をレンダリングするためだけに試みたのは、admins def render を自分のカスタム ウィジェット ファイルにコピーし、それを追加してサブクラス化したことです。

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 つ指定)

以前にこの問題に遭遇した人はいますか?

4

2 に答える 2

9

RelatedFieldWidgetWrapperウィジェットとそのサブクラスは、 でウィジェットとして使用するためのものではありませんformfield_overrides__init__メソッドには異なる関数シグネチャがあるため、TypeError.

のコードをdjango.contrib.admin.options見ると、RelatedFieldWidgetWrapperウィジェットがモデル admin の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

ウィジェットをサブクラス化し、そのSelectrender メソッドにリンクを追加できる場合があります。カスタム選択ウィジェットは、 でラップされますRelatedFieldWidgetWrapperview_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}}
于 2011-09-27T00:27:39.207 に答える
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ます。さらに、単なる文字列の代わりに「変更」アイコンを使用します。外部キーがどこにも指していない場合、または外部キーが管理インターフェースが定義されていないモデルを指している場合でも、正常に存続します。

于 2013-02-26T02:28:04.483 に答える