Язык программирования Python

Создание блога на Django. Часть 6: Представления.

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

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

Теперь мы изменим урлы, чтобы они указывали на представление, которое мы создадим в следующем шаге. Для этого мы отредактируем наш файл simple_blog/urls.py и изменим существующий код на следующий:

from django.contrib import admin from django.urls import path, include from django.views.generic import TemplateView from django.conf.urls.static import static from django.conf import settings urlpatterns = [ path('admin/', admin.site.urls), path('', include(('posts.urls', 'posts'), namespace='posts')), path( route='sobre-mi', view=TemplateView.as_view(template_name='about.html'), name='about' ), path('', include(('users.urls', 'users'), namespace='users')), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Code language: JavaScript (javascript)

Как вы видите, мы сделали то же самое, что и для пользователей. Теперь мы собираемся сохранить все урлы постов внутри posts/urls.py. Мы также добавили строку static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), которая уже была в настройках и используется для доступа к файлам, размещенным в папке media.

Далее мы создаем файл posts/urls.py и добавляем следующий код, чтобы наши url указывали на функции, которые мы собираемся создать в наших представлениях:

"""Posts URLs.""" # Django from django.urls import path # Views from posts import views urlpatterns = [ path( route='', view=views.PostsFeedView.as_view(), name='blog' ), path( route='posts/<slug:url>/', view=views.PostDetailView.as_view(), name='detail' ), path( route='posts/save_comment', view=views.save_comment, name='save_comment' ), ]
Code language: PHP (php)

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

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

"""User forms.""" # Django from django import forms # Models from comments.models import Comment from django.contrib.auth.models import User class CreateCommentForm(forms.ModelForm): """Post model form.""" comment = forms.CharField(widget=forms.Textarea) class Meta: """Form settings.""" model = Comment fields = ('user', 'profile', 'post', 'comment')

Здесь мы создаем поле типа textarea, которое будет хранить комментарий пользователя. А в классе Meta мы указываем ему модель ссылки и поля, которые он должен хранить. В данном случае пользователя, который написал его, пост, на который он ссылается, и комментарий.

Теперь, когда у нас есть информация для отображения, мы собираемся получить ее в представлении, чтобы она могла быть отображена позже на нашем сайте. Для этого мы откроем файл posts/views.py и добавим следующие строки:

from django.shortcuts import render, redirect from django.views.generic import DetailView, ListView from django.contrib.auth.decorators import login_required from django.http import HttpResponse # Models from posts.models import Post from categories.models import Category from comments.models import Comment # Forms from comments.forms import CreateCommentForm class PostsFeedView(ListView): """Index.""" template_name = 'posts/index.html' model = Post ordering = ('-created',) paginate_by = 10 context_object_name = 'posts' queryset = Post.objects.filter(is_draft=False) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['categories'] = Category.objects.all() return context class PostDetailView(DetailView): """Detail post.""" template_name = 'posts/detail.html' model = Post context_object_name = 'post' slug_field = 'url' slug_url_kwarg = 'url' def get_queryset(self): return Post.objects.filter(is_draft=False) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['categories'] = Category.objects.all() context['comments'] = Comment.objects.filter(post=self.get_object()).all() context['form_comments'] = CreateCommentForm() return context @login_required def save_comment(request): if request.method == 'POST': url = request.POST['url'] post = { 'user': request.user.id, 'profile': request.user.id, 'comment': request.POST['comment'], 'post': request.POST['post'] } form = CreateCommentForm(post) if form.is_valid(): form.save() return redirect('posts:detail', url=url) else: return HttpResponse(status=405) return HttpResponse(status=500)

В этом файле у нас есть три представления, которые вызываются из урлов, давайте объясним, что они делают:

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

  • template_name: Шаблон, в который будет поступать вся информация.
  • model: Модель, которую мы будем использовать, в данном случае Post.
  • ordering: Последовательность появления постов, в нашем случае мы хотим, чтобы они появлялись в порядке убывания.
  • paginate_by: Количество постов для отображения на странице.
  • queryset: Для добавления дополнительных условий, мы не хотим, чтобы черновики отображались.

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

Класс PostDetailView имеет тип DetailView и отвечает за отображение конкретного поста. Для этого он получает заголовок в url и использует его в качестве запроса. Для извлечения он использует slug_field = ‘url’ и slug_url_kwarg = ‘url’.

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

Наконец, у нас есть функция saveComment с декоратором @login_required, который заставляет нас войти в систему, чтобы сохранить комментарий. Мы также проверим, что вызов сделан с использованием метода POST, и если это не так, мы вернем ошибку. Если все прошло успешно, мы сохраняем его и перезагружаем страницу, на которой находимся.

Теперь, когда представление завершено, мы собираемся дать функциональность всем ссылкам на сайте и показать посты и категории. Для этого откроем наш файл templates/base.html и заменим его код на следующий:

<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>{% block title %}Simple blog{% endblock %}</title> {% load bootstrap4 %} {% bootstrap_css %} {% load static %} <link rel="stylesheet" href="{% static 'css/blog-home.css' %}"> </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top"> <div class="container"> <a class="navbar-brand" href="{% url 'posts:blog' %}">Simple Blog</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav ml-auto"> <li class="nav-item active"> <a class="nav-link" href="{% url 'posts:blog' %}">Blog <span class="sr-only">(current)</span> </a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'about' %}">Обо мне</a> </li> {% if user.is_authenticated %} <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{{request.user.username}}</a> <div class="dropdown-menu"> <a class="dropdown-item" href="{% url 'users:logout' %}">Logout</a> </div> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:login' %}">Вход</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'users:register' %}">Регистрация</a> </li> {% endif %} </ul> </div> </div> </nav> <!-- Page Content --> <div class="container"> {% block content %}{% endblock %} <!-- Sidebar Widgets Column --> <div class="col-md-4"> <!-- Categories Widget --> <div class="card my-4"> <h5 class="card-header">Categories</h5> <div class="card-body"> <div class="row"> <div class="col-lg-12"> <ul class="list-unstyled mb-0"> {% for category in categories %} <li> <a href="#">{{category.name}}</a> </li> {% endfor %} </ul> </div> </div> </div> </div> <!-- Side Widget --> <div class="card my-4"> <h5 class="card-header">Side Widget</h5> <div class="card-body"> You can put anything you want inside of these side widgets. They are easy to use, and feature the new Bootstrap 4 card containers! </div> </div> </div> </div> <!-- Footer --> <footer class="py-5 bg-dark"> <div class="container"> <p class="m-0 text-center text-white">Copyright &copy; Simple Blog {% now "Y" %}</p> </div> <!-- /.container --> </footer> {% bootstrap_javascript jquery='full' %} </body> </html>
Code language: HTML, XML (xml)

В этом файле наиболее важными изменениями, которые мы добавили, являются урлы, условие для проверки того, вошел ли пользователь в систему с помощью {% if user.is_authenticated %} и если да, то мы рисуем кнопку с опцией выхода из системы, в противном случае – опцию для перехода на экран регистрации и входа. Кроме того, мы размещаем список категорий.

Далее откроем файл templates/posts/index.html и изменим его содержимое следующим кодом:

{% extends "base.html" %} {% block content %} <div class="row"> <div class="col-md-8"> <h1 class="my-4">Simple Blog </h1> {% for post in posts %} <div class="card mb-4"> <img class="card-img-top" src="{{ post.image_header.url }}" alt="Card image cap"> <div class="card-body"> <h2 class="card-title">{{post.title}}</h2> <p class="card-text">{{post.post|safe|truncatechars:300}}</p> <a href="{% url 'posts:detail' post.url%}" class="btn btn-primary">Leer más &rarr;</a> </div> <div class="card-footer text-muted"> Escrito el {{post.created}} <a href="#">{{post.user.username}}</a> </div> </div> {% endfor %} <ul class="pagination justify-content-center mb-4"> <li class="page-item"> <a class="page-link" href="#">&larr; Older</a> </li> <li class="page-item disabled"> <a class="page-link" href="#">Newer &rarr;</a> </li> </ul> </div> {% endblock %}
Code language: JavaScript (javascript)

Здесь мы добавляем данные постов и заменяем их на те, которые были у нас в качестве теста. Наиболее заметной будет ссылка на запись, которая выглядит следующим образом {% url ‘posts:detail’ post.url%}. Мы берем url поста и объединяем сгенерированный нами url со slug, чтобы создать ссылку на него.

В конце отредактируем файл templates/posts/details.html и заменим существующий код на этот:

{% extends "base.html" %} {% block content %} <div class="container"> <div class="row"> <div class="col-lg-8"> <h1 class="mt-4">{{post.title}}</h1> <p class="lead"> por <a href="#">{{post.user.username}}</a> </p> <hr> <p>Escrito el {{post.created}}</p> <hr> <img class="img-fluid rounded" src="{{ post.image_header.url }}" alt=""> <hr> <p class="lead">{{post.post|safe}}</p> <hr> {% if user.is_authenticated %} <div class="card my-4"> <h5 class="card-header">Оставить комментарий:</h5> <div class="card-body"> <form method="POST" action="{% url "posts:save_comment" %}"> {% csrf_token %} <input type="hidden" name="url" value="{{post.url}}" /> <input type="hidden" name="post" value="{{post.id}}" /> <div class="form-group"> <textarea class="form-control" name="comment" rows="3"></textarea> </div> <button type="submit" class="btn btn-primary">Отправить</button> </form> </div> </div> {% endif %} {% for comment in comments %} <div class="media mb-4"> {% if comment.user.profile.photo %} <img class="d-flex mr-3 rounded-circle" width="50px" height="50px" src="/media/{{ comment.user.profile.photo }}" alt=""> {% else %} <img class="d-flex mr-3 rounded-circle" src="https://placehold.it/50x50" alt=""> {% endif %} <div class="media-body"> <h5 class="mt-0">{{comment.user.username}}</h5> {{comment.comment}} </div> </div> {% endfor %} </div> {% endblock %}
Code language: HTML, XML (xml)

Как и в index, мы добавляем данные о посте и модифицируем форму, чтобы иметь возможность сохранять комментарии, которые пишет пользователь. Он также показывает список всех комментариев, если они есть у поста.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *