Django — Урок 021. Наследование моделей, абстрактная модель

После проведения рефакторинга на сайте, было выделено четыре основных сущности, у которых были выделены общие свойства, а именно:

  • Article — Статьи
  • Comment — Комментарии
  • ForumTopic — Темы форума (они же вопросы)
  • ForumPost — Ответы к темам форума

Конечно, и так было ясно, что эти сущности могут иметь одинаковые поля данных, одинаковые методы и т.д. Но при разработке данного сайта я и сам одновременно изучаю Python и Django. Поэтому проект носит характер хаотичного внесение небольших ToDo с последующим рефакторингом при изучении лучших подходов. Поэтому после изучения возможностей наследования моделей в Django, была выделена одна общая абстрактная модель данных PostBase , которая имеет четыре поля, которые повторяются во всех выше перечиcленных моделях.

Здесь есть один важный момент: Модель, которая объявляется абстрактной, не будет создавать таблицу в базе данных.

Чтобы создать абстрактную модель необходимо установить переменную abstract в значение True для класса Meta.

PostBase — это будет базовая абстрактная модель. В этой абстрактной модели данных было выделено четыре поля, которые являются общими для выше перечисленных моделей:

  • author — автор статьи, комментария, темы или ответа;
  • content — содержание;
  • pub_date — дата публикации;
  • moderation — модерация, весь контент может иметь четыре возможных варианта:
    • SPAM — без комментариев;
    • NOT_MODERATED — Не проверенная запись, в данном случае статьи пользователей с данным статусом не будут доступны другим пользователя до модерации;
    • POST_MODERATED — модерация после публикации, статья пользователя будет доступна другим пользователям после публикации, но она ещё не прошла модерацию;
    • MODERATED — запись прошла модерацию.

Объявление данной абстрактной модели будет следующее:

Таким образом имеется возможность сократить программный код проекта и добавить возможность повторного использования повторяющегося кода.

Структура модели, например, для комментариев теперь может выглядеть следующим образом:

Поля author , pub_date , content и moderation уже не требуется указывать, поскольку они присутствуют в PostBase классе. Главное, не вводите в свою модель поля с такими же именами, как в модели PostBase.

PostBaseAdmin

Также несомненным плюсом является то, что можно точно также сделать одну общую для всех классов настройку админ панели.

Сделаем, например, отображение полей, настройку поиска и фильтрации контента и возможность установки статуса модерации (то есть добавим соответствующие actions ).

Если вы захотите расширить листы фильтрации или добавить actions, для какой-то из моделей, то можно наследоваться от PostBaseAdmin следующим образом:

Для Django рекомендую VDS-сервера хостера Timeweb .

Рекомендуемые статьи по этой тематике

evileg.com

Django наследование

Прочитал статью «Фрагментарное кэширование в MVC веб-фреймворках». Статья описывает проблему кеширования фрагмета отображения, а именно проблему полного разделения контроллера и отображения — контроллер отрабатывает полностью до вызова отображения. Если в отображении мы кешируем фрагмент, это ничего не меняет — контроллер-то уже отработал! В статье описан способ этого избежать: сделать запрос данных «ленивым».

Начав писать, как это должно быть сделано правильно, решил написать, как устроены шаблоны Django, чтобы не-джанговодам тоже было понятно.

Как это сделано в Django?

Структура шаблонов Django

Управляющими элементами шаблонов Django являются переменные, фильтры и теги.

При рендеринге шаблона переменные заменяются на свое значение, вычисленное в контексте вызова. Синтаксис — двойные фигурные скобки — например: << title >> .

Фильтры служат для простых преобразований переменных. Синтаксис — переменная|имя_фильтра:»параметры» . Фильтр может встречаться как в переменных, так и в качестве параметра тега. Например: << title|lowercase >> .

При рендеринге шаблона теги, грубо говоря, заменяются на результаты работы ассоциированной с этим тегом функции на питоне. Синтаксис: <% тег параметры %>, например: <% url blog_article slug=article.slug %>.

Программист может написать свои фильтры и теги, но об этом позже.

Кроме того, есть три специальных тега: include , block и extend . Тег include подставляет запрошенный шаблон (отрендеренный в текущем контексте).

Все выше перечисленное тривиально и в той или иной форме есть в любом движке шаблонов. Теперь перейдем к особенностям Django: на тегах block и extend строится наследование шаблонов. Остановимся на них подробнее.

Наследование шаблонов

Основная фишка шаблонов Django — наследование. Шаблон может расширять (уточнять) поведение родительского шаблона.

Любой участок шаблона может быть обернут в блочный тег (естественно, что тег не может начинаться перед, а заканчиваться внутри цикла). Блоку дается имя. Например:

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

Получается мощный механизм, практически исключающий необходимость повторения частей шаблонов. Вкратце это описано в документации (см. ссылки в конце статьи). Давайте разберем реальный пример.

Пример наследования шаблонов

Допустим, мы хотим сделать сайт, содержащий простые страницы и блог.

От верстальщика мы получили макет страницы, содержащий:

  • шапку (логотип, заголовок страницы, меню);
  • тело страницы;
  • и «подвал» с информацией о правах распространения.

Вот как это выглядит:

Для всех указанных элементов мы создаем соответствующие блочные теги.

Простая страница ложится в этот макет — у нее есть только заголовок и тело.

Теперь перейдем к блогу. В блоге хотелось бы добавить правую колонку для вывода списка тегов и последних статей. Возможно мы захотим добавить правую колонку к каким-нибудь другим страницам сайта. Чтобы избежать копирования «двухколоночности», вынесем ее в отдельный шаблон, переопределив тело страницы у базового.

В блоге будет несколько типов страниц:

  • список статей;
  • статья;
  • список тегов;
  • список статей, у которых есть определенный тег;
  • пр.

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

Теперь приведем примеры внутренних страниц блога (все они наследуются от базовой страницы блога).

Список статей, у которых есть определенный тег:

В данном случае, мы воспользовались еще одной хитростью. Ведь этот список ничем не отличается от простого списка статей — он просто отфильтрован по дополнительным параметрам. Поэтому мы унаследовали его от списка статей и при перекрытии тела использовали тег << block.super >> — вывести все содержимое родительского блока.

Как видно, каждый шаблон очень конкретен и отвечает только за свою функциональность. Ему нет необходимости знать о всей странице в целом.

Поклонники других шаблонных систем скажут, что для приведенного примера наследование не нужно. Действительно, то же самое можно реализовать, используя теги подстановки ( include , ssi ). Вот только логика и структура этих включений будет намного запутаннее. Получится, что статья должна знать, какие блоки будут на ее странице, и предоставлять данные для всех этих блоков. Тут на помощь приходит еще одна особенность Django — пользовательские теги.

Пользовательские теги

В нашем примере на странице статьи блога есть 7 блоков. Два из них — логотип и copyright — не нуждаются в данных. Для остальных пяти контроллеру необходимо предоставить шаблону данные. А именно:

  • заголовок статьи;
  • меню;
  • тело статьи;
  • список тегов;
  • последние статьи.

Блоков могло быть намного больше, но непосредственное отношение к статье имеют только заголовок и тело статьи. Зачем статье знать, какие данные нужны этим блокам, откуда и как их получить? Абсолютно незачем — это не ее задача. Django предлагает нам следующее решение этой проблемы.

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

Вот пример тега для вывода списка последних статей в блоге:

Еще одним приемуществом такого подхода является то, что данные запрашиваются непосредственно при вставке тега. Если кэшировать несколько тегов, то будут кэшированы результаты их работы — повторно данные запрашиваться не будут! И не надо изобретать велосипеды, как тут 😉

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

m.habr.com

Python-сообщество

Уведомления

#1 Июль 29, 2011 15:23:55

Наследование моделей

Здравствуйте!
Необходимо наследовать модель, при создании класса-потомка, устанавливать поля класса-родителя в определенное значение.
В Питоне все просто, но применительно к моделям все несколько иначе.
Например, имеем родительский класс Дерево и классы-потомки Дуб и Береза.

#2 Июль 29, 2011 21:04:55

Наследование моделей

А почему бы просто конструктор не переопределить?

#3 Июль 31, 2011 21:25:07

Наследование моделей

Прошу ткнуть носом, как это сделать.
Пробовал так

Отредактировано (Июль 31, 2011 21:25:34)

#4 Авг. 1, 2011 11:40:46

Наследование моделей

а что, у нас Django уже на третьем питоне работает?

Во-первых, super(Береза, self).__init__(*args, **kwargs), во вторых, разве присваивание просто будет работать?

Я даже больше скажу, возможно, проще переопределить не конструктор, а метод save(). Ну как-то вроде

Отредактировано (Авг. 1, 2011 11:50:29)

#5 Авг. 1, 2011 14:42:39

Наследование моделей

Присваивание в самом деле не работает.
Есть еще связанный вопрос.
В Джанго создаются экземпляры классов Береза и Дуб. Экземпляров класса Дерево не создается, он задуман как виртуальный класс.
При этом делая выборку по модели Дерево, получаю все имеющиеся экземпляры потомков.

#6 Авг. 1, 2011 17:02:16

Наследование моделей

wildDAlex
Может, есть смысл указать для класса Дерево abstract=True? Хотя, зачем тогда вообще наследование, если разница между классами только в типе?

#7 Авг. 1, 2011 18:41:46

Наследование моделей

Ну я упростил пример, выкинув все не относящееся к вопросу, в реальности разница не только в типе, так же как и общих полей много.
В целом почитал буржуйские источники, превалирует мнение, что наследование в Джанго очень не очевидно и проблемно. Плюс реализовано посредством линкованных таблиц, негативно влияющих на производительности БД. abstract=True похоже решение, хотя там на самом деле все поля создаются в таблице потомка.

python.su

Django Book: наследование в шаблонах

Предыдущая часть.

Наши предыдущие примеры шаблонов были небольшими фрагментами HTML-кода, однако в реальной ситуации вы будете использовать Django для создания больших страниц. Отсюда возникает один из наиболее существенных вопросов веб-разработки – как уменьшить количество повторяющегося и избыточного кода в общих частях страниц, таких как навигация по сайту?

Классическое решение этой проблемы заключается в использовании инклюдов (includes), или “вложений” – особых директив в коде HTML, которые позволяют включать одну HTML-страницу в код другой. Действительно, Django поддерживает такой подход с помощью тега шаблона <% include %>, который мы рассмотрели в статье Django Book: глава 2 – загрузка шаблонов. Но имеется и другой, более предпочтительный, способ решения, который называется Наследование шаблонов.

Кратко говоря, наследование позволяет вам построить базовый “скелет” шаблона, который содержит все части вашего сайта и определяет блоки, которые могут быть перезаписаны “дочерними” шаблонами.

Давайте рассмотрим пример реализации этого, создав более полный шаблон для нашего преставления current_datetime , отредактировав файл timetemplate.html :

Он выглядит достаточно хорошо, но что будет, если мы захотим создать шаблон для другого представления, например – hours_ahead из статьи Django Book: третье представление – динамические URL-ы? Если мы опять хотим создать красивый и валидный HTML-шаблон – нам придётся делать что-то вроде такого:

Очевидно, что мы дублируем много HTML-кода. Представьте, что у нас есть типичный сайт, с панелью навигации, несколько CSS, возможно JavaScript – нам придётся добавлять весь избыточный код в каждый шаблон.

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

Например, вы могли бы сохранить заголовок страницы в шаблоне с именем header.html :

А окончание страницы – в файле footer.html :

С использованием инклюдов такое реализовать очень просто. Однако, тут есть несколько проблем. Например, обе страницы используют заголовок страницы –

My helpful timestamp site

в заголовок – нам придётся включить туда и , и это не позволит нам настраивать страницы.

Система наследования в шаблонах Django решает эту проблему. Вместо определения того, как части страницы будут общими – вы можете определить какие из них будут разными.

Первый шаг – определить базовый шаблон – скелет страницы, который потом будет заполнен дочерними шаблонами. Вот пример нашего текущего шаблона:

Этот шаблон мы назовём base.html , и он описывает простой HTML-скелет документа, который мы будем использовать для всех страниц сайта. Далее уже наши дочерние шаблоны будут перезаписывать, добавлять или удалять содержимое блоков. Сохраните его в вашей директории templates с именем base.html .

Тут мы использовали тег шаблона, который вы раньше не встречали – <% block %>. Всё, что он делает – это указывает системе шаблонов, что дочерний шаблон может перезаписать этот участок шаблона.

Теперь, мы можем отредактировать наш шаблон timetemplate.html , что бы использовать эту возможность:

Теперь, давайте создадим шаблон для представления hours_ahead (мы предлагаем вам самим изменить само представление, что бы оно использовало систему шаблонов, вместо кода непосредственно в коде представления). Вот как он может выглядеть:

Правда – замечательно? Каждый шаблон только свой уникальный код, больше нет никакой избыточности. Если вам необходимо сделать изменения, касающиеся всего сайта – достаточно отредактировать файл base.html , и все шаблоны немедленно их отобразят.

Вот как это работает. Когда загружается шаблон timetemplate.html , система шаблонов встречает тег <% extends %>, который означает что это дочерний шаблон. Система сразу же подгружает родительский шаблон, в данном случае это base.html .

Далее, система находит три тега <% block %>в шаблоне base.html , и заменяет их значение блоками из дочерней страницы. Таким образом, мы заменяем блоки <% block title %>и <% block content %>.

Заметьте, что так как дочерний шаблон не определяет блок footer – система шаблонов использует значение из родительского шаблона. Содержимое блока <% block %>в родительском шаблоне всегда используется как “запасной”, если не указано ничего в дочернем.

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

Вы можете использовать любое количество уровней наследования. Обычно используют трёх-уровневый подход:

  1. Базовый шаблон base.html , который описывает общий вид вашего сайта. Он меняется редко – или даже вообще никогда.
  2. Создаются base_SECTION.html шаблоны, один для каждой “секции” сайта (например – base_photos.html и base_forum.html ). Эти шаблоны дополняют base.html и содержат в себе специфичные для раздела стили и дизайн.
  3. Создаются отдельные шаблоны для каждого типа страницы, таких как форум или фотогалерея, которые дополняют шаблон секции.

Такой подход позволяет максимально использовать один и тот же код, и упрощает добавление объектов в общие части сайта, такие как панель навигации.

Вот несколько указаний для работы с наследованием шаблонов:

rtfm.co.ua

Абстрактные модели джанго и регулярное наследование

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

ОБНОВЛЕНИЕ: Я думаю, что мой вопрос был неправильно понят, и я получил ответы на разницу между абстрактной моделью и классом, который наследуется от django.db.models.Model. Я действительно хочу знать разницу между классом модели, который наследуется от абстрактного класса django (Meta: abstract = True) и простого класса Python, который наследует, скажем, «объект» (а не модели .Model).

Я действительно хочу знать разницу между классом модели, который наследуется от абстрактного класса django (Meta: abstract = True) и простой класс Python, который наследует, например, «объект» (а не models.Model).

Django будет генерировать таблицы для подклассов models.Model , поэтому предыдущий.

. приведет к созданию отдельной таблицы по строкам.

. тогда как последний.

. не приведет к созданию каких-либо таблиц.

Вы можете использовать множественное наследование, чтобы сделать что-то вроде этого.

. который создал бы таблицу, но будет игнорировать поля, определенные в классе User , поэтому вы получите таблицу, подобную этой.

Абстрактная модель создает таблицу со всем набором столбцов для каждого подраздела, тогда как использование «простого» наследования Python создает набор связанных таблиц (например, «наследование на нескольких таблицах» ). Рассмотрим случай, когда у вас две модели:

Если Vehicle — абстрактная модель, у вас будет одна таблица:

Однако, если вы используете обычное наследование Python, у вас будет две таблицы:

Где vehicle_id — ссылка на строку в app_vehicle , которая также будет иметь количество колес для автомобиля.

Теперь Django прекрасно сочетает это в объектной форме, поэтому вы можете получить доступ к num_wheels как атрибут на Car , но базовое представление в базе данных будет иным.

Чтобы ответить на ваш обновленный вопрос, разница между наследованием от абстрактного класса Django и наследованием от Python object заключается в том, что он рассматривается как объект базы данных (поэтому таблицы для него синхронизируются с базой данных), и он имеет поведение a Model . Наследование с простого Python object не дает классу (и его подклассам) ни одного из этих качеств.

Просто хотел добавить что-то, чего я не видел в других ответах.

В отличие от классов python, скрытие имени поля не разрешено с наложением модели.

Например, я экспериментировал с вариантами использования следующим образом:

У меня была модель, наследующая от django auth PermissionMixin:

Тогда у меня был мой mixin, который среди прочего я хотел, чтобы он переопределил related_name поля groups . Так было примерно так:

Я использовал эти 2 смеси, как показано ниже:

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

Пытаясь решить эту проблему, я беспорядочно решил изменить свой mixin и преобразовать его в абстрактный модель mixin. Ошибка изменилась на следующее:

Как вы можете видеть, эта ошибка объясняет, что происходит.

Это была огромная разница, на мой взгляд:)

Основное отличие заключается в том, как создаются таблицы баз данных для моделей. Если вы используете наследование без abstract = True , Django создаст отдельную таблицу как для родительской, так и для детской модели, в которой хранятся поля, определенные в каждой модели.

Если вы используете abstract = True для базового класса, Django создаст таблицу для классов, которые наследуют от базового класса — независимо от того, определены ли поля в базовом классе или наследующем классе.

Плюсы и минусы зависят от архитектуры вашего приложения. Учитывая следующие примеры моделей:

Если класс Publishable не является абстрактным, Django создаст таблицу для публикации с столбцами title и date и разделит таблицы для BlogEntry и Image . Преимущество этого решения состояло бы в том, что вы можете запрашивать через все публикации для полей, определенных в базовой модели, независимо от того, являются ли они блогами или изображениями. Но поэтому Django придется делать соединения, если вы, например. делать запросы для изображений. Если создание Publishable abstract = True Django не создаст таблицу для Publishable , а только для записей и изображений блога, содержащих все поля (также унаследованные). Это было бы удобно, потому что никакие объединения не нужны для операции, такой как get.

Основное отличие заключается в том, что вы наследуете класс User. Одна версия будет вести себя как простой класс, а другая будет вести себя как режим Django.

Если вы наследуете базовую версию «объект», ваш класс Employee будет просто стандартным классом, а first_name не станет частью таблицы базы данных. Вы не можете создать форму или использовать с ней какие-либо другие функции Django.

Если вы наследуете версию models.Model, ваш класс Employee будет иметь все методы Django Model, и он наследует поле first_name как поле базы данных, которое можно использовать в форме.

В соответствии с документацией Абстрактная модель «дает возможность разделить общую информацию на уровне Python, в то же время создавая только одну базу данных таблица на дочернюю модель на уровне базы данных.»

qaru.site

Еще по теме:

  • Ставка налога на ценные бумаги физических лиц Налогообложение: акции и облигации В нашей стране стандартная налоговая ставка по этому налогу для физических лиц-резидентов установлена в размере 13% - по ней происходит налогообложение большей части доходов, получаемых частными инвесторами на […]
  • Новые правила учета тепловой энергии 2013 Минстрой предлагает новые Правила коммерческого учета тепла и теплоносителя Согласно опубликованному на Официальном портале проектов нормативных правовых актов тексту, предлагается ввести новые Правила коммерческого учета тепла и теплоносителя и […]
  • Какие документы нужны уход на пенсию по старости Какие нужны документы для оформления пенсии по старости? Законодательство РФ предусматривает три различных вида пенсий по старости (п. 1 ст. 3 Закона от 28.12.2013 N 400-ФЗ; п. п. 3, 6 ст. 5, пп. 5, 8 п. 1 ст. 4 Закона от 15.12.2001 N 166-ФЗ): 1) […]
  • Саки нотариус Саки нотариус Нотариальная палата Республики Крым Абдураимова Бахтилле РахмиевнаТелефон: (3652)32-35-82 , +79781089317 Адрес: пгт. Гвардейское Симферопольского района, ул. Карла Маркса, дом 47, кв. 30 Автухович Эра ВладимировнаТелефон: […]
  • Как заключить договор купли продажи квартиры за материнский капитал Договор купли-продажи недвижимости с использованием материнского капитала Материнский семейный капитал — это сертификат, который можно потратить на покупку жилья. В этой части вопросов не возникает: семья, получившая материальную помощь от […]
  • Налог на автомобиль льготы москва Налог на автомобиль льготы москва Получите квалифицированную помощь прямо сейчас! Наши адвокаты проконсультируют вас по любым вопросам вне очереди. Льготы по транспортному налогу для ветеранов труда В Москве - нет, а в Московской области действует […]