Cat

Django 37. Две формы - добавление категории и файла

В этом посте добавим две формы, создадим три представления, применив наследование и напишем два шаблона для страниц.

Все статьи

Icon Link

Дополнительные материалы

Icon Link

Реклама

Icon Link
Сайт на Django proDream 28 Ноябрь 2023 Просмотров: 822

В прошлом посте мы написали форму добавления поста автором. 
В этом посте добавим две формы: добавление новой категории и добавления файла для поста.

В процессе работы мы закрепим создание форм, создадим три представления, применив наследование, а также создадим два шаблона.

Однако есть и второй вариант, а именно создание нового связанного объекта (в нашем случае категория и файл поста) прямо на странице добавления нового поста. Примерно также, как это реализовано в панели администратора Django. Подробнее можно прочитать в посте на Boosty "Django - Добавление новой категории в форме создания поста без перезагрузки страницы" (платный контент).

 

Формы добавления категории и файла поста.

Откроем файл forms.py в директории приложения user_app.

 

Форма добавления категории.

Создадим новый класс AddCategoryByAuthorForm, унаследованный от ModelForm.

Так же, как мы делали в посте "Django 36. Добавление постов пользователем", создаём конструктор класса __init__.
В нём добавляем всем полям формы bootstrap-класс form-control и дополнительный класс для поля description - django_ckeditor_5, для работы визуального редактора Django CKEditor 5.

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

Далее создаём подкласс Meta, прописываем модель и необходимые поля.

 

Код формы:

from blog.models import CategoryModel


class AddCategoryByAuthorForm(forms.ModelForm):  
    def __init__(self, *args, **kwargs):  
        super().__init__(*args, **kwargs)  
        for field in self.fields:  
            self.fields[field].widget.attrs.update({'class': 'form-control', 'autofocus': ''})  

        self.fields['description'].widget.attrs.update({'class': 'django_ckeditor_5'})  

    class Meta:  
        model = CategoryModel  
        fields = ('title', 'parent', 'description', 'telegram_link')

 

Форма добавления файла.

Форма такая же, как и предыдущая. Исключение составляет только отсутствие дополнительного класса для поля, модель и сам список полей формы.

 

Код формы:

from blog.models import PostFilesModel


class AddFileByAuthorForm(forms.ModelForm):  
    def __init__(self, *args, **kwargs):  
        super().__init__(*args, **kwargs)  
        for field in self.fields:  
            self.fields[field].widget.attrs.update({'class': 'form-control', 'autofocus': ''})  

    class Meta:  
        model = PostFilesModel  
        fields = ('title', 'file')

 

Представления страницы добавления категории и страницы добавления файла поста.

Откроем файл views.py в директории приложения user_app.

Прочитав вступление, вы можете задаться вопросом: "зачем нам три представления, если будет две формы?". Всё верно, мы создадим три представления, а не два.
В посте "Django 27.1 Представления на основе классов" я описывал преимущества классовых представлений, одно из них - наследование
Оба представления практически идентичны, отчего было бы разумнее сделать общий класс представления и уже от него наследовать два конкретных представления для страниц.

 

Базовый класс.

Создадим класс AddCategoryAndFile с двойным наследованием: UserPassesTestMixin и CreateView.

В теле класса будет всего два метода:

  • test_func - для проверки того, что пользователь входит в группу "Автор". Подробнее об этом рассказывал в прошлом посте - "Django 36. Добавление постов пользователем".
  • get_success_url - метод, определяющий куда будет перенаправлен пользователь после заполнения формы. В нашем случае на страницу профиля.

Это всё, что будет в нашем классе. Остальные параметры будут заменены в классах-наследниках.

 

Код класса:

from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic import CreateView


class AddCategoryAndFile(UserPassesTestMixin, CreateView):  
    def test_func(self):  
        return self.request.user.groups.filter(name='Автор').exists()  

    def get_success_url(self):  
        return reverse('user_app:user_profile', kwargs={'username': self.request.user.username})

 

Представление добавления файла.

Создадим класс AddFileView, унаследованный от нашего класса AddCategoryAndFile.

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

  • template_name - шаблон страницы.
  • form_class - класс формы.
  • model - используемая модель.
  • extra_context - дополнительные данные, в нашем случае это заголовок страницы.

Это весь класс.

 

Код класса:

from blog.models import PostFilesModel


class AddFileView(AddCategoryAndFile):  
    template_name = 'user_app/add_file.html'  
    form_class = forms.AddFileByAuthorForm  
    model = PostFilesModel  
    extra_context = {'title': 'Добавление файла'}

 

Представление добавления категории.

Создадим класс AddCategoryView, унаследованный от нашего класса AddCategoryAndFile.

В этом представлении будут точно такие же поля, как и в предыдущем, а также один метод:

  • form_valid - в этом методе мы приводим поле slug к корректному виду. Почему это важно сделать, я объяснял в прошлом посте.

 

Код класса:

from blog.models import CategoryModel


class AddCategoryView(AddCategoryAndFile):  
    template_name = 'user_app/add_category.html'  
    form_class = forms.AddCategoryByAuthorForm  
    model = CategoryModel  
    extra_context = {'title': 'Добавление категории'}  

    def form_valid(self, form):  
        form.instance.slug = slugify(form.instance.title)  
        return super().form_valid(form)

 

URL-паттерны.

Откроем файл urls.py в директории приложения user_app и в список urlpatterns добавим следующие строки:

path('post/add_category/', views.AddCategoryView.as_view(), name='add_category'),  
path('post/add_file/', views.AddFileView.as_view(), name='add_file'),

 

Шаблоны страниц.

В директории с шаблонами приложения user_app создадим два файла:

  • add_category.html - файл с формой добавления категории.
  • add_file.html - файл с формой добавления файла поста.

Оба шаблона практически идентичны. 
Различия заключаются в том, что в форме добавления категории выводится {{ form.media }} для отображения визуального редактора, а в форме добавления файла указано enctype="multipart/form-data", означающее, что форма может передавать файлы.

 

Код страницы добавления категории:

{% extends 'blog/base.html' %}
{% block title %}{{ title }}{% endblock %}

{% block content %}
    <div class="container mt-3 d-flex justify-content-center">
        <div class="col-lg-8 col-sm-12">
            <h2>{{ title }}</h2>
            <form method="post">
                {% csrf_token %}
                {{ form.media }}
                {{ form.as_p }}
                <button type="submit" class="btn btn-primary my-btn-success mt-3">Сохранить</button>
            </form>
        </div>
    </div>
{% endblock %}

 

Код страницы добавления файла:

{% extends 'blog/base.html' %}
{% block title %}{{ title }}{% endblock %}

{% block content %}
    <div class="container mt-3 d-flex justify-content-center">
        <div class="col-lg-8 col-sm-12">
            <h2>{{ title }}</h2>
            <form method="post" enctype="multipart/form-data">
                {% csrf_token %}
                {{ form.as_p }}
                <button type="submit" class="btn btn-primary my-btn-success mt-3">Сохранить</button>
            </form>
        </div>
    </div>
{% endblock %}

 

Добавляем ссылки в меню пользователя.

Откройте файл header.html и под ссылкой на добавление нового поста добавьте следующий код:

<li>  
    <a class="dropdown-item dd-item-color" href="{% url 'user_app:add_category' %}">Добавить  
        категорию</a>  
</li>  
<li>  
    <a class="dropdown-item dd-item-color" href="{% url 'user_app:add_file' %}">Добавить  
        файл</a>  
</li>

 

Заключение.

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

Если вас заинтересует другой способ реализации подобного функционала (не в виде отдельных страниц, а непосредственно в форме добавления поста), то об этом я рассказываю в посте на Boosty "Django - Добавление новой категории в форме создания поста без перезагрузки страницы" (платный контент).

В следующем посте мы продолжим эту тему и затронем такие конструкции, как декораторы и миксины.

 

Автор

    Нет комментариев

    Реклама