Реклама

Django - Урок 003. Модель, шаблон и представление в Django

model, template, view

В Django используется модульная система приложения, когда одно приложение состоит из нескольких приложений, отвечающих каждое за свой функционал. Как Вы успели заметить, на момент написания статьи, на сайте присутствует раздел "База Знаний", в котором присутствует несколько разделов, по которым уже разделены статьи.

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

Предлагаю разобраться, как это реализуется в минимальном варианте на примере EVILEG COM.

Структура проекта

Конкретно в данном сайте используется два приложения в проекте:

  1. home - приложение, которое отвечает за основную страницу index, страницы ошибок и за базовые шаблоны
  2. knowledge - приложение, которое отвечает именно за статьи и разделы статей

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

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py
    home/
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py
        templates/
            home/
                base.html
    knowledge/
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py
        templates/
            knowledge/
                article.html
                index.html
                section.html

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

Для создания приложений необходимо воспользоваться следующими командами:

python manage.py startapp home
python manage.py startapp knowledge

Настройка settings.py

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

INSTALLED_APPS = [
    'home.apps.HomeConfig',
    'accounts.apps.AccountsConfig',
    ...
]

Модели

В проекте используется два вида моделей:

  1. Section - разделы, которые объединяют все статьи по одному общему признаку, принадлежности к определённой тематике
  2. Article - сами статьи

Модели описываются в файле models.py

Для разработки этих моделей данных было применено наследование от встроенного в Django класса Model. А также использовалась модель пользователя User, поскольку в статьях указывается автор из числа зарегистрированных на сайте пользователей.

from django.db import models
from django.contrib.auth.models import User

Section

Модель раздела состоит из:

  1. Наименование - title раздела
  2. URL - используется для формирование часть адреса на сайте, то есть хранятся не абсолютные пути, а часть пути к разделу
  3. Описание - которое выводится в начале страницы перед списком статей

Посмотрим на реализацию модели в коде:

class Section(models.Model):
    class Meta:
        db_table = "section"

    section_title = models.CharField(max_length=200)
    section_url = models.CharField(max_length=50)
    section_description = TextField()

    def __str__(self):
        return self.section_title

Класс Meta позволяет переопределить некоторые параметры модели, которые имеют непосредственное отношение к таблицам в базе данных. В данном случае определяется, как будет называться таблица.

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

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

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

Article

Модель состоит из следующих полей:

  1. Наименование статьи
  2. Раздел - это внешний ключ на таблицу Разделов, определяет принадлежности статьи к определённому разделу
  3. Автор - это внешний ключ на таблицу пользователей, из которых будет выбирать автор статьи
  4. Дата - дата и время публикации
  5. Контент - текстовое поле аналогично описанию, как в модели разделов
  6. Статус - предполагаю несколько статусов для статей, на данный момент используется лишь два:
    1. Черновик - значение 0
    2. Опубликовано - значение 1
class Article(models.Model):
    class Meta:
        db_table = "article"

    article_title = models.CharField(max_length=200)
    article_section = models.ForeignKey(Section)
    article_author = models.ForeignKey(User)
    article_date = models.DateTimeField('Дата публикации')
    article_content = TextField()
    article_status = models.IntegerField()

    def __str__(self):
        return self.article_title

Регистрация моделей в админке

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

from django.contrib import admin

from .models import Section, Article

admin.site.register(Section)
admin.site.register(Article)

Миграция

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

python manage.py makemigrations
python manage.py migrate

Примечание

Primary Key  в общем случае подставляется автоматически и является автоинкрементируемым, поэтому мы его не указываем.

Шаблоны

Шаблоны в Django просто замечательная вещь, особенно если учитывать то, что они могут наследоваться и определённые блоки будут переопределяться.

В проекте создан один базовый шаблон base.html в приложении home , от которого наследуются все остальные страницы. Условная структура шаблона следующая:

{% block head %}
{% endblock %}
{% block content %}
    {% block page %}
    {% endblock page %}
    {% block sidebar %}
    {% endblock %}
{% endblock content %}
{% block footer %}
{% endblock %}

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

index.html

Это шаблон основной страницы приложения, где выводится список всех разделов. С помощью оператора extends , мы указываем, что наследуемся от базового шаблона приложения home. А продублировав блок page, из базового шаблона и прописав в него содержимое мы переопределяем данный блок базового шаблона внося нужную нам информацию для конкретной страницы.

Как Вы могли заметить, в языке шаблонов Django используются конструкции if, for, url. Но откуда взялись переменные section_list и section? - section_list передаётся в качестве контекстного значения в шаблон при подготовке данных в представлении ( view ). Проверив, что список разделов существует, мы проходим по все элементам списка подставляя необходимые значения в шаблон. Поскольку section соответствует модели Section, то соответствующие названия полей модели подхватываются автоматически.

Что касается url , то данный оператор указывает, что необходимо взять шаблон адреса из приложения knowledge с именем section, а также мы знаем, что у этого шаблона адреса имеется одна переменная, которой мы присваиваем url объекта section. Данные шаблоны описываются в файле urls.py. Мы их рассмотрим чуть позже.

{% extends 'home/base.html' %}
{% block page %}
    <article>
    <h1>Разделы</h1>
    {% if section_list %}
        <ul>
        {% for section in section_list %}
            <li>
            <a href="{% url 'knowledge:section' section.section_url %}">{{ section.section_title }}</a>
            </li>
        {% endfor %}
        </ul>
    {% endif %}
    </article>
{% endblock %}

section.html

В шаблоне разделов происходит формирование списка статей, которые соответствуют данному раздлу. В качестве контекста в шаблон передаётся переменная section, которая содержит информацию об объекте раздела. Мы подставляем название раздела и его описание. Заметьте, что описание подставляется со специальным аргументом safe, который указывает на сохранение форматирования. Это необходимо сделать, чтобы сохранить html разметку, иначе пользователь увидит вместо красиво оформленного описания код html вёрстки.

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

А блоке if проверяется статус статьи, то есть те статьи, которые не опубликованы, отображаться не будут.

Что касается url, то опять используется шаблон адреса, но уже с двумя аргументами.

{% extends 'home/base.html' %}
{% block page %}
    <article>
    <h1>{{ section.section_title }}</h1>
        <p>{{ section.section_description|safe }}</p>
        <h2>Статьи</h2>
        <ul>
        {% for article in section.article_set.all|dictsort:'article_title' %}
            {% if article.article_status %}
            <li>
            <a href="{% url 'knowledge:article' section.section_url article.id %}">
                {{ article.article_title }}
            </a>
            </li>
            {% endif %}
        {% endfor %}
        </ul>
    </article>
{% endblock %} 

article.html

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

{% extends 'home/base.html' %}
{% block page %}
    <article>
        <h1>{{ article.article_title }}</h1>
        <p>{{ article.article_content|safe }}</p>
    </article>
{% endblock %}

Примечание

Если Вы посмотрите на структуру проекта, до увидите, что приложения содержат в папках templates ещё папки, которые имеют такое же наименование что и приложения, а уже в них находятся шаблоны страниц. Делается это для того, чтобы точно определять, какой шаблон будет использоваться. Django ищет шаблоны по всем папкам и выбирает первый шаблон, который соответствует наименованию. И если названия пересекаются, например index.html, то может быть выбран неправильный шаблон. Поэтому используется пространство имён, что точно определить нужный шаблон.

Представление

В данном случае работа с представления достаточно ограниченная, поэтому приведу сразу полный листинг файла views.py.

Мы наследуемся от класса View и переопределяем метод get, который отвечает за выполнение GET запроса, то есть запроса страницы пользователем. Также импортируем методы render_to_response, который будет обрабатывать шаблон в нужном нам контексте, и get_object_or_404 , который автоматически вернёт ошибку 404, если объект не будет найден, по соответствующей переменной.

В EKnowledgeIndex забираются абсолютно все разделы с сортировкой по наименованию и помещаются в контекст под именем section_list, Вы помните, что данная переменная использовалась в шаблоне index.html.

В ESectionView забирается уже конкретный Раздел, по аргументу, который передаётся в запрашиваемом url. kwargs как раз отвечает за переменные, которые вычленяются из url по шаблону, заданном в файле urls.py.

В EArticleView всё гораздо интереснее. Дело в том, что в шаблоне url используется две переменных, но для получения статьи достаточно сделать запрос по id, то есть Primary Key , который передаётся в url.

from django.views import View
from django.shortcuts import render_to_response, get_object_or_404

from .models import *


class EKnowledgeIndex(View):
    template_name = 'knowledge/index.html'

    def get(self, request, *args, **kwargs):
        context = {}
        context['section_list'] = Section.objects.all().order_by('section_title')

        return render_to_response(template_name=self.template_name, context=context)


class ESectionView(View):
    template_name = 'knowledge/section.html'

    def get(self, request, *args, **kwargs):
        context = {}
        section = get_object_or_404(Section, section_url=self.kwargs['section'])
        
        context['section'] = section

        return render_to_response(template_name=self.template_name, context=context)


class EArticleView(View):
    template_name = 'knowledge/article.html'

    def get(self, request, *args, **kwargs):
        context = {}
        article = get_object_or_404(Article, id=self.kwargs['article_id'])
        
        context['article'] = article

        return render_to_response(template_name=self.template_name, context=context)

URL шаблоны

Хорошо, всё что нужно написано, но осталось совсем немного, чтобы всё заработало, а именно настроить шаблоны url, по которым будут обрабатываться запросы.

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

mysite/mysite/urls.py

Для начала необходимо задать шаблоны в файле mysite/mysite/urls.py , который будет определять, в какое приложение направить запрос. Здесь Вы видите, что есть направление в приложение home, там отображается главная страница сайта, но я не буду углубляться в описание данной части, поскольку о ней мы не говорим в данной статье. Также имеется направление в админку сайта и в приложение knowledge, которое отвечает как раз за разделы и статьи.

Через include, подключаются файлы urls.py в приложениях home и knowledge.

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^', include('home.urls')),
    url(r'^knowledge/', include('knowledge.urls')),
    url(r'^admin/', admin.site.urls),
]

mysite/knowledge/urls.py

app_name отвечает за пространство имён в шаблонах. Помните? Чуть выше в шаблоне была запись 'knowledge:section'.

Ну а регулярочки определяют, что и куда будет направляться. Что касается записей <section> и <article_id>, то это как раз те переменный, которые обрабатывались в методе get в представлениях и передавались с помощью kwargs.

Ну а name указывает наименование URL шаблона, то есть вторая часть в 'knowledge:section'.

Ну и последнее, что хотелось бы отметить, так это символ ^ , который присутствует в шаблоне. С помощью него отбрасывается уже распознанная часть шаблона. То есть в этих url также присутствует в самом начале knowledge/ , которая была распознана ранее в mysite/mysite/urls.py.

from django.conf.urls import url

from . import views

app_name = 'knowledge'
urlpatterns = [
    url(r'^$', views.EKnowledgeIndex.as_view(), name='index'),
    url(r'^(?P<section>[\w]+)/$', views.ESectionView.as_view(), name='section'),
    url(r'^(?P<section>[\w]+)/(?P<article_id>[0-9]+)/$', views.EArticleView.as_view(), name='article')
]

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь

Qt - Тест 001. Сигналы и слоты

  • Результат 5 баллов
  • Очки рейтинга -10

C++ - Тест 003. Условия и циклы

  • Результат 57 баллов
  • Очки рейтинга -2

C++ - Тест 003. Условия и циклы

  • Результат 7 баллов
  • Очки рейтинга -10
Последние комментарии
  • EVILEG
  • 7 декабря 2017 г. 9:47

Django - Урок 011. Добавление комментариев на сайт с Django

Визуальный пример чего? комментариев? При ответе на конкретный комментарий рядом с ником отвечающего будет стрелочка и указание ник другого пользователя. Который будет ссылкой на коммента...

  • Bernar
  • 7 декабря 2017 г. 9:24

Django - Урок 011. Добавление комментариев на сайт с Django

есть визуальный пример ?

  • EVILEG
  • 6 декабря 2017 г. 11:30

Django - Урок 011. Добавление комментариев на сайт с Django

Да, так будет даже лучше, я на сайте уже обновил до такого вида код Вот это уже не нужно if request.method == 'POST': Поскольку Вы и так используете метод post, то есть эта про...

  • Bernar
  • 6 декабря 2017 г. 11:19

Django - Урок 011. Добавление комментариев на сайт с Django

сделал немного по другому class EArticleView(View): template_name = 'knowledge/article.html' comment_form = CommentForm def get(self, request, *args, **kwargs): ...

Сейчас обсуждают на форуме
  • Миша
  • 15 декабря 2017 г. 11:26

Как найти в QVector макс и мин

Спасибо

  • Galant
  • 14 декабря 2017 г. 19:58

LPT

Понял! Спасибо!

  • EVILEG
  • 14 декабря 2017 г. 13:38

QCustomPlot можно ли построить прерывистую линию на одном графике?

Во-первых: В pro файле проект по идее достаточно указать следующий define для включения возможности рендеринга через OpenGL DEFINES += QCUSTOMPLOT_USE_OPENGL И во вторых:...

  • EVILEG
  • 13 декабря 2017 г. 8:05

В многопоточности выполнять действие только в одном из потоков

Статическиe методs QThread::currentThread(); и QThread::currentThreadId() могут возвращать указатель на поток и его handle id соответственно. Можете попробовать через как...

  • EVILEG
  • 13 декабря 2017 г. 7:57

А что по поводу авторизации ?

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