Django InlineModelAdmin: частично показать встроенную модель и ссылку на полную модель - PullRequest
32 голосов
/ 23 января 2010

Я определил несколько моделей: журналы, тома, volume_scanInfo и т. Д.

Журнал может иметь больше томов, а том может иметь больше scanInfo.

Что я хочу сделать, это:

  • на странице администратора журналов. Я хочу, чтобы список томов был встроен (сделано)
  • подключите каждый том предыдущего списка к его странице администратора, где я могу показать форму для редактирования тома и список встроенных «сканирующих данных».

так что я хочу что-то вроде:

Journal #1 admin page
[name]
[publisher]
[url]
.....
list of volumes inline
    [volume 10] [..(other fields)..]   <a href="/link/to/volume/10">Full record</a>
    [volume 20] [..(other fields)..]   <a href="/link/to/volume/20">Full record</a>

Тогда

Volume #20 admin page
[volume number]
[..(other fields)...]
......
list of the scan info inline
    [scan info 33] [..(other fields)..]   <a href="/link/to/scaninfo/33">Full record</a>
    [scan info 44] [..(other fields)..]   <a href="/link/to/scaninfo/44">Full record</a>

То, что я пытался сделать, - это определить метод модели, который создает код, и попытаться использовать его внутри класса, который определяет «объемную строку» в админке, но он не работает.

Другими словами

модель "Объем" имеет внутри что-то вроде:

def selflink(self):
    return '<a href="/admin/journaldb/volume/%s/">Full record</a>' % self.vid
selflink.allow_tags = True

и

class VolumeInline(admin.TabularInline):
    fields = ['volumenumber', 'selflink']
    model = Volume
    extra = 1

Но это дает следующую ошибку:

Exception Value: 'VolumeInline.fields' refers to field 'selflink' that is missing from the form.

Есть идеи?

Спасибо, Giovanni

Ответы [ 6 ]

29 голосов
/ 04 февраля 2010

UPDATE: Начиная с Django 1.8, это встроено.

См. этот ответ и официальная документация .

СТАРЫЙ ОТВЕТ:

В конце концов я нашел простое решение.

Я создаю новый шаблон с именем linked.html, который является копией tabular.html, и добавил этот код для создания ссылки.

{% if inline_admin_form.original.pk %}
          <td class="{{ field.field.name }}">
              <a href="/admin/{{ app_label }}/{{ inline_admin_formset.opts.admin_model_path }}/{{ inline_admin_form.original.pk }}/">Full record</a>
          </td>
{% endif %}

затем я создал новую модель LinkedInline наследуя InlineModelAdmin

#override of the InlineModelAdmin to support the link in the tabular inline
class LinkedInline(admin.options.InlineModelAdmin):
    template = "admin/linked.html"
    admin_model_path = None

    def __init__(self, *args):
        super(LinkedInline, self).__init__(*args)
        if self.admin_model_path is None:
            self.admin_model_path = self.model.__name__.lower()

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

Я надеюсь, что это может быть полезно для других людей.

Giovanni

25 голосов
/ 26 февраля 2012

Обновление:

Начиная с Django 1.8, теперь это встроенный .

Ответ для Джанго <= 1,7: </strong>

Сохраните ваш код в models.py с условным регистром:

def selflink(self):
    if self.id:
        return "<a href='/link/to/volume/%s' target='_blank'>Edit</a>" % str(self.id)
    else:
        return "Not present"

selflink.allow_tags = True

В admin.py добавить собственную ссылку как только для чтения поле:

class VolumeInline(admin.TabularInline):
    readonly_fields = ['selflink',]
    model = Volume

Это сработало для меня.

13 голосов
/ 13 января 2014

Вот многоразовый миксин, основанный на некоторых других ответах. Это удобно, потому что работает как с табличными, так и со стекированными строками и не влияет на вашу модель или код администратора.

# put this somewhere like admin_helpers.py
from django.core.urlresolvers import reverse

class InlineEditLinkMixin(object):
    readonly_fields = ['edit_details']
    edit_label = "Edit"
    def edit_details(self, obj):
        if obj.id:
            opts = self.model._meta
            return "<a href='%s' target='_blank'>%s</a>" % (reverse(
                'admin:%s_%s_change' % (opts.app_label, opts.object_name.lower()),
                args=[obj.id]
            ), self.edit_label)
        else:
            return "(save to edit details)"
    edit_details.allow_tags = True

# admin.py

class VolumeInline(InlineEditLinkMixin, admin.TabularInline):
    fields = ['foo', 'bar', 'edit_details']

class JournalAdmin(admin.ModelAdmin):
    inlines = [VolumeInline]

class ScanInfoInline(InlineEditLinkMixin, admin.StackedInline):
    fields = ['foo', 'bar', 'edit_details']

class JournalAdmin(admin.ModelAdmin):
    inlines = [ScanInfoInline]
9 голосов
/ 08 августа 2015

В Django 1.8+ теперь это намного проще . Просто добавьте show_change_link = True к вашему подклассу TabularInline или StackedInline, например:

class VolumeInline(admin.TabularInline):
    fields = ['volumenumber']
    model = Volume
    extra = 1
    show_change_link = True

И Django автоматически добавит ссылку на форму полного изменения для каждого встроенного элемента, если модель имеет собственную зарегистрированную ModelAdmin.

3 голосов
/ 09 ноября 2011

Вы пробовали Обратный админ URL система?
Это может дать что-то подобное (на странице журнала):

<ul>
{% for volume in original.volume_set.all %}
  <li>
    <a href="{% url admin:yourapp_volume_change volume.id %}">Edit {{ volume }}
    </a>
  </li>
{% endfor %}
</ul>
2 голосов
/ 27 декабря 2012

После некоторой путаницы я смог выполнить эту работу в InlineAdmin и TabularInline, используя reverse (). По крайней мере, с TabularInline, поле, которое вы хотите связать , должно быть указано в 'readonly_fields'

# create a read-only inline with the first field linked
from django.core import urlresolvers
class YOUR_MODEL_Inline(LinkedTabularInline):
    max_num = 0 # remove the "Add another ..." link
    model = YOUR_MODEL_NAME
    fk_name = "YOUR_FOREIGN_KEY_NAME"
    fields = [ 'link_name', ] # , 'field1', 'field2', 'etc' ]
    readonly_fields = fields
    can_delete = False
    def link_name(self, obj):
        if obj.pk:
            url = urlresolvers.reverse('admin:%s_%s_change'
                % (obj._meta.app_label, obj._meta.module_name), args=[obj.id])
            # obj.MODEL_FIELD can be a text string or whatever you want
            return '<a href="{0}">{1}</a>'.format(url, obj.MODEL_FIELD) 
    link_name.allow_tags = True
    link_name.short_description = "MODEL_FIELD"

Если вы хотите связать список изменений вместо представления изменений, вы можете изменить обратный вызов (). список изменений не требует идентификатора объекта.

    url = urlresolvers.reverse('admin:%s_%s_changelist' 
        % (obj._meta.app_label, obj._meta.module_name))
    return '<a href="{0}">{1}</a>'.format(url, obj.name)

А если вы хотите создать ссылку на подмножество объектов, вы можете добавить параметры в URL:

    return '<a href="{0}?YOUR_MODEL_FIELD__id__exact={1}">{2}</a>'.format(url, obj.id, obj.name)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...