В этом шаге мы создадим и отобразим форму, в которой пользователи смогут обновлять свой профиль.
Для создания форм мы будем использовать ModelForm. Она позволяет нам создавать формы, которые взаимодействуют с определенной моделью внутри базы данных.
Добавим новые классы в наш accounts/forms.py:
from .models import Profile
class UpdateUserForm(forms.ModelForm):
username = forms.CharField(max_length=100,
required=True,
widget=forms.TextInput())
email = forms.EmailField(required=True,
widget=forms.TextInput())
class Meta:
model = User
fields = ['username', 'email']
class UpdateProfileForm(forms.ModelForm):
avatar = forms.ImageField(widget=forms.FileInput())
bio = forms.CharField(widget=forms.Textarea(attrs={'rows': 5}))
class Meta:
model = Profile
fields = ['avatar', 'bio']
UpdateUserForm взаимодействует с моделью пользователя, позволяя пользователям обновлять свое имя пользователя и адрес электронной почты.
UpdateProfileForm взаимодействует с моделью профиля, позволяя пользователям обновлять свой профиль.
Теперь давайте обновим наше представление profile в файле accounts/views.py, чтобы добавить формы, которые мы только что создали:
from .forms import SignUpForm, LoginForm, UpdateUserForm, UpdateProfileForm
@login_required
def profile(request):
if request.method == 'POST':
user_form = UpdateUserForm(request.POST, instance=request.user)
profile_form = UpdateProfileForm(request.POST, request.FILES, instance=request.user.profile)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, 'Your profile is updated successfully')
return redirect(to='users-profile')
else:
user_form = UpdateUserForm(instance=request.user)
profile_form = UpdateProfileForm(instance=request.user.profile)
return render(request, 'registration/profile.html', {'user_form': user_form, 'profile_form': profile_form})
Вы должны быть знакомы с большинством того, что мы сделали в приведенном выше коде, но, повторим, в основном, что он делает, это импортирует необходимые формы и создает экземпляры этих форм в зависимости от того, является ли запрос получением или публикацией.
Если форма отправлена (запрос является POST), нам нужно передать данные поста в формы. Но для формы профиля вместе с запросом приходят данные файла/изображения. Данные этого файла помещаются в него, request.FILES поэтому нам нужно передать и его.
Затем он заполняет поля формы текущей информацией о вошедшем пользователе, т.е. форма пользователя ожидает экземпляр пользователя, поскольку она работает с моделью User, поэтому мы говорим instance=request.user, а для формы профиля мы передаем экземпляр модели профиля, говоря instance=request.user.profile.
Наконец, давайте обновим шаблон для отображения форм. Поэтому в profile.html изменим код:
{% extends "blog/base.html" %}
{% block title %}Profile Page{% endblock title %}
{% block content %}
<div>
<img src="{{ user.profile.avatar.url }} " style="cursor: pointer;"/>
</div>
{% if user_form.errors %}
<div role="alert">
<div>
{% for key, value in user_form.errors.items %}
<strong>{{ value }}</strong>
{% endfor %}
</div>
<button type="button" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endif %}
<div class="form-content">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div>
<div>
<div>
<label>Username:</label>
{{ user_form.username }}
<hr>
<label>Email:</label>
{{ user_form.email }}
</div>
<hr>
<div><a href="#">Change Password</a>
<hr>
<label>Change Avatar:</label>
{{ profile_form.avatar }}
</div>
<hr>
<label>Bio:</label> {{ profile_form.bio }}
</div>
</div>
<br><br>
<button type="submit">Save Changes</button>
<button type="reset">Reset</button>
</form>
</div>
{% endblock content %}
Создание профиля у существующего администратора или пользователей
На данный момент у нас может возникнуть ошибка из-за того что у нашего администратора нет профиля. Для этого давайте выполним следующий код в консоли:
$ python manage.py shell
from django.contrib.auth.models import User
from accounts.models import Profile
user = User.objects.get(username='<admin_user_name>')
profile = Profile(user=user)
profile.save()
Вместо <admin_user_name> напишите имя пользователя, которого вы создавали в разделе 5.3 данного курса с правами администратора. Для всех новых пользователей, профиль будет создаваться автоматически.
Изменение размера изображений в Django
Сохранение больших изображений только для отображения их масштабированной/уменьшенной версии на странице профиля может привести к замедлению работы нашего приложения. Мы можем смягчить эту проблему, используя pillow для изменения размера большого изображения и переопределения его с измененным/меньшим размером изображения.
Теперь, чтобы сохранить это изображение с измененным размером, нам нужно переопределить метод save(), который существует для всех моделей и используется для сохранения экземпляра модели.
Почему нам нужно переопределить этот метод? Это потому, что нам нужно настроить поведение сохранения. Откройте models.py и добавьте этот код в класс Profile:
from PIL import Image
# resizing images
def save(self, *args, **kwargs):
super().save()
img = Image.open(self.avatar.path)
if img.height > 100 or img.width > 100:
new_img = (100, 100)
img.thumbnail(new_img)
img.save(self.avatar.path)
Данный код открывает изображение и проверяет, имеет ли оно размер больше 100x100 пикселей. Если это так, изменит размер изображения и сохранит его по тому же пути, по которому он был изначально сохранен (переопределив исходное большое изображение).