django-controlcenter

Widget

Всім привіт хочу поділитися своєю невеликою розробкою - django-controlcenter. Ця програма для створення дешбоардів для вашого django-проекту.

Django-controlcenter з'явився саме через подібну ситуацію, коли потрібно регулярно перевіряти кілька моделей на нові записи і ігнорувати їх, або змінювати, або видаляти, і бачити динаміку в графіках.

Дисклеймер

Поточна версія не використовує ajax, і насправді це навіть не CRUD, це тільки Read, але з розширеними можливостями.

Простий приклад

Почнемо з невеликого прикладу:

Цей віджет виведе табличку в дві колонки з 10 останніми значеннями (за замовчуванням ItemList обмежений у видачі, щоб не порвати сторінку).

django-controlcenter

Я використав знайомі терміни; в цілому, віджет - це суміш Views і ModelAdmin у плані іменування методів та атрибутів, та їх поведінки.

Як бачите, нічого нового. Поки ще.

Дисклеймер

Далі піде по суті документація, тож, якщо вам зручніше розбирати приклади, переходьте відразу до них.

Основних віджетів всього три: Widget, ItemList і Chart. Ще є Group, але це не віджет, а обгортка. Почнемо із нього.

Віджети можуть збиратися в групи, тоді вони перемикатимуться на кліку по заголовку. Для угруповання віджети вказуються списком/картежем або використовується спеціальна обгортка — Group.

Group приймає три необов'язкові аргументи: width, height, attrs. Важливий момент: такий "складовий" віджет отримує висоту найвищого в групі, оскільки дизайн адаптивний і використовує Masonry - якщо не зафіксувати габарити блоку, є шанс отримати забавний ефект, коли перемикаючись між віджетами групи у вас буде перебудовуватися весь дешбоард.

Group.width

Сітка дешбоарда адаптивна: до 768px віджети займають всю ширину, потім 50% або 100%. Від 1000px використовується 6-колонна сітка. Для зручності значення зберігаються в модулі widgets :

Проміжні значення не дуже корисні, але використовувати їх ніхто не забороняє.

Group.height

Спочатку None, але отримавши інтеджер, виставить віджету це значення як max-height і з'явиться необов'язковий скролл. width і height є і у віджетів, якщо ці значення не вказані в Group , береться максимальне значення у віджетів у цій групі.

Group.attrs

Все, що захочеться вписати у віджет як html атрибут. Можна навіть задати id.

Базовий віджет. Майже нічого не вміє. Але має одну корисність: в момент створення обертає метод values ​​(і series, labels, legend для чартів) в дескриптор cached_property. Відповідно значення доступні як при зверненні до атрибуту (без виклику), а дані кешуються. Це просто невелика зручність, оскільки часто доводиться звертатися до цих методів. Наприклад, для чартів робиться така штука:

Ще з десяток разів це запитається у шаблонах, так що краще відразу все закешувати.

Widget.title

Заголовок віджету. Якщо не заданий, сформується із назви класу.

Widget.width та Widget.height

Поведінка аналогічна Group (див. вище).

Widget.model

Widget.get_queryset

  • якщо є queryset , поверне його.
  • якщо є model, поверне його дефолтного менеджера.

Widget.values ​​та Widget.limit_to

Widget.template_name_prefix

Директорія з темплейтами.

Widget.template_name

Widget.get_template_name

Повертає Widget.template_name_prefix +Widget.template_name.

Це найскладніший віджет і водночас найпростіший. Простий, тому що жує все, що не попадя: моделі, словники, листи, namedtuple - все, що піддається ітерації або має доступ за ключем/атрибутом. Проте є особливості.

ItemList.list_display

Під час рендерингу шаблонів значення елементів в values ​​беруться по ключах з list_display (для моделей, словників і namedtuple), для послідовностей індекс ключа дорівнює індексу значення, грубо кажучи zip(list_display, values) .

Нумерація рядків

Додайте # до list_display і отримайте нумерацію рядків. Також "решітку" можна замінити на інший символ, встановивши його як значення в settings.CONTROLCENTER_SHARP .

ItemList.list_display_links

Поведінка аналогічна list_display_links у django.

Посилання на редагування об'єкта

ItemList намагається повісити посилання на сторінку редагування об'єкта в адмінці, для цього потрібен клас об'єкта і первинний ключ. Тому віджет шукатиме ці дані скрізь: якщо values ​​поверне інстанс моделі, то витягне все з нього. Якщо значення поверне словник, список або namedtuple, то знадобиться вказати ItemList.model , тому що, зрозуміло, більше не звідки. У всіх випадках віджет спробує знайти pk або id самостійно, але у разі послідовностей це зробити не вийде, тому віджет буде шукати ці ключі list_display зіставляючи його індекс з індексом значень послідовності. До речі, віджет розуміє deferred моделі, так що можна писати так: queryset = Model.obejcts.defer('field') . Для роботи цієї фічі модель має бути зареєстрована у django-admin.

Посилання на changelist моделі

Іноді недостатньо подивитись на 10 останніх значень і треба перейти насторінку моделі. ModelAdmin будує такі шляхи самостійно. Але у віджет можна підставити все, що завгодно в queryset, тому доведеться допомогти. Варіантів кілька:

Для роботи цієї фічі модель має бути зареєстрована у django-admin.

ItemList.sortable

Щоб сортувати табличку, достатньо вказати sortable=True , але пам'ятайте, що джанга сортує в базі, а віджет на стороні клієнта, тому можуть траплятися казуси, наприклад, якщо в стовпці дати вказані у форматі dd.mm . Використовується бібліотека sortable.js.

ItemList.method.allow_tags та ItemList.method.short_description

Поведінка аналогічна джанговський allow_tags і short_description.

ItemList.empty_message

Виведе це значення, якщо значення поверне порожній список.

ItemList.limit_to

За замовчуванням має значення 10 щоб ви собі в ногу не вистрілили.

Для графіків використовується Chartist – це невелика бібліотека зі своїми особливостями. Вона дуже швидка, просто миттєва, я просто не міг пройти повз.

Є три типи чартів: LINE, BAR, PIE; та відповідні до них класи: LineChart, BarChart, PieChart. Плюс кілька додаткових, про це згодом.

Chart визначає три додаткові методи: legend, lables, series, які ще й кешуються. Всі три методи повинні повертати json-серіалізований об'єкт, до яких не належать генератори.

Chart.legend

З коробки Chartist не вміє показувати легенду, але без неї ніяк, оскільки чартист ще й не малює значення графіки (так, є такий момент). Легенда допоможе у таких випадках.

Chart.labels

Значення на осі x. Повинен повертати послідовність, у жодному разі не передавайте генератор.

Chart.series

Значенняна осі y. Повинен повертати список списків, оскільки на графіках можуть бути множинні дані. Знову ж таки, ніяких генераторів. Тут є невелика " готча " , типу BAR з однією типом значень передається " плоский " список, тобто. не вкладений, при цьому встановлюється додаткова опція чартиста. Найпростіше використовувати SingleBarChart – у ньому все налаштовано.

Chart.Chartist

Chart - це віджет з додатковим класом Chartist всередині на зразок Meta або Media в джангу.

Важливо! Для LineChart встановлено 'reverseData': True , що реверсує значення labels та series на клієнті. Найчастіше цей тип чартів використовується для відображення останніх даних і щоб вам не довелося в кожному першому чарті цим займатися вручну, ця опція включена за замовчуванням.

Chart.Chartist.klass

Визначає тип чарту: widgets.LINE, widgets.BAR, widgets.PIE.

Chart.Chartist.point_lables

Підключається плугін до Chartist, який проставляє значення на графіку. Це дивно, але дефолтний чартист обходиться без значень на самому графіку. На жаль, ця штука працює тільки з widgets.LINE. В інших випадках допоможе метод legend.

Chart.Chartist.options

Словник, який повністю вирушає до джсона і передається конструктуру чартиста. Усі опції описані на сайті.

Додаткові класи

У модулі widgets підготовлено ще кілька допоміжних класів: SingleLineChart, SingleBarChart, SinglePieChart – для простих юзкейсів.

Ну, власне, і все. Значення name підуть у вісь x, а score у вісь y.

Dashboard.widgets

Приймає список віджетів.

Dashboard.title

Довільний заголовок. Якщо не заданий, буде сформовано із назви класу.

Dashboard.Media

Клас Media із джанги.

Давайте зробимо все те саме, що і на скріншоті. Створимо проект, назвемо його pizzeria, додамо до нього додаток pizza.

pizzeria.pizza.models

Внесемо програми до pizzeria.settings

Додамо урли до pizzeria.urls

У файлі pizzeria.dashboards створимо віджети:

django-controlcenter підтримує до 10 дешбоардів. Але ми створимо один у pizzeria.dashboards

От і все, відкриваємо /admin/dashboard/0/.

Сумісність

Тести проводилися на python 2.7.9, 3.4.3, 3.5.0 та django 1.8, 1.9.

Так само додаток чудово дружить з django-grappelli.

Документація

Цю статтю можна вважати такою, переклад на кострубату англійську я виконаю найближчим часом, як тільки розберуся зі sphinx.

P.S. Я вперше вирішив зайнятися OSP і, треба зізнатися, більше витратив часу на розгляди з дистриб'юцією, ніж на сам код, проте я не до кінця впевнений, що все зробив правильно, тому буду вдячний за будь-який фідбек.

А у нас тут можна отримати грант на тестовий період Яндекс.Хмари. Варто лише у полі «секретний пароль» запровадити «Хабр»