Простой блог на Django - часть 13. Реализуем возможность создания, редактирования и удаления поста на frontend

Первым делом реализуем функционал по созданию поста из личного кабинета. Начнём как всегда с добавлением маршрута: 

path("new/", views.new_post, name="new_post"),

Чтобы создать пост, как минимум необходимы будут два поля - заголовок и собственно само содержимое. Для этого создадим класс формы в файле posts/forms.py:

from django import forms
from .models import Post class PostForm(forms.ModelForm): class Meta: model = Post fields = ['title', 'text']

Здесь в свойстве fields мы указываем поля формы, которые будем связывать с моделью. А в самом свойстве model указываем модель, в которую будем сохранять данные из формы. 

Далее перейдём к View функции:

@login_required
def new_post(request):
  form = PostForm(request.POST or None, files=request.FILES or None)
  if form.is_valid():
    post = form.save(commit=False)
    post.author = request.user
    post.save()
    return redirect('posts:index')
context = { 'header': 'Добавить запись', 'submit_text': 'Добавить', 'form': form, }
return render(request, 'new_post.html', context)

Здесь мы используем декоратор login_required, который выполняет функцию только в том случае, если пользователь авторизован. Сама же логика функции состоит в том, что если форма не отправлена, то мы рендерим шаблон new_post.html и передаём ему словарь context, который помимо объекта формы содержит так же и надписи на кнопках. Зачем передавать надписи на кнопках? Дело в том, что мы будем использовать один и тот же шаблон как для создания, так и для редактирования формы; поэтому логично не создавать отдельные шаблоны, а использовать один, передавая ему только те данные, которые будут отличаться. Если же форма отправлена, то происходит её валидация с помощью form.is_valid() и в случае успеха, данные из формы передаются в объект PostForm и сохраняются с помощью метода save(). Так же хочу обратить внимание на то, как происходит связь поста с пользователем, который его создал:

post.author = request.user

Не забываем импортировать все необходимые функции:

from django.contrib.auth.decorators import login_required
from .forms import PostForm

Создаём шаблон new_post.html:

{% extends "index.html" %}
{% block content %}
<div class="row justify-content-center">
  <div class="col-md-10 p-5">
    <div class="card">
      <div class="card-body">
        <form method="post" enctype="multipart/form-data">
          {% csrf_token %}

          {% for field in form %}
            <div class="form-group row" aria-required={% if field.field.required %}"true"{% else %}"false"{% endif %}>
              <label for="{{ field.id_for_label }}" class="col-md-4 col-form-label text-md-right">
{{ field.label }}{% if field.field.required %}<span class="required">*</span>{% endif %}
</label> <div class="col-md-6"> {{ field }}   {% if field.help_text %} <small id="{{ field.id_for_label }}-help" class="form-text text-muted">{{ field.help_text|safe }}</small> {% endif %} </div> </div> {% endfor %} <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ submit_text }} </button> </div> </form> </div> </div> </div> </div> {% endblock %}

Чтобы проверить как всё работает, переходим по адресу: http://127.0.0.1:8000/new/

Редактирование статьи

Собственно тут всё аналогично созданию поста, но с небольшими отличиями. Объявляем маршрут:

path("<str:username>/<int:post_id>/edit/", views.post_edit, name="post_edit"),

Создаём функцию, на которую он ссылается:

def post_edit(request, username, post_id):
  if request.user.username != username:
    return redirect('posts:post', username, post_id)

post = get_object_or_404(Post, author__username=username, id=post_id) form = PostForm( request.POST or None, files=request.FILES or None, instance=post ) if form.is_valid(): post = form.save(commit=False) post.save() return redirect('posts:post', post_id)
context = { 'header': 'Редактировать запись', 'submit_text': 'Сохранить', 'form': form, 'post': post, }
return render(request, 'new_post.html', context)

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

post = get_object_or_404(Post, author__username=username, id=post_id)
form = PostForm(
request.POST or None, files=request.FILES or None, instance=post
)

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

Ну и конечно, для удобства выносим ссылку на редактирование в файл post_in_cat.html

<div class="btn-group">
{% if user == post.author %}
  <a class="btn btn-sm btn-info" href="{% url 'posts:post_edit' post.author post.id %}" role="button">
    Редактировать
  </a>
{% endif %}
</div>

Удаление статьи

Здесь уже работаем по накатанной - прописываем маршрут: 

path("<str:username>/<int:post_id>/delete/", views.post_delete, name="post_delete"),

Функция View выглядит следующим образом:

def post_delete(request, username, post_id):
if request.user.username != username:
return redirect('posts:post', username, post_id)
post = get_object_or_404(Post, author__username=username, id=post_id)
post.delete()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))

Здесь тоже первым делом проверяем, принадлежит ли пост пользователю, чтобы тот не смог удалить материал, к которому он не имеет отношения. Ну и далее по классике получаем объект класса Post и удаляем его с помощью метода delete(). Последняя строка отвечает за редирект на ту страницу, с которой мы ушли. Поэтому импортируем  соответствующую функцию:

from django.http import HttpResponseRedirect

Ну и конечно нам надо вывести эту ссылку в личном кабинете - сделаем это там же, где находится ссылка для редактирования. Для разнообразия, поскольку мы используем фреймворк Bootstrap, реализуем этот функционал в модальном окне. Ссылка будет выглядеть так:

<a class="btn btn-sm btn-danger" role="button" data-toggle="modal" data-target="#post-delete-{{post.id}}" href="#">Удалить</a>

а само модальное окно:

{% if user == post.author %}
  <div id="post-delete-{{post.id}}" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenter">
    <div class="modal-dialog modal-dialog-centered">
      <div class="modal-content">
        <div class="card-header">
          <div class="modal-body text-center">Вы уверены что хотите это удалить?</div>
          <div class="d-flex justify-content-between align-items-center">
            <a class="btn btn-danger" type="button" href="{% url 'posts:post_delete' post.author.username post.id %}">Удалить</a>
            <a class="btn btn-secondary"type="button" data-dismiss="modal">Отмена</a>
          </div>
        </div>
      </div>
    </div>
  </div>
{% endif %}

Но чтобы функционал модальных окон работал, необходимо правильно указать путь для статических Javascript файлов в базовом шаблоне index.html:

<!-- Bootstrap core JavaScript -->
<script src="{% static 'vendor/jquery/jquery.min.js' %}"></script>
<script src="{% static 'vendor/bootstrap/js/bootstrap.bundle.min.js' %}"></script>

<!-- Custom scripts for this template -->
<script src="{% static 'js/clean-blog.min.js' %}"></script>

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

Код, который упоминается в этой заметке, можно посмотреть здесь - https://github.com/maclen2007/simple_django_blog/commit/34d23c07bb36e5ccd03afe0df3887f0d28c4d645

Комментарии ()

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