我正在建立一个论坛网站,我有以下模型:
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
自定义用户字段是在注册过程中输入的,而个人资料字段应该在网站的/个人资料页面上进行选择性编辑。
两种模型的形式表示为
{{ 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 和位置已存储到数据库中。
我尝试编辑 profile.html 中已有的 Football_club 和位置字段,但它只是在数据库中创建另一个模型实例,而不是编辑。
另外,在 python shell 中我尝试了以下操作:
>>> 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 的
[Django-doc] 有一个DateTimeField
auto_now_add=…
参数[Django-doc] 使用时间戳。这将自动分配当前日期时间 创建对象时,将其标记为不可编辑(editable=False
),例如 默认情况下它不会出现在ModelForm
中。
注意:请不要在模型中存储聚合:在需要时确定聚合:在模型中存储聚合会使更新和保持数据同步变得更加困难。