Создание сайта на Django: Урок 21, профиль пользователя в Django: часть 3 (редактирование профиля, формы)
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Django»
Комментариев: 0

Создание форм в Django для редактирования профиля через OneToOneField 

Для декомпозиции проекта, в модуле system я создам папку forms с исполняющим файлом __init__.py и с файлом profiles.py

Выглядеть это будет следующим образом...

Напоминаю, что вы можете не использовать такую же структуру папок, как использую я. 

Для меня такое разделение - чистота, понятливость, четкость, декомпозиция, где что находится.

Перейдем к созданию формы для модели профиля:

modules/system/forms/profiles.py

from django import forms
from django.contrib.auth.models import User

from modules.system.models import Profile


class ProfileUpdateForm(forms.ModelForm):
    """
    Форма обновления данных профиля пользователя
    """
    class Meta:
        model = Profile
        fields = ('slug', 'status', 'bio', 'avatar', 'date_birthday')

    def __init__(self, *args, **kwargs):
        """
        Обновление стилей формы обновления
        """
        super().__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs.update({
                'class': 'form-control',
                'autocomplete': 'off'
            })

И для модели пользователя (в том же файле):

class UserUpdateForm(forms.ModelForm):
    """
    Форма обновления данных пользователя
    """

    class Meta:
        model = User
        fields = ('username', 'email', 'first_name', 'last_name')

    def __init__(self, *args, **kwargs):
        """
        Обновление стилей формы обновления
        """
        super().__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs.update({
                'class': 'form-control',
                'autocomplete': 'off'
            })

    def clean_email(self):
        """
        Проверка email на уникальность
        """
        email = self.cleaned_data.get('email')
        username = self.cleaned_data.get('username')
        if email and User.objects.filter(email=email).exclude(username=username).exists():
            raise forms.ValidationError('Email адрес должен быть уникальным')
        return email

Получится вместе так:

from django import forms
from django.contrib.auth.models import User

from modules.system.models import Profile


class ProfileUpdateForm(forms.ModelForm):
    """
    Форма обновления данных профиля пользователя
    """
    class Meta:
        model = Profile
        fields = ('slug', 'bio', 'avatar', 'date_birthday')

    def __init__(self, *args, **kwargs):
        """
        Обновление стилей формы обновления
        """
        super().__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs.update({
                'class': 'form-control',
                'autocomplete': 'off'
            })
            

class UserUpdateForm(forms.ModelForm):
    """
    Форма обновления данных пользователя
    """

    class Meta:
        model = User
        fields = ('username', 'email', 'first_name', 'last_name')

    def __init__(self, *args, **kwargs):
        """
        Обновление стилей формы обновления
        """
        super().__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs.update({
                'class': 'form-control',
                'autocomplete': 'off'
            })

    def clean_email(self):
        """
        Проверка email на уникальность
        """
        email = self.cleaned_data.get('email')
        username = self.cleaned_data.get('username')
        if email and User.objects.filter(email=email).exclude(username=username).exists():
            raise forms.ValidationError('Email адрес должен быть уникальным')
        return email

Пояснения:

  • Наши формы основаны на классе ModelForm. Наследуемся от него.
  • В мета мы указали необходимую модель за параметр, а также необходимые поля для показа при редактировании формы.
  • В методе dif __init__ мы обновляем стили всех полей, добавляя им атрибуты из Bootstrap, а именно class="form-control" 
  • В методе def clean_email мы проверяем введенный email на уникальность, что такого в базе данных нет. 

Таким образом, у нас есть две формы для пользователя. Теперь ими нужно воспользоваться, для этого мы создадим новое представление в views/profiles.py, не забываем про все импорты!

modules/system/views/profiles.py

from modules.system.forms.profiles import ProfileUpdateForm, UserUpdateForm
from django.views.generic import UpdateView

from django.db import transaction
from django.urls import reverse_lazy

class ProfileEditView(UpdateView):
    """
    Представление для редактирования профиля пользователя.
    """
    model = Profile
    form_class = ProfileUpdateForm
    template_name = 'modules/system/profiles/profile-edit.html'

    def get_object(self, queryset=None):
        return self.request.user.profile

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = f'Редактирование профиля пользователя: {self.request.user.username}'
        if self.request.POST:
            context['user_form'] = UserUpdateForm(self.request.POST, instance=self.request.user)
        else:
            context['user_form'] = UserUpdateForm(instance=self.request.user)
        return context

    def form_valid(self, form):
        context = self.get_context_data()
        user_form = context['user_form']
        with transaction.atomic():
            if all([form.is_valid(), user_form.is_valid()]):
                user_form.save()
                form.save()
            else:
                context.update({'user_form': user_form})
                return self.render_to_response(context)
        return super(ProfileEditView, self).form_valid(form)

    def get_success_url(self):
        return reverse_lazy('profile', kwargs={'slug': self.object.slug})

Пояснения:

  • Мы используем представление на основе классов обновления контента -  UpdateView.
  • В методе get_object мы передаем текущего пользователя, чтобы не редактировать чужие профили.
  • В контексте мы добавляем форму пользователя, где  ссылаемся на текущего пользователя.
  • В методе form_valid мы используем transaction.atomic, для корректного сохранения данных в нашей БД. 
  • Проверяем обе формы на правильность, и сохраняем их. 
  • В методе get_success_url мы ссылаемся на наш профиль, т.е после сохранения мы переходим к нашему профилю.

Давайте добавим в urls.py наше представление:

modules/system/urls.py

from django.urls import path

from modules.system.views import ProfileView, ProfileEditView

urlpatterns = [
    path('user/edit/', ProfileEditView.as_view(), name='profile-edit'),
    path('user/<str:slug>/', ProfileView.as_view(), name='profile'),
]

Пояснения:

  • Обязательно разместите user/edit/ первее user/<str:slug>/, чтобы не было проблем совпадающими именами ссылок.

Отлично, теперь создадим шаблон profile-edit.html в папке templates/modules/system/profiles/

templates/modules/system/profiles/profile-edit.html

{% extends 'main.html' %}
{% block content %}
<div class="card mb-3 border-0 nth-shadow">
   <div class="card-body">
      <div class="card-title nth-card-title">
         <h4>Изменение профиля</h4>
      </div>
      <form method="post" enctype="multipart/form-data">
         {% csrf_token %}
         {{ user_form.as_p }}
         {{ form.as_p }}
         <div class="d-grid gap-2 d-md-block mt-2">
            <button type="submit" class="btn btn-dark">Подтвердить изменение профиля</button>
         </div>
      </form>
   </div>
</div>
{% endblock %}

Пояснения:

  • Мы добавили рендеринг двух форм, а также {% csrf_token %} для защиты от фейковых данных и взломов.
  • Добавили enctype="multipart/form-data" к форме для загрузки медиа файлов!

Давайте посмотрим на результат:

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

Чтож, все сохранилось! И это отлично. Аватарка изменилась, мы перешли в свой же профиль. В следующем уроке мы рассмотрим теорию о CRUD запросах (UpdateView, CreateView, DeleteView), а потом рассмотрим миксины.

Комментарии к статье 0
Комментариев нет
Форма добавления комментария (необходима регистрация)