Перевод статьи подготовлен в преддверии старта курса «Web-разработчик на Python».
Пфф… Снова базы данных?
В реляционной базе данных есть три основных отношения:
В этой статье мы будем разбираться с первым из них – отношением «один-к-одному».
Обычно в Django уже есть модель пользователя, которая поставляется с фреймворком. Она поставляется со своими полями, методами, атрибутами и т.д. Недостатком этой модели пользователя является то, что она не позволяет добавлять специальные поля отдельно от значений по умолчанию уже представленных в Django. Это может стать серьезной проблемой, поскольку разработчику может понадобиться полностью настроить профиль пользователя/клиента из группы аутентифицированных пользователей. Например, сайту блога может понадобиться профиль автора, который будет включать в себя фотографию пользователя, контактный адрес, хобби, нишу и т.д. А модель пользователя, поставляемая с Django, не позволяет так сделать.
Чтобы решить эту проблему, разработчики создают кастомную модель профиля и соединяют ее с моделью пользователя в Django по умолчанию с помощью отношения «один-к-одному». Так получается, что пользователь гарантированно подключен к одному профилю и наоборот. Кроме того, эта механика позволяет лучше управлять настройкой модели профиля.
Теперь я расскажу вам, как в Django можно сделать такую настройку.
1. Используйте модель пользователя в Django по умолчанию
В том приложении, где вы хотите создать профиль, создайте новый файл forms.py. В forms.py импортируйте следующие модули:
Создайте класс, который будет наследоваться от
Код выше создаст форму с полями для имени пользователя, пароля и подтверждения пароля.
2. Создайте свою кастомную модель профиля пользователя.
В файле models.py импортируйте модель пользователя по умолчанию.
Дальше нужно создать свою модель профиля, а также создать поле пользователя со связью «один-к-одному» с моделью пользователя по умолчанию в Django.
3. Создайте форму для своей модели профиля.
Откройте файл form.py и импортируйте свою модель профиля из models.py, также добавьте несколько других импортов, которые пригодятся при создании формы профиля.
Затем создайте класс, который будет наследоваться от
Теперь, когда формы готовы, мы определим логику views.py перед тем, как рендерить его в наши шаблоны.
4. Пропишите логику в views.py.
Для работы с формами, созданными в forms.py нужно импортировать их в наш views.py, и добавить еще несколько модулей, которые пригодятся для создания логики.
Теперь создадим страницу регистрации. Назовем ее просто registerPage. Создадим пустой контекстный словарь и вернем рендер.
Присвоим значения из формы переменным, вызвав метод POST. Затем передадим переменные в контекстный словарь.
Затем мы сделаем валидацию обеих форм и сохраним их после нее.
Если значения форм валидированы и сохранены, мы выведем сообщение об успешном выполнении операции и перенаправим пользователя на страницу входа в систему.
5. Рендер шаблона
В файле
Поскольку после заполнения формы, ее проверки и сохранения, мы перенаправляем на страницу входа в систему, сообщение об успешности операции будет отображаться на странице входа в систему снизу прямо перед надписью «Don't have an account? Register».
Вот так можно создать модель профиля для вашего сайта, связанную с вашей моделью пользователя отношением «один-к-одному».
Пфф… Снова базы данных?
В реляционной базе данных есть три основных отношения:
- Отношение «один-к-одному»;
- Отношение «один-ко-многим»;
- Отношение «многие-ко-многим».
В этой статье мы будем разбираться с первым из них – отношением «один-к-одному».
Обычно в Django уже есть модель пользователя, которая поставляется с фреймворком. Она поставляется со своими полями, методами, атрибутами и т.д. Недостатком этой модели пользователя является то, что она не позволяет добавлять специальные поля отдельно от значений по умолчанию уже представленных в Django. Это может стать серьезной проблемой, поскольку разработчику может понадобиться полностью настроить профиль пользователя/клиента из группы аутентифицированных пользователей. Например, сайту блога может понадобиться профиль автора, который будет включать в себя фотографию пользователя, контактный адрес, хобби, нишу и т.д. А модель пользователя, поставляемая с Django, не позволяет так сделать.
Чтобы решить эту проблему, разработчики создают кастомную модель профиля и соединяют ее с моделью пользователя в Django по умолчанию с помощью отношения «один-к-одному». Так получается, что пользователь гарантированно подключен к одному профилю и наоборот. Кроме того, эта механика позволяет лучше управлять настройкой модели профиля.
Теперь я расскажу вам, как в Django можно сделать такую настройку.
1. Используйте модель пользователя в Django по умолчанию
В том приложении, где вы хотите создать профиль, создайте новый файл forms.py. В forms.py импортируйте следующие модули:
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
Создайте класс, который будет наследоваться от
UserCreationForm
. Внутри этого класса создайте другой мета-класс, у которого будут две переменные: model
и fields
. В переменной model
будет храниться ваша модель пользователя, а в переменной fields
– поля формы, которые будут созданы.class createUserForm(UserCreationForm):
class meta:
model = User
fields = ['username', 'password1', 'password2']
Код выше создаст форму с полями для имени пользователя, пароля и подтверждения пароля.
2. Создайте свою кастомную модель профиля пользователя.
В файле models.py импортируйте модель пользователя по умолчанию.
from django.contrib.auth.models import User
Дальше нужно создать свою модель профиля, а также создать поле пользователя со связью «один-к-одному» с моделью пользователя по умолчанию в Django.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True,)
name = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200, null=True)
address = models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
3. Создайте форму для своей модели профиля.
Откройте файл form.py и импортируйте свою модель профиля из models.py, также добавьте несколько других импортов, которые пригодятся при создании формы профиля.
from django import forms
from django.utils.translation import ugettext_lazy as _
from .models import Profile
Затем создайте класс, который будет наследоваться от
forms.ModelForm
. В этом классе создайте другой мета-класс, в котором будут две переменные: model
и fields
. Переменная model
содержит модель профиля, а fields
– поля формы, которые будут созданы.class profileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['name', 'email', 'address']
#The labels attribute is optional. It is used to define the labels of the form fields created
labels = {
"name": _("Name "),
"email": _("Email Address"),
"address": _("Street Address"),
}
Теперь, когда формы готовы, мы определим логику views.py перед тем, как рендерить его в наши шаблоны.
4. Пропишите логику в views.py.
Для работы с формами, созданными в forms.py нужно импортировать их в наш views.py, и добавить еще несколько модулей, которые пригодятся для создания логики.
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login,
from django.contrib import messages
from .models import Profile
from .forms import createUserForm, profileForm
Теперь создадим страницу регистрации. Назовем ее просто registerPage. Создадим пустой контекстный словарь и вернем рендер.
def registerPage(request):
context = {}
return render(request, 'app_name/register.html', context)
Присвоим значения из формы переменным, вызвав метод POST. Затем передадим переменные в контекстный словарь.
def registerPage(request):
if request.method == 'POST':
form = createUserForm(request.POST)
profile_form = profileForm(request.POST)
context = {'form': form, 'profile_form': profile_form}
return render(request, 'app_name/register.html', context)
Затем мы сделаем валидацию обеих форм и сохраним их после нее.
def registerPage(request):
if request.method == 'POST':
form = createUserForm(request.POST)
profile_form = profileForm(request.POST)
if form.is_valid() and profile_form.is_valid():
user = form.save()
#we don't save the profile_form here because we have to first get the value of profile_form, assign the user to the OneToOneField created in models before we now save the profile_form.
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
context = {'form': form, 'profile_form': profile_form}
return render(request, 'app_name/register.html', context)
Если значения форм валидированы и сохранены, мы выведем сообщение об успешном выполнении операции и перенаправим пользователя на страницу входа в систему.
def registerPage(request):
if request.method == 'POST':
form = createUserForm(request.POST)
profile_form = profileForm(request.POST)
if form.is_valid() and profile_form.is_valid():
user = form.save()
#we don't save the profile_form here because we have to first get the value of profile_form, assign the user to the OneToOneField created in models before we now save the profile_form.
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
messages.success(request, 'Your account has been successfully created')
return redirect('login')
context = {'form': form, 'profile_form': profile_form}
return render(request, 'app_name/register.html', context)
5. Рендер шаблона
В файле
register.html
создадим тег формы с помощью метода POST и action
со значением пустой строки. В тег формы поместим csrf_token
в формате шаблона django, а затем будем динамически визуализировать формы (форму пользователя и профиля). Также не забудем про кнопку отправки.<form method="POST" action="">
{% csrf_token %}
<h3>Register Profile</h3>
<div class="form-field">
{{profile_form.name.label_tag}}
{{profile_form.name}}
</div>
<div class="form-field">
{{form.username.errors}}
{{form.username.label_tag}}
{{form.username}}
</div>
<div class="form-field">
{{profile_form.email.label_tag}}
{{profile_form.email}}
</div>
<div class="form-field">
{{profile_form.address.label_tag}}
{{profile_form.address}}
</div>
<div class="form-field">
{{form.password1.errors}}
{{form.password1.label_tag}}
{{form.password1}}
</div>
<div class="form-field">
{{form.password2.errors}}
{{form.password2.label_tag}}
{{form.password2}}
</div>
<hr>
<input id="form-button" class="btn btn-success btn-block" type="submit" value="Create Profile">
<br>
{{form.non_field_errors}}
<p>Already have an account? <a href="{% url 'login' %}">Login</a></p>
</form>
Поскольку после заполнения формы, ее проверки и сохранения, мы перенаправляем на страницу входа в систему, сообщение об успешности операции будет отображаться на странице входа в систему снизу прямо перед надписью «Don't have an account? Register».
<form method="POST" action="">
...
<hr>
<input id="form-button" class="btn btn-success btn-block" type="submit" value="Login">
{% for message in messages %}
<p>{{message}}</p>
{% endfor %}
<p>Don't have an account? <a href="{% url 'store:register' %}">Register</a></p>
</form>
Вот так можно создать модель профиля для вашего сайта, связанную с вашей моделью пользователя отношением «один-к-одному».
andreymal
Позволяет, настройка AUTH_USER_MODEL и класс AbstractUser как раз для этого. Более того, документация Django крайне рекомендует прописать свой AUTH_USER_MODEL с самого старта проекта, чтобы в будущем потом не страдать. Для большинства стандартных задач этого вполне достаточно, и городить отдельную модель профиля обычно ни к чему (в моей личной практике это так ни разу и не понадобилось)
anonymous
Делаю также шесть лет и ни разу не пожалел. Плюсы от подмены через AUTH_USER_MODEL:
Только лучше переопределять не AbstractUser, а унаследовать AbstractBaseUser и PermissionMixin. Более гибкое и настраиваемое решение получится.
AstraVlad
Есть вариант, когда у пользователя два «лица» и они могут существовать отдельно. Я сейчас делаю сайт с БД федерации фехтовальных клубов и там есть профиль пользователя сайта и профиль собственно фехтовальщика (бойца) и возможны все три варианта:
1. Пользователь сайта не фехтует и не имеет профиля как боец (административный персонал, учетки заводятся через админку).
2. У бойца нет учетной записи пользователя, но профиль бойца заполнен руководителем его клуба.
3. Пользователь регистрировался обычным образом, при этом заполнение профиля бойца обязательно (чтобы не регистрировались посторонние люди, не состоящие в федерации).
В этом случае подход с двумя отдельными профилями вполне оправдан.