Курс по Django: https://stepik.org/a/183363
Архив проекта: 64_sitewomen.zip
Продолжаем тему
авторизации и регистрации пользователей. Процесс авторизации мы с вами в целом уже
рассмотрели, пришло время познакомиться с регистрацией пользователей на сайте. У
нас с вами уже есть ссылка «Регистрация» на панели главного меню, давайте создадим
для нее маршрут, функцию представления и шаблон по аналогии с тем, как мы это
делали для задачи авторизации.
Вначале опишем
шаблон формы регистрации в файле users/register.html следующим
образом:
{% extends 'base.html' %}
{% block content %}
<h1>Регистрация</h1>
<form method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
{{ form.as_p }}
<p ><button type="submit">Регистрация</button></p>
</form>
{% endblock %}
Здесь совершенно
ничего нового для вас нет, мы это уже делали, когда говорили об авторизации. Далее,
объявим класс формы регистрации в файле users/forms.py:
class RegisterUserForm(forms.ModelForm):
username = forms.CharField(label="Логин")
password = forms.CharField(label="Пароль", widget=forms.PasswordInput)
password2 = forms.CharField(label="Повтор пароля", widget=forms.PasswordInput)
class Meta:
model = get_user_model()
fields = ['username', 'email', 'first_name', 'last_name', 'password', 'password2']
labels = {
'email': 'E-mail',
'first_name': 'Имя',
'last_name': 'Фамилия',
}
У вас может
возникнуть вопрос, откуда мы знаем, какие поля следует прописывать в форме
регистрации? Очень просто. Во-первых, есть документация:
https://docs.djangoproject.com/en/4.2/topics/auth/default/
где подробно
описываются стандартные поля модели User.
Итак, форма у
нас определена. Теперь можно объявить функцию представления в файле users/views.py следующим
образом:
def register(request):
form = RegisterUserForm()
return render(request, 'users/register.html', {'form': form})
И свяжем ее с
маршрутом (в файле users/urls.py):
urlpatterns = [
path('login/', views.LoginUser.as_view(), name='login'),
path('logout/', LogoutView.as_view(), name='logout'),
path('register/', views.register, name='register'),
]
Осталось
прописать этот маршрут для ссылки «Регистрация». Откроем файл base.html и внесем
следующие изменения:
<li class="last"><a href="{% url 'users:login' %}">Войти</a> | <a href="{% url 'users:register' %}">Регистрация</a></li>
Проверим, что
получилось. Запустим веб-сервер и перейдем по ссылке «Регистрация». Видим
отображение формы регистрации.
Проверка данных формы
Я сразу же в
класс формы внесу одно дополнение – метод clean_password2(), который
будет проверять равенство введенных паролей. Напомню, что все методы формы,
которые начинаются с префикса clean_ и продолжаются именем поля,
автоматически вызываются при проверке корректности переданных данных. Метод
реализуем следующим образом:
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError("Пароли не совпадают!")
return cd['password2']
И, сразу же,
пропишу еще одну проверку на уникальность введенного E-mail адреса. В
таблице user это поле не
помечено, как уникальное, поэтому разные пользователи могут вводить один и тот
же E-mail. Дополнительной
проверкой мы это исключим:
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError("Такой E-mail уже существует!")
return email
Все, теперь при
попытке ввести повторяющийся E-mail адрес в форме
будет отображаться ошибка.
Реализация процесса регистрации пользователя
Теперь
воспользуемся этой формой и реализуем функционал для регистрации новых
пользователей. Перейдем в файл users/views.py и перепишем
функцию register() следующим
образом:
def register(request):
if request.method == "POST":
form = RegisterUserForm(request.POST)
if form.is_valid():
user = form.save(commit=False) # создание объекта без сохранения в БД
user.set_password(form.cleaned_data['password'])
user.save()
return render(request, 'users/register_done.html')
else:
form = RegisterUserForm()
return render(request, 'users/register.html', {'form': form})
В целом, здесь
все похоже на то, как мы делали при авторизации. Добавилось только формирование
объекта пользователя с помощью метода save() формы.
Причем, с параметром commit=False, который
запрещает запись данных непосредственно в БД. Мы это делаем позже, после
установки пароля с помощью метода set_password(). Зачем
понадобилось использовать этот метод? Дело в том, что пароли в таблице БД
хранятся в зашифрованном виде. Там хранится только их хэш – закодированная
определенным образом последовательность символов. Раскодировать хэш невозможно:
алгоритмы хэширования работают в одну сторону – кодирования. Обратной операции
они не подразумевают. Благодаря этому злоумышленник, которому удается скачать
БД не сможет получить доступ к аккаунтам пользователей, так как у него не будет
паролей. Но тогда как сам фреймворк Django сможет сравнить
переданный ему от пользователя пароль, если в таблице хранится только его хэш? Нет
ничего проще! Django снова по тому
же алгоритму захэширует переданный пароль и будет сравнивать их хэши. Если хэши
совпадут, то делается вывод о корректности переданного пароля. Мало того,
результат работы алгоритма хэширования уникальный для каждого проекта из-за
секретного ключа SECRET_KEY, который прописан в файле settings.py. Этот ключ
используется для внесения дополнительных искажений в результирующий хэш. Поэтому
злоумышленнику, по хорошему, еще нужно знать и эту дополнительную информацию.
Конечно, при
большом желании взламываются многие системы. Есть приемы преодоления и этих
преград защиты. Но сделать это будет весьма непросто. Поэтому для большинства
рядовых сайтов, включая и интернет-магазины, такого механизма хранения паролей
вполне достаточно.
Итак,
возвращаясь к нашей функции представления, нам нужно определить еще один шаблон
с именем register_done.html:
{% extends 'base.html' %}
{% block content %}
<h1>Добро пожаловать!</h1>
<p >Вы успешно зарегистрировались на сайте! Для входа в систему необходимо авторизоваться по этой <a href="{% url 'users:login' %}">ссылке</a>
</p>
{% endblock %}
Все, регистрация
пользователя в самом простом варианте реализована на сайте.
Курс по Django: https://stepik.org/a/183363