在 Django 1.7 中扩展用户配置文件

Extending User profile in Django 1.7

我知道,这个问题已经在 SO 中被问过很多次了,但是我读到的大部分答案要么已经过时(建议现在弃用的 AUTH__PROFILE_MODULE 方法),要么缺少具体示例。

因此,我阅读了 Django 文档 [1,2],但我缺少有关如何正确使用它的真实示例。

事实上,当通过表单创建(或更新)新用户时,我的问题就来了。用户显然已创建,但扩展中的字段均未设置。我知道 Django 文档说明:

These profile models are not special in any way - they are just Django models that happen to have a one-to-one link with a User model. As such, they do not get auto created when a user is created, but a django.db.models.signals.post_save could be used to create or update related models as appropriate.

但是,我不知道如何在实践中做到这一点(我应该添加一个 receiver 如果'',哪个)。

现在,我有以下内容(为简洁起见,摘自文档):

文件models.py

from django.contrib.auth.models import User

class Employee(models.Model):
  user = models.OneToOneField(User)
  department = models.CharField(max_length=100)

文件admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

from my_user_profile_app.models import Employee

# Define an inline admin descriptor for Employee model
class EmployeeInline(admin.StackedInline):
  model = Employee
  can_delete = False
  verbose_name_plural = 'employee'

# Define a new User admin
class UserAdmin(UserAdmin):
  inlines = (EmployeeInline, )

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

文件forms.py

class SignupForm(account.forms.SignupForm):
  department = forms.CharField(label="Department", max_length=100)

class SettingsForm(account.forms.SignupForm):
  department = forms.CharField(label="Department", max_length=100)

然后,在我的代码中,我这样使用它:

u = User.objects.get(username='fsmith')
freds_department = u.employee.department

但是,注册和设置表单未按预期运行,并且未记录 departement 的新值。

欢迎任何提示!

我更喜欢扩展用户模型。例如:

class UserProfile(User):
    def __unicode__(self):
         return self.last_name + self.first_name

    department = models.CharField(max_length=100)


class SignupForm(forms.Form):
    username = forms.CharField(max_length=30)
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)
    department = forms.CharField(label="Department", max_length=100)

保存数据

form = UserRegistrationForm(request.POST)
    if form.is_valid():

    client = UserProfile()
    client.username = username
    client.set_password(password)
    client.first_name = first_name
    client.department = department
    client.save()

验证表单后检查您如何保存数据

我为这个话题苦苦挣扎了大约一年,直到我终于找到了一个令我满意的解决方案,而且我很清楚你所说的 "there is a lot out there, but it doesn't work" 是什么意思。我尝试过以不同的方式扩展 User 模型,我尝试过 UserProfile 方法,以及其他一些一次性解决方案。

我终于想出了如何简单地扩展 AbstractUser class 来创建我的自定义用户模型,这对我的许多项目来说都是一个很好的解决方案。

所以,让我澄清一下你上面的评论之一,你真的 不应该 在两个模型之间创建一个 link,普遍接受的 "best" 解决方案是根据您的需要使用一种从 AbstractUser 或 AbstractBaseUser 继承的模型。

让我感到棘手的事情是 "Extending the User Model" 没有让我到达我想要的位置,我需要 Substitute the User Model,我相信你已经 seen/read 多次了, 但可能没有吸收它(至少我知道我没有)。

一旦你掌握了它的窍门,真的没有那么多代码,也不太复杂。

# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    '''
    Here is your User class which is fully customizable and
    based off of the AbstractUser from auth.models
    '''
    my_custom_field = models.CharField(max_length=20)

    def my_custom_model_method(self):
        # do stuff
        return True

在此之后有几件事需要注意,其中一些出现在 django 1.7 中。

首先,如果你想让管理页面看起来像以前那样,你必须使用 UserAdmin

# admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin

# Register your models here.
admin.site.register(get_user_model(), UserAdmin)

另一件事是,如果您想在模型文件中导入用户 class,则必须从设置中导入它,而不是使用 get_user_model()。如果你 运行 喜欢这个,它很容易修复,所以我只是想提醒你一下。

您可以查看我的 seed project 我用来启动项目以获得使用自定义用户模型的完整但简单的项目。用户内容在主应用程序中。

从那里开始,所有注册和登录的工作方式与普通 Django 用户的工作方式相同,因此我不会详细讨论该主题。我希望这对你有帮助,就像对我有帮助一样!

我尽量避免扩展用户模型,如 django 文档中所述。

我用这个:

class UserExtension(models.Model):
    user=models.OneToOneField(User, primary_key=True)
    ... your extra model fields come here

OneToOneField 文档:https://docs.djangoproject.com/en/1.7/topics/db/examples/one_to_one/

我看到了这些好处:

  • 相同的模式适用于其他模型(例如组)
  • 如果你有 N 个应用,每个应用都可以自己扩展模型。

无需提供参数即可创建 UserExtension。所有字段都必须具有合理的默认值。

然后您可以创建一个信号处理程序,如果创建了用户,它会创建 UserExtension 个实例。

我已经查看了所有答案,但 none 确实能解决我的问题(尽管你们中的一些人给了我很好的提示让我找到正确的方向)。我将在这里总结我找到的解决问题的方法。

首先,我必须承认我没有把我的问题全部说出来。我想在 User 模型中插入额外的字段并使用其他应用程序,例如 Django 的默认身份验证方案。因此,通过继承和设置 AUTH_USER_MODEL 扩展默认 User 是一个问题,因为其他 Django 应用程序停止正常工作(我相信他们没有使用 user = models.OneToOneField(settings.AUTH_USER_MODEL)user = models.OneToOneField(User)).

因为,正确重写我正在使用的其他应用程序太长了,我决定通过一对一字段添加这个额外的字段。但是,文档遗漏了我想在下面填写的几点。

因此,这是一个向 User 模型添加额外字段以及使用相同模型的其他应用程序的完整示例。

首先,编写模型描述,收集要添加到 models.py 文件的额外字段:

from django.contrib.auth.models import User

class UserProfile(models.Model):
  user = models.OneToOneField(User)

  extra_field = models.CharField(max_length=100)

然后,我们需要在每次创建User时触发添加一个对象UserProfile。这是通过将此代码附加到 receiver.py 文件中的正确信号来完成的:

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

from my_user_profile_app.models import UserProfile

@receiver(post_save, sender=User)
def handle_user_save(sender, instance, created, **kwargs):
  if created:
    UserProfile.objects.create(user=instance)

现在,如果您希望能够通过管理界面对其进行修改,只需将其与通常的 UserAdmin 形式堆叠在 admin.py 文件中即可。

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

from my_user_profile_app.models import UserProfile

# Define an inline admin descriptor for UserProfile model
class UserProfileInline(admin.StackedInline):
  model = UserProfile
  can_delete = False

# Define a new User admin
class UserAdmin(UserAdmin):
  inlines = (UserProfileInline, )

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

那么,现在是时候尝试将这个额外的字段与默认的 Django 身份验证应用程序混合使用了。为此,我们需要在forms.py文件中通过继承添加一个额外的字段来填写SignupFormSettingsForm

import account.forms
from django import forms

class SignupForm(account.forms.SignupForm):
  extra_field = forms.CharField(label="Extra Field", max_length=100)

class SettingsForm(account.forms.SignupForm):
  extra_field = forms.CharField(label="Extra Field", max_length=100)

而且,我们还需要添加一些代码来正确显示和获取您已添加到原始 User 模型中的数据。这是通过继承 SignupViewviews.py 文件中的 SettingsView 视图来完成的:

import account.views

from my_user_profile_app.forms import Settings, SignupForm
from my_user_profile_app.models import UserProfile


class SettingsView(account.views.SettingsView):
  form_class = SettingsForm

  def get_initial(self):
    initial = super(SettingsView, self).get_initial()

    initial["extra_field"] = self.request.user.extra_field

    return initial

  def update_settings(self, form):
    super(SettingsView, self).update_settings(form)

    profile = self.request.user.userprofile
    profile.extra_field = form_cleaned_data['extra_field']
    profile.save()


class SignupView(account.views.SignupView):
  form_class = SignupForm

  def after_signup(self, form):
    profile = self.created_user.userprofile
    profile.extra_field = form_cleaned_data['extra_field']
    profile.save()

    super(SignupView, self).after_signup(form)

一旦一切就绪,它应该可以很好地工作(希望如此)。