Невозможно соединить две модели Django
Я создаю сайт форума и у меня есть следующие модели:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile', null=True, blank=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
football_club = models.CharField(max_length=100)
location = models.CharField(max_length=100, blank=True)
class CustomUser(AbstractBaseUser, PermissionsMixin):
userprofile = models.OneToOneField(Profile, on_delete=models.CASCADE, related_name='user_profile', null=True, blank=True)
username = models.CharField(max_length=150, unique=True)
email = models.EmailField(unique=True, null=False)
created_at = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
post_count = models.PositiveIntegerField(default=0)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.username
Поля CustomUser вводятся при регистрации, в то время как поля профиля должны опционально редактироваться на странице /profile сайта.
Формы для обеих моделей представлены в виде
{{ form.as_p }}
внутри соответствующих html-файлов.
Обе формы настроены в файле forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser, Profile
class SignUpForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ['username', 'email']
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['avatar', 'football_club', 'location']
И у меня есть следующая функция/класс в моем файле views.py:
class SignUpView(CreateView):
form_class = SignUpForm
template_name = 'accounts/signup.html'
success_url = reverse_lazy('login'))
@login_required
def profile(request):
profile = request.user.userprofile
print("Profile: ", profile)
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
return redirect('profile')
else:
form = ProfileForm(instance=profile)
return render(request, 'accounts/profile.html', {'form': form})
Но на самом деле эти две модели не связаны друг с другом. Я попробовал сделать следующее в одном из своих html-шаблонов:
<ul>
<li><h3>{{ post.author.username }}</h3></li>
<li>Posts: {{ post.author.post_count }}</li>
<li>Club: {{ post.author.userprofile.football_club }}</li>
</ul>
И у меня правильно отображаются имя пользователя и количество сообщений, но поле "Клуб" остается пустым, хотя футбольный_клуб и местоположение сохраняются в базе данных.
Я попытался отредактировать уже существующие поля football_club и location внутри profile.html, но вместо редактирования он просто создает еще один экземпляр модели в базе данных.
Кроме того, в оболочке python я пробовал следующее:
>>> from accounts.models import Profile, CustomUser
>>> lastprofile = Profile.objects.last()
>>> print(lastprofile.football_club)
Inter
>>> print(lastprofile.location)
Milan
>>> print(lastprofile.user)
None
Странно иметь одновременно CustomUser
и Profile
. Обычно, если вы хотите настроить пользовательское моделирование, вы либо сами создаете пользовательскую модель со всеми (необязательными и обязательными) данными, либо, если вы не хотите подключать пользовательскую User
модель, вы работаете с Profile
для хранения дополнительных (часто необязательных) данных, но обычно вы не делаете ни того, ни другого: Это часто является худшим из двух миров, поскольку тогда вам придется реализовать пользовательскую логику для создания и регистрации пользователей, но также придется просматривать ForeignKey
или OneToOneField
для других данных. Еще хуже то, что эти две модели, похоже, имеют каждую связь друг с другом OneToOneField
. Обычно пишут one OneToOneField
, и таким образом используют обратную связь.
Я думаю, что в данном конкретном случае лучше придерживаться одной модели, и поэтому перенести поля из Profile
в CustomUser
, и сделать их blank=True
[Django-doc], это сделает поля необязательными:
class CustomUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=150, unique=True)
email = models.EmailField(unique=True, null=False)
created_at = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
post_count = models.PositiveIntegerField(default=0)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
football_club = models.CharField(max_length=100, null=True, blank=True)
location = models.CharField(max_length=100, blank=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.username
Тогда форма упрощается до:
from django.contrib.auth.forms import UserCreationForm
from django import forms
class SignUpForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ['username', 'email', 'avatar', 'football_club', 'location']
а Profile
можно просто сделать подмножество полей:
class ProfileForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ['avatar', 'football_club', 'location']
@login_required
def profile(request):
profile = request.user
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
return redirect('profile')
else:
form = ProfileForm(instance=profile)
return render(request, 'accounts/profile.html', {'form': form})
Таким образом, нам не нужно определять значения для OneToOneField
, и уж точно не нужно связывать их двунаправленно. Производительность, как правило, также будет выше, так как не требуется дополнительных запросов для получения .location
, .avatar
и т. д. данных пользователя.
Примечание: В Django
DateTimeField
[Django-doc] есть параметрauto_now_add=…
[Django-doc] для работы с временными метками. Он автоматически присваивает текущее время при создании объекта, и пометит его как нередактируемый (editable=False
), так что что оно не будет отображаться вModelForm
по умолчанию.
Примечание: Пожалуйста, не храните агрегаты в модели: определяйте агрегаты по мере необходимости: хранение агрегатов в модели усложняет обновление и синхронизацию данных.