Простой блог на 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
Вы должны авторизоваться, чтобы оставлять комментарии.
Комментарии ()