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

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

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

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

Сергей Серов

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

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

from django.shortcuts import render, get_object_or_404, redirect
from django.conf import settings

from core.utils import pagination

from . import forms
from .models import Question


def index_view(request):
    questions = pagination(request, Question.public.all(), settings.QUESTIONS_PER_PAGE)
    if request.user.is_authenticated:
        form = forms.QuestionForm(request.POST or None)
        if form.is_valid():
            form.save(request.user, request.META['REMOTE_ADDR'])
            return redirect('questions:index')
    else:
        form = None
    return render(request, 'questions/index.html', {
        'questions': questions,
        'form': form,
    })


def question_view(request, slug, pk):
    question = get_object_or_404(Question.public, pk=pk)
    if question.slug != slug:
        # делаем постоянный редирект, слаг был изменен
        return redirect(question, permanent=True)
    if request.user.is_authenticated:
        form = forms.ArticleForm(request.POST or None)
        if form.is_valid():
            form.save(question, request.user, request.META['REMOTE_ADDR'])
            return redirect(question)
    else:
        form = None
    return render(request, 'questions/question.html', {
        'question': question,
        'form': form,
    })

В первом представлении мы для начала разбиваем список вопросов на страницы и получаем нужную страницу в зависимости от номера в запросе соответственно.

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

Второе представление отображает тему конкретного вопроса и ответов к нему.

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

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

Например, вот так выглядит обычная ссылка на вопрос:

/questions/obychnyi-zagolovok-tipovogo-voprosa-kotoryi-mozhet-zainteresovat-avtora-resursa-17/

Далее рассмотрим пример короткой ссылки:

/questions/q-17/

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

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

Лента последних вопросов

В каталоге приложения создаем файл 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 Question


class LatestQuestionsFeed(Feed):
    feed_type = Atom1Feed
    title = settings.SITE_TITLE
    link = reverse_lazy('questions:index')
    subtitle = 'Вопросы'

    def items(self):
        return Question.public.all()[:settings.QUESTIONS_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.created

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

Лента составлена аналогично, как и в приложении Статьи.

Карта вопросов

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

from django.contrib.sitemaps import GenericSitemap

from .models import Question

questions_sitemaps = {
    'questions': GenericSitemap({
        'queryset': Question.public,
        'date_field': 'updated',
    }, priority=0.7, changefreq=None),
}

Приоритет выставляем 0.7 поскольку вопросы являются второстепенным контентом.

Маршруты

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

from django.conf.urls import url

from . import views
from .feeds import LatestQuestionsFeed

app_name = 'questions'
urlpatterns = [
    url(r'^$', views.index_view, name='index'),
    url(r'^feed/$', LatestQuestionsFeed(), name='feed'),
    url(r'^(?P<slug>[\w-]+)-(?P<pk>\d+)/$', views.question_view, name='question'),
]

В директории проекта переходим в каталог 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 questions.sitemaps import questions_sitemaps
from articles.sitemaps import articles_sitemaps

sitemaps = {}
sitemaps.update(questions_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'^questions/', include('questions.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)),
    ]

Здесь мы добавляем карту вопросов и само приложение questions.