1. 程式人生 > >自定義 Django的User Model,擴充套件 AbstractUser類注意事項

自定義 Django的User Model,擴充套件 AbstractUser類注意事項

本篇主要討論一下User Model的使用技巧. 注意, 由於Django 1.5之後user model帶來了很大的變化, 本篇內容只針對django 1.5之後的版本.

1. 確定 User Model

我們推薦一下方式來確定某一django專案使用的user model:

    # 使用預設User model時
    >>> from django.contrib.auth import get_user_model
    >>> get_user_model()
    <class
'django.contrib.auth.models.User'> # 使用自定義User model時 >>> from django.contrib.auth import get_user_model >>> get_user_model() <class 'xxx.models.UserProfile'>

2. 使用settings.AUTH_USER_MODEL

自從django 1.5之後, 使用者可以自定義User model了, 如果需要外來鍵使用user model, 官方推薦的方法如下:

在settings中設定AUTH_USER_MODEL:

    # settings.py
    # 格式為 "<django_app名>.<model名>"
    AUTH_USER_MODEL = "myapp.NewUser"

在models.py中使用

    # models.py
    from django.conf import settings
    from django.db import models

    class Article(models.Model):
        author 
= models.ForeignKey(settings.AUTH_USER_MODEL) title = models.CharField(max_length=255)

還有需要注意的是, 不要在外來鍵中使用get_user_model().

3. 自定義 User Model

方法1: 擴充套件 AbstractUser類

如果你對django自帶的User model剛到滿意, 又希望額外的field的話, 你可以擴充套件AbstractUser類:

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

    class NewUser(AbstractUser):
        new_field = models.CharField(max_length=100)

不要忘了在settings.py中設定:

 AUTH_USER_MODEL = "myapp.NewUser"

方法2: 擴充套件 AbstractBaseUser類

AbstractBaseUser中只含有3個field: password, last_login和is_active. 如果你對django user model預設的first_name, last_name不滿意, 或者只想保留預設的密碼儲存方式, 則可以選擇這一方式.

方法3: 使用OneToOneField

如果你想建立一個第三方模組釋出在PyPi上, 這一模組需要根據使用者儲存每個使用者的額外資訊. 或者我們的django專案中希望不同的使用者擁有不同的field, 有些使用者則需要不同field的組合, 且我們使用了方法1或方法2:

    # profiles/models.py
    from django.conf import settings
    from django.db import models

    from flavors.models import Flavor

    class EasterProfile(models.Model):
        user = models.OneToOneField(settings.AUTH_USER_MODEL)
        favorite_ice_cream = models.ForeignKey(Flavor, null=True, blank=True)
        
        
    class ScooperProfile(models.Model):
        user = models.OneToOneField(settings.AUTH_USER_MODEL)
        scoops_scooped = models.IntergerField(default=0)
        
        
    class InventorProfile(models.Model):
        user = models.OneToOneField(settings.AUTH_USER_MODEL)
        flavors_invented = models.ManyToManyField(Flavor, null=True, blank=True)

使用以上方法, 我們可以使用user.easterprofile.favorite_ice_cream獲取相應的profile.

使用這一方法的壞處可能就是增加了程式碼的複雜性.

 

原文連結: http://www.weiguda.com/blog/28/

 

另外一篇參考文章  Django 重寫使用者模型

 

django——重寫使用者模型

Django內建的User模型可能不適合某些型別的專案。例如,在某些網站上使用郵件地址而不是使用者名稱作為身份的標識可能更合理。

1.修改配置檔案,覆蓋預設的User模型

Django允許你通過修改setting.py檔案中的 AUTH_USER_MODEL 設定覆蓋預設的User模型,其值引用一個自定義的模型。

?
1 AUTH_USER_MODEL = 'myapp.MyUser'

上面的值表示Django應用的名稱(必須位於INSTALLLED_APPS中)和你想使用的User模型的名稱。

注意:
1.在建立任何遷移或者第一次執行 manager.py migrate 前設定 AUTH_USER_MODEL
設定AUTH_USER_MODEL對你的資料庫結構有很大的影響。它改變了一些會使用到的表格,並且會影響到一些外來鍵和多對多關係的構造。在你有表格被建立後更改此設定是不被 makemigrations 支援的,並且會導致你需要手動修改資料庫結構,從舊使用者表中匯出資料,可能重新應用一些遷移。

警告 :
1.確保 AUTH_USER_MODEL 引用的模型在所屬app中第一個遷移檔案中被建立
由於Django的可交換模型的動態依賴特性的侷限,你必須確保 AUTH_USER_MODEL 引用的模型在所屬app中第一個遷移檔案中被建立(通常命名為 0001_initial),否則你會碰到錯誤。

The easiest way to construct a compliant custom User model is to inherit fromAbstractBaseUser. AbstractBaseUser provides the core implementation of a Usermodel, including hashed passwords and tokenized password resets. You must then provide some key implementation details:

2.引用User模型

AUTH_USER_MODEL 設定為自定義使用者模型時,如果你直接引用User(例如:通過一個外來鍵引用它),你的程式碼將不能工作。你應該使用django.contrib.auth.get_user_model()來引用使用者模型————指定的自定義使用者模型或者User

?
1 2 3 from django.contrib.auth import get_user_model   User = get_user_model()

當你定義一個外來鍵或者到使用者模型的多對多關係是,你應該使用AUTH_USER_MODEL設定來指定自定義的模型。

?
1 2 3 4 5 from django.conf import settings from django.db import models   class Article(models.Model):      author = models.ForeignKey(settings.AUTH_USER_MODEL)

一般來說,在匯入時候執行的程式碼中,你應該使用 AUTH_USER_MODEL 設定引用使用者模型。get_user_model() 只在Django已經匯入所有的模型後才工作。

3.指定自定義的使用者模型

3.1 Django 期望你自定義的 User model 滿足一些最低要求:

  1. 模型必須有一個唯一的欄位可被用於識別目的。可以是一個使用者名稱,電子郵件地址,或任何其它獨特屬性。
  2. 定製一個User Model最簡單的方式是構造一個相容的使用者模型繼承於AbstractBaseUser
    AbstractBaseUser提供了User類最核心的實現,包括雜湊的passwords和 標識的密碼重置。

3.2 下面為一些AbstractBaseUser的子類必須定義的關鍵的欄位和方法:

USERNAME_FIELD
必須設定。 設定認證標識,設定成標識的欄位 unique=True

?
1 2 3 4 class MyUser(AbstractBaseUser):      identifier = models.CharField(max_length = 40 , unique = True )         ...      USERNAME_FIELD = 'identifier'

REQUIRED_FIELDS
必須設定。當通過createsuperuser管理命令建立一個使用者時,用於提示的一個欄位名稱列表。

?
1 2 3 4 5 6 class MyUser(AbstractBaseUser):      ...      date_of_birth = models.DateField()      height = models.FloatField()      ...      REQUIRED_FIELDS = [ 'date_of_birth' , 'height' ]

列表中不應該包含USERNAME_FIELD欄位和password欄位。

is_active
必須定義。 一個布林屬性,標識使用者是否是 "active" 的。AbstractBaseUser預設為 Ture

get_full_name()
必須定義。 long格式的使用者標識。

get_short_name()
必須定義。 short格式的使用者標識。

3.3 下面為一些AbstractBaseUser的子類可以使用的方法:

get_username()
返回 USERNAME_FIELD 的值。

is_anonymous()
一直返回 False。用來區分 AnonymousUser。

is_authenticated()
一直返回 Ture。用來告訴使用者已被認證。

set_password(raw_password)
設定密碼。按照給定的原始字串設定使用者的密碼,taking care of the password hashing。 不儲存 AbstractBaseUser 物件。如果沒有給定密碼,密碼就會被設定成不使用,同用set_unusable_password()。

check_password(raw_password)
檢查密碼是否正確。 給定的密碼正確返回 True。

set_unusable_password()
設定user無密碼。 不同於密碼為空,如果使用 check_password(),則不會返回True。不儲存AbstractBaseUser 物件。

has_usable_password()
如果設定了set_unusable_password(),返回False。

get_session_auth_hash()
返回密碼欄位的HMAC。 Used for Session invalidation on password change.

3.4 為你的User模型自定義一個管理器

如果你的User模型定義了這些欄位:username, email, is_staff, is_active, is_superuser, last_login, and date_joined跟預設的User沒什麼區別, 那麼你還不如僅僅替換Django的UserManager就行了; 總之,如果你的User定義了不同的欄位, 你就要去自定義一個管理器,它繼承自BaseUserManager並提供兩個額外的方法:

create_user(username_field, password=None, other_fields)**
接受username field和required欄位來建立使用者。例如,如果使用email作為username field, date_of_birth作為required field:

?
1 2 3 def create_user( self , email, date_of_birth, password = None ):      # create user here      ...

create_superuser(username_field, password, other_fields)**
接受username field和required欄位來建立superuser。例如,如果使用email作為username field, date_of_birth作為required field:

?
1 2 3 def create_superuser( self , email, date_of_birth, password):      # create superuser here      ...

create_superuser中的password是必需的

4.擴充套件Django預設的User

如果你完全滿意Django的使用者模型和你只是想新增一些額外的屬性資訊,你只需繼承 django.contrib.auth.models.AbstractUser 然後新增自定義的屬性。AbstractUser 作為一個抽象模型提供了預設的User的所有的實現(AbstractUser provides the full implementation of the default User as an abstract model.)。

5.自定義使用者與內建身份驗證表單

Django內建的forms和views和相關聯的user model有一些先決條件。如果你的user model沒有遵循同樣的條件,則需要定義一個替代的form,通過form成為身份驗證views配置的一部分。

UserCreationForm
依賴於User Model. 擴充套件User時必須重寫。

UserChangeForm
依賴於User Model. 擴充套件User時必須重寫。

AuthenticationForm
Works with any subclass of AbstractBaseUser, and will adapt to use the field defined in USERNAME_FIELD.

PasswordResetForm
Assumes that the user model has a field named email that can be used to identify the user and a boolean field named is_active to prevent password resets for inactive users.

SetPasswordForm
Works with 任何AbstractBaseUser子類

PasswordChangeForm
Works with 任何AbstractBaseUser子類

AdminPasswordChangeForm
Works with 任何AbstractBaseUser子類

6.自定義使用者和django.contrib.admin

如果你想讓你自定義的User模型也可以在站點管理上工作,那麼你的模型應該再定義一些額外的屬性和方法。 這些方法允許管理員去控制User到管理內容的訪問:

is_staff
是否允許user訪問admin介面

is_active
使用者是否活躍。

has_perm(perm, obj=None):
user是否擁有perm許可權。

has_module_perms(app_label):
user是否擁有app中訪問models的許可權

你同樣也需要註冊你自定義的使用者模型到admin。如果你的自定義使用者模型擴充套件於django.contrib.auth.models.AbscustomauthtractUser,你可以用django的 django.contrib.auth.admin.UserAdmin 類。如果你的使用者模型擴充套件於 AbstractBaseUser,你需要自定義一個ModelAdmin類。他可能繼承於預設的django.contrib.auth.admin.UserAdmin。然而,你也需要覆寫一些django.contrib.auth.models.AbstractUser 欄位的定義不在你自定義使用者模型中的。

7.自定義使用者和許可權

如果想讓在自定義使用者模型中包含Django的許可權控制框架變得簡單,Django提供了PermissionsMixin。這是一個抽象的類,你可以為你的自定義使用者模型中的類的層次結構中包含它。它提供給你所有Django許可權類所必須的的方法和欄位

7.1 如果要定製User的許可權系統,最簡單的方法是繼承PermissionsMixin

原始碼:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 class PermissionsMixin(models.Model):      """      A mixin class that adds the fields and methods necessary to support      Django's Group and Permission model using the ModelBackend.      """      is_superuser = models.BooleanField(_( 'superuser status' ), default = False ,          help_text = _( 'Designates that this user has all permissions without '                      'explicitly assigning them.' ))      groups = models.ManyToManyField(Group, verbose_name = _( 'groups' ),          blank = True , help_text = _( 'The groups this user belongs to. A user will '                                  'get all permissions granted to each of '                                  'their groups.' ),          related_name = "user_set" , related_query_name = "user" )      user_permissions = models.ManyToManyField(Permission,          verbose_name = _( 'user permissions' ), blank = True ,          help_text = _( 'Specific permissions for this user.' ),          related_name = "user_set" , related_query_name = "user" )        class Meta:          abstract = True        def get_group_permissions( self , obj = None ):          """          Returns a list of permission strings that this user has through their          groups. This method queries all available auth backends. If an object          is passed in, only permissions matching this object are returned.          """          permissions = set ()          for backend in auth.get_backends():              if hasattr (backend, "get_group_permissions" ):                  permissions.update(backend.get_group_permissions( self , obj))          return permissions        def get_all_permissions( self , obj = None ):          return _user_get_all_permissions( self , obj)        def has_perm( self , perm, obj = None ):          """          Returns True if the user has the specified permission. This method          queries all available auth backends, but returns immediately if any          backend returns True. Thus, a user who has permission from a single          auth backend is assumed to have permission in general. If an object is          provided, permissions for this specific object are checked.          """            # Active superusers have all permissions.          if self .is_active and self .is_superuser:              return True            # Otherwise we need to check the backends.          return _user_has_perm( self , perm, obj)        def has_perms( self , perm_list, obj = None ):          """          Returns True if the user has each of the specified permissions. If          object is passed, it checks if the user has all required perms for this          object.          """          for perm in perm_list:              if not self .has_perm(perm, obj):                  return False          return True        def has_module_perms( self , app_label):          """          Returns True if the user has any permissions in the given app label.          Uses pretty much the same logic as has_perm, above.          """          # Active superusers have all permissions.          if self .is_active and self .is_superuser:              return True            return _user_has_module_perms( self , app_label)
4.3.2 Django內建的User物件就繼承了AbstractBaseUser和PermissionsMixin:

原始碼:

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.
    Username, password and email are required. Other fields are optional.
    """
    username = models.CharField(_('username'), max_length=30, unique=True,
        help_text=_('Required. 30 characters or fewer. Letters, digits and '
                    '@/./+/-/_ only.'),
        validators=[
            validators.RegexValidator(r'^[\[email protected]+-]+$',
                                      _('Enter a valid username. '
                                        'This value may contain only letters, numbers '
                                        'and @/./+/-/_ characters.'), 'invalid'),
        ],
        error_messages={
            'unique': _("A user with that username already exists."),
        })
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(_('staff status'), default=False,
        help_text=_('Designates whether the user can log into this admin '
                    'site.'))
    is_active = models.BooleanField(_('active'), default=True,
        help_text=_('Designates whether this user should be treated as '
                    'active. Unselect this instead of deleting accounts.'))
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def get_full_name(self):
        """
        Returns the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        "Returns the short name for the user."
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)


class User(AbstractUser):
    """
    Users within the Django authentication system are represented by this
    model.
    Username, password and email are required. Other fields are optional.
    """
    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'
4.3.3 PermissionsMixin提供的這些方法和屬性:

is_superuser
布林型別。 Designates that this user has all permissions without explicitly assigning them.

get_group_permissions(obj=None)
Returns a set of permission strings that the user has, through their groups.

If obj is passed in, only returns the group permissions for this specific object.

get_all_permissions(obj=None)
Returns a set of permission strings that the user has, both through group and user permissions.

If obj is passed in, only returns the permissions for this specific object.

has_perm(perm, obj=None)
Returns True if the user has the specified permission, where perm is in the format "<app label>.<permission codename>" (see permissions). If the user is inactive, this method will always return False.

If obj is passed in, this method won’t check for a permission for the model, but for this specific object.

has_perms(perm_list, obj=None)
Returns True if the user has each of the specified permissions, where each perm is in the format "<app label>.<permission codename>". If the user is inactive, this method will alway