Чуть более быстрое удаление объектов в админке

И опят речь пойдет об оптимизации, но оптимизации не программной части сайта, а работы человека с сайтом. Вас никогда не напрягало то, что в штатной джанговской админке для того чтобы удалить объект, нужно выбрать его из списка, потом прокрутить страницу внизу и нажать на кнопку "Удалить"?

Меня вот это раздражало, пока мне не пришла в голову простая идея. Надо кнопку удаления сделать в list view в каждой строчке.

Решение ещё проще чем идея.:) Вот пример:

class Entry( models.Model ):
    value = models.IntegerField()

    #начинается самое интересное. внимание
    def remove(self):
        from django.core.urlresolvers import reverse
        return '<a href="%s" class="deletelink">Delete</a>'\
                   % reverse( "django.contrib.admin.views.main.delete_stage",
                                        args=(self.__class__._meta.app_label,
                                             self.__class__.__name__.lower(),
                                             self._get_pk_val(),) )
    remove.allow_tags = True
    #интересное почти кончилось

    class Admin:
        list_display = ( "value", "remove" )

Вот такой результат:

delete buttons

Просто, не правда ли? Немного расскажу про код на всякий случай. Вся соль в возможности указывать в list_display не только реальные поля, но и методы модели. Так же эти методы можно помечать атрибутом allow_tags чтобы возвращаемое значение не эскейпилось автоматически. Метод сам по себе тривиален - возвращает тег-ссылки на страницу удаления. Ссылка получается через реверс с нужными параметрами.

Нужно всего-лишь добавить такой метод в модель и прописать его в list_display. Можно немного доработать и сделать базовый класс с этим методом, а потом от него наследовать нужные модели. Так же можно Delete вынести как параметр форматирования и обернуть его в _(), чтобы сохранилась возможность перевода в соответствии с языковыми настройками. Так что простор для творчества большой. Я лишь предложил концепцию:)

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

Как думаете, поможет в работе с админкой такой трюк? Я пока испытываю только положительные эмоции. Менеджеры моих проектов скоро тоже оценят, я думаю...:)

P.S: прошу прощения у читателей за долгое молчание, просто сейчас нахожусь в стадии "суровой сессии" и времени катастрофически не хватает. Обещаю скоро исправиться:)

Комментарии 20

  1. FX Poster написал:

    Пасиб. :) Понравилось.

    PS. Убери у label'ов display: block

    Оставлен 15 Январь 2008 в 22:05
  2. Igorek написал:

    Полезная весчь. Но вот если бы ещё можно было как-то сделать кастомизацию list_filter, цены б не было. Вот я сейчас над этим долблюсь статьи к разделу категорий Статьи, а продукция к разделу категории Продукция. Ничего в голову не лезет. :) И всё таки сортировка в админке сделана через опу. Ну не выдаёт она так как мне надо. :)

    Оставлен 16 Январь 2008 в 10:06
  3. igorekk написал:

    Спасибо, отличная идея. Буду использовать.

    Оставлен 16 Январь 2008 в 19:06
  4. Александр Кошелев написал:

    И всё таки сортировка в админке сделана через опу.

    Может быть она просто не для того предназначена?:) Ждать много от стандартного CRUD'a я бы не стал. Если не хватает функциональности, то нужно писать свой бекэнд.

    Оставлен 17 Январь 2008 в 19:21
  5. Александр Кошелев написал:

    PS. Убери у label'ов display: block

    А что не так? Просто верстка не мой конек:)

    Оставлен 17 Январь 2008 в 19:22
  6. FX Poster написал:

    просто "Уведомлять о новых комментариях по почте" под чекбоксом не смотрится :)

    Оставлен 17 Январь 2008 в 22:07
  7. playpauseandstop написал:

    Ну и для полного счастья я еще добавляю самую малость JavaScript'a:

    def remove(self):
         return u'<a href="%s" style="color: red;" onclick="return window.confirm(\'%s\')">%s</a>'\
           %  (reverse('django.contrib.admin.views.main.delete_stage',
                args=(self .__class__._meta.app_label,
                   self.__class__. __name__ .lower(),
                  self._get_pk_val(),
               ) ),unicode(_('Are you sure?')),unicode(_('Delete')),)
    
    Оставлен 20 Январь 2008 в 13:29
  8. Александр Кошелев написал:

    Ага, хорошая идея:)

    Оставлен 20 Январь 2008 в 13:42
  9. Александр Кошелев написал:

    А я просто перенаправляю на страницу удаления на delete % self.id, а >там уже оно спрашивает подтверждение

    Кстати, да. У меня уже юрл-реверс мозг съел, поэтому я его всегда использую. А как вариант можно и относительный путить писать, так и джанго-девелоперы сами не брезгуют делать.

    и не ставлю крестик удаления -- потому что с ним некрасиво получается, он в глаза сильно >бросается, всё-таки строчка текста с единственной картинкой.

    Ну это уже дело вкуса.:) Мне крестик не мешает да и как-то разбавляет он скучную картинку.

    Оставлен 22 Январь 2008 в 02:30
  10. Alex Kamedov написал:

    за статью спасибо, но джанговский стандартный вариант с точки зрения информационного дизайна более логичен. сценарий взаимодействия такой: вы смотрите список объектов, решаете что какой-то из них лишний, заходите посмотреть его параметры, ваша уверенность становится стопроцентной, вы удаляете объект. то есть вероятность того, что вы по ошибке что-то удалите сокращается и при этом нет необходимости использования дурацких диологов подтвеждения. хотя конечно время работы с системой существенно увеличивается (((

    если развивать вашу идею дальше, то гораздо эффективней использовать массэкшены - чекбоксом выделяем нужные записи и затем производим с ними необходимое действие :)

    Оставлен 25 Январь 2008 в 07:44
  11. Александр Кошелев написал:

    если развивать вашу идею дальше, то гораздо эффективней использовать массэкшены - чекбоксом выделяем нужные записи и затем производим с ними необходимо

    С чекбоксами конечно удобней. Но этот вариант требует более комплексного решения, чем написание одного метода модели:) Хотя в перспективе некое универсальное решение наверно можно найти. Но уж лучше подождать до того как newforms-admin сольется с транком.

    Оставлен 01 Февраль 2008 в 20:03
  12. Yuri Baburov написал:

    Чекбоксы делаются очень просто:

    
           class MyModel(Model):
        class Admin: list_display=['checkboxes', ...]
        def checkboxes(self):
            return "<input type='checkbox' name='checkboxes' value='%s'>" % self.id
        checkboxes.allow_tags = True
          
    

    вот и всё, осталось добавить на страницу кнопку Mass delete, поставить на неё перенаправление на нужный метод:

    
           <input type='button' value='Mass delete' onclick='if(confirm("Really delete?")){f=document.
    getElementById("имя формы");f.action="mass_delete/"; f.submit();}'>,
          
    

    ну и нарисовать этот самый метод массового удаления объектов:

    
           def mass_delete(request):
        for obj in request.POST.get_list("checkboxes"):
            obj.delete()
        request.user.message_set.create(message="Removed.")
        return HttpResponseRedirect('..')
          
    

    P.S. не удивлюсь, если есть баги, т.к. пишу по памяти. Кроме того, есть подобный snippet на djangosnippets.org. Как говорится, было бы желание :)

    Оставлен 01 Февраль 2008 в 23:58
  13. valera.grishin@gmail.com написал:

    А как сделать эти удаления теперь, когда django.contrib.admin.views.main.delete_stage уже больше нет в транке?

    Оставлен 31 Октябрь 2008 в 11:10
  14. Александр Кошелев написал:

    А как сделать эти удаления теперь, когда django.contrib.admin.views.main.delete_stage уже больше нет в транке?

    Я на самом деле ещё не портировал это. Поэтому для начала можно просто вбить ссылку руками:)

    Оставлен 02 Ноябрь 2008 в 20:09
  15. Valera Grishin написал:

    Пока получается вот такая заплатка:

    return '<a href="%s/%s/%s/%i/%s/" class="deletelink">%s</a>' % \
    (
        '/admin',
        self.__class__._meta.app_label,
        self.__class__.__name__.lower(),
        self._get_pk_val(),
       'delete',
       ugt('Delete')
    )
    

    Заставить работать django.contrib.admin.site не удалось. Пробовал методы root и model_page.

    Оставлен 04 Ноябрь 2008 в 10:41
  16. Valera Grishin написал:

    Отформатировать текст не получилось :( Справка по markdown не работает.

    Оставлен 04 Ноябрь 2008 в 16:42
  17. Александр Кошелев написал:

    Пока получается вот такая заплатка:

    Да, тоже вариант.

    Кстати, могу посоветовать посмотреть django-batchadmin. Это приложение позволяет делать операции над множеством объектов сразу. Скоро его в джангу непосредственно встроят, так что возможно нет необходимости так шаманить вообще.

    Отформатировать текст не получилось :( Справка по markdown не работает.

    Спасибо, поправлю. У меня как раз скоро обновление движка:)

    Оставлен 04 Ноябрь 2008 в 20:12
  18. Valera Grishin написал:

    Этот django-batchadmin не решает всех подобных проблем. Я на предложенной в этой статье технологии цеплял сразу несколько функций к моделям. Причём не только для экземпляров, но и для класса. Например, ссылка на создание нового экземпляра (типа того, что джанго делает в админке вверху справа).

    з.ы. Приведённый мною пример не работает, кстати. Другого варианта пока не нашёл.

    Оставлен 05 Ноябрь 2008 в 18:55
  19. Александр Кошелев написал:

    Вы меня заинтересовали:-) Надо всё-таки выделить время и портировать это решение на текущую джангу.

    Оставлен 06 Ноябрь 2008 в 00:46
  20. Valera Grishin написал:

    Да-да, очень будет интересно посмотреть на результат :)

    Оставлен 10 Ноябрь 2008 в 19:07