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

В настоящее время я публично описываю процесс создания веб-сайта с нуля на Django и Bootstrap, где задача разработать такие приложения как: аккаунты, вопросы, статьи и книги.

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

Создание веб-сайта с нуля на Django и Bootstrap. Статьи. Список статей и их содержимое. Представления

Сергей Серов

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

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

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

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

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

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

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

В директории приложения открываем views.py и наполняем следующим:

from django.shortcuts import render, get_object_or_404, redirect

from .models import Article


def index_view(request):
    articles = Article.public.all()
    return render(request, 'articles/index.html', {
        'articles': articles,
    })


def article_view(request, slug, pk):
    if request.user.is_superuser:
        # получаем все объекты для предварительного просмотра
        queryset = Article.objects
    else:
        queryset = Article.public
    article = get_object_or_404(queryset, pk=pk)
    if article.slug != slug:
        # делаем постоянный редирект, слаг был изменен
        return redirect(article, permanent=True)
    return render(request, 'articles/article.html', {
        'article': article,
    })

Первое представление покажет нам список всех опубликованных статей на данный момент.

Второе представление призвано показать нам содержимое конкретной статьи.

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

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

Например, при первой публикации статьи, мы получили следующую ссылку:

/articles/staryi-zagolovok-stati-14/

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

Иногда в процессе работы появляется необходимость изменить заголовок статьи, например, добавить слово и соответственно после сохранения маршрут изменится:

/articles/novyi-zagolovok-stati-14/

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

Лента последних публикаций

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

В каталоге приложения создаем файл feeds.py:

from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Atom1Feed
from django.urls import reverse_lazy
from django.conf import settings

from .models import Article


class LatestArticlesFeed(Feed):
    feed_type = Atom1Feed
    title = settings.SITE_TITLE
    link = reverse_lazy('articles:index')
    subtitle = settings.SITE_SUBTITLE
    author_name = settings.SITE_AUTHOR

    def items(self):
        return Article.public.all()[:settings.ARTICLES_ITEMS_IN_FEED]

    def item_title(self, item):
        return item.title

    def item_description(self, item):
        return item.summary

    def item_pubdate(self, item):
        return item.published

    def item_updateddate(self, item):
        return item.updated

Ленты можно выводить в двух форматах: RSS и Atom. Последний более продвинутый — мы используем его.

Далее устанавливаем заголовок и подзаголовок ленты с указанием авторства и ссылки на страницу статей.

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

Затем мы начинаем работать непосредственно с объектами ленты.

В методе items мы получаем только публичные объекты с заданным ограничением, которое установим чуть позже. Количество зависит от частоты появления нового материала. Например, если вы публикуете статью раз в неделю, то последние десять публикаций будут вполне уместным выбором.

Остальные методы позволяют нам внести подробности к каждому объекту, указывая заголовок, резюме, дату публикации и обновления статьи.

Карта статей

Функционал для создания карты сайта тоже присутствует в арсенале Django.

В директории приложения создаем файл sitemaps.py:

from django.contrib.sitemaps import GenericSitemap

from .models import Article

articles_sitemaps = {
    'articles': GenericSitemap({
        'queryset': Article.public,
        'date_field': 'updated',
    }, priority=1.0, changefreq=None),
}

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

Выставляем максимальный приоритет поскольку статьи — это самый важный контент на сайте.

Настройки

Открываем файл настроек проекта settings.py и добавляем:

ARTICLES_ITEMS_IN_FEED = 20  # кол-во статей в фиде

Далее добавляем приложение sitemaps:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sitemaps',
    'ckeditor',
    'ckeditor_uploader',
    'core.apps.CoreConfig',
    'accounts.apps.AccountsConfig',
    'articles.apps.ArticlesConfig',
]

Маршруты

В каталоге приложения создаем файл urls.py:

from django.conf.urls import url

from . import views
from .feeds import LatestArticlesFeed

app_name = 'articles'
urlpatterns = [
    url(r'^$', views.index_view, name='index'),
    url(r'^feed/$', LatestArticlesFeed(), name='feed'),
    url(r'^(?P<slug>[\w-]+)-(?P<pk>\d+)/$', views.article_view, name='article'),
]

В директории проекта переходим в каталог zero, затем открываем urls.py и приводим к следующему виду:

from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic.base import RedirectView
from django.contrib.sitemaps.views import sitemap
from django.conf.urls.static import static
from django.conf import settings

from articles.sitemaps import articles_sitemaps

sitemaps = {}
sitemaps.update(articles_sitemaps)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^ckeditor/', include('ckeditor_uploader.urls')),
    url(r'^accounts/', include('accounts.urls')),
    url(r'^articles/', include('articles.urls')),
    url(r'^$', RedirectView.as_view(pattern_name='articles:index'), name='index'),
    url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, name='sitemap'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


# Django Debug Toolbar

if settings.DEBUG:
    import debug_toolbar
    urlpatterns += [
        url(r'^__debug__/', include(debug_toolbar.urls)),
    ]

Мы подключили маршруты приложения articles и установили на него перенаправление с главной страницы сайта.

Затем подключили представление карты сайта для поисковых систем.