В этом шаге мы создадим и отобразим форму, в которой пользователи смогут обновлять свой профиль.
Для создания форм мы будем использовать ModelForm. Она позволяет нам создавать формы, которые взаимодействуют с определенной моделью внутри базы данных.
Для этого в нашем приложении accounts создадим файл forms.py. И добавим в него следующий код:
from django import forms
from django.contrib.auth.models import User
from .models import Profile
class UserUpdateForm(forms.ModelForm):
"""
Форма обновления данных пользователя
"""
username = forms.CharField(max_length=100,
widget=forms.TextInput(
attrs={"class": "form-control mb-1"}))
email = forms.EmailField(widget=forms.TextInput(attrs={"class": "form-control mb-1"}))
first_name = forms.CharField(max_length=100,
widget=forms.TextInput(attrs={"class": "form-control mb-1"}))
last_name = forms.CharField(max_length=100,
widget=forms.TextInput(attrs={"class": "form-control mb-1"}))
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name')
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
class ProfileUpdateForm(forms.ModelForm):
"""
Форма обновления данных профиля пользователя
"""
slug = forms.CharField(max_length=100,
widget=forms.TextInput(
attrs={"class": "form-control mb-1"}))
birth_date = forms.DateField(
widget=forms.TextInput(attrs={"class": "form-control mb-1"}))
bio = forms.CharField(max_length=500,
widget=forms.Textarea(attrs={'rows': 5, "class": "form-control mb-1"}))
avatar = forms.ImageField(widget=forms.FileInput(attrs={"class": "form-control mb-1"}))
class Meta:
model = Profile
fields = ('slug', 'birth_date', 'bio', 'avatar')
UserUpdateForm взаимодействует с моделью пользователя, позволяя пользователям обновлять свое имя пользователя и адрес электронной почты, а также имя и фамилию.
ProfileUpdateForm взаимодействует с моделью профиля, позволяя пользователям обновлять свой профиль.
В классе Meta мы указываем model - модель для обработки, а также fields - наши поля.
В методе clean_email() мы сделали возможность проверки email на уникальность присутствия в базе данных, если такой email уже используется, то мы запрещаем пользователю его устанавливать.
Создание представлений для просмотра профиля, редактирования профиля
Теперь перейдем к созданию необходимых представлений в файле views.py нашего приложения accounts.
from django.views.generic import DetailView, UpdateView
from django.db import transaction
from django.urls import reverse_lazy
from .models import Profile
from .forms import UserUpdateForm, ProfileUpdateForm
class ProfileDetailView(DetailView):
"""
Представление для просмотра профиля
"""
model = Profile
context_object_name = 'profile'
template_name = 'accounts/profile_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'Профиль пользователя: {self.object.user.username}'
return context
class ProfileUpdateView(UpdateView):
"""
Представление для редактирования профиля
"""
model = Profile
form_class = ProfileUpdateForm
template_name = 'accounts/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(ProfileUpdateView, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('profile_detail', kwargs={'slug': self.object.slug})
Для первого представления на основе классов мы использовали класс DetailView, для получения одного объекта.
- Задали свойство
context_object_name, какprofileдля использования переменных в шаблоне.
- Добавили контекст для заголовка страницы
f'Профиль пользователя: {self.object.user.username}'.
Для второго представления мы используем класс UpdateView для редактирования профиля. Мы импортируем и используем созданные формы из файла forms.py.
- В методе
get_object()мы передаем текущего пользователя, чтобы не редактировать чужие профили.
- В контексте мы добавляем форму пользователя, где ссылаемся на текущего пользователя.
- В методе
form_valid()мы используемtransaction.atomic, для корректного сохранения данных двух форм в нашей БД.
- Проверяем обе формы на правильность, и сохраняем их.
- В методе
get_success_url()мы ссылаемся на наш профиль, т.е после сохранения мы переходим на страницу нашего профиля.
Далее нам необходимо указать обработку представлений в файле urls.py, для этого мы его создадим в приложении accounts и добавим следующий код:
from django.urls import path
from .views import ProfileUpdateView, ProfileDetailView
urlpatterns = [
path('user/edit/', ProfileUpdateView.as_view(), name='profile_edit'),
path('user/<str:slug>/', ProfileDetailView.as_view(), name='profile_detail'),
]
И подключим в основном файле urls.py обработку ссылок из приложения accounts.
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('apps.blog.urls')),
path('', include('apps.accounts.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += [path('__debug__/', include('debug_toolbar.urls'))]