衆所周知, Django自帶的Auth backend username最大隻支援30個字元, 對應的Django admin也是這麼校驗的。 最近項目換了Oauth, 想把Oauth id直接存到username上友善登陸和管理。問題來了。。 auth子產品是随Django一起安裝的, 我想沒人會有想法去改site-packages吧? 考慮到項目的部署友善, 是以隻能在項目代碼中想辦法把這個可惡的限制去掉。
嘗試1: 找Django提供的接口:
很遺憾,這個長度是Django直接hard code的:
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, numbers and '
'@/./+/-/_ characters'),
validators=[
validators.RegexValidator(re.compile('^[\[email protected]+-]+$'), _('Enter a valid username.'), 'invalid')
])
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
User 是繼承于AbstractUser, 而username max length已在此寫死, 沒有提供配置的接口。
嘗試2: 繼承重寫User model:
其實就是寫一個自己的User Model, 然後在各處import這個自定義的Model. 不過這個似乎有些不妙,問題有如下4:
1. 棄用Django auth會導緻資料庫多幾張廢表,而且Django的權限機制包括Group, Permission什麼的都是和預設的User Model關聯的,一一修改十分麻煩。
2. 将來引入的第三方庫有auth子產品依賴怎麼辦?第三方庫可不知道我們重寫了User Model.
3. 老代碼一大堆, 挨個改也是個煩人的dirty work.
4. 實際上重寫了User model還沒完, Admin儲存的時候照樣給你抛Validate Error. 因為牛逼的Django Admin Form也是hard code的。。。
嘗試3: Black Magic —— MonkeyPatch:
關于這個黑魔法的原理就不贅述了. 動态語言的特性, 實作如下:
1. 實作一個patch 函數如下:
# -*- coding: utf-8 -*-
from django.contrib.auth import models as auth_models
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
def patch_auth_username_field():
NEW_USERNAME_LENGTH = 40
HELP_TEXT = _('Required. %d characters or fewer.'
' Letters, numbers and @/./+/-/_ characters'
) % NEW_USERNAME_LENGTH
model_field = auth_models.User._meta.get_field("username")
model_field.max_length = NEW_USERNAME_LENGTH
model_field.validators[1].limit_value = NEW_USERNAME_LENGTH
model_field.help_text = HELP_TEXT
for form in (UserChangeForm, UserCreationForm):
form_field = form.base_fields['username']
form_field.max_length = NEW_USERNAME_LENGTH
form_field.widget.attrs.update({
'maxlength': NEW_USERNAME_LENGTH,
'style': "width: 410px;"
})
form_field.validators[0].limit_value = NEW_USERNAME_LENGTH
form_field.help_text = HELP_TEXT
此處分别patch了 User Model 和相關的兩個Admin Form. 修改了max_length屬性, validators, 和提示内容。我此處是講username長度patch為40個字元, 你可以根據你的需要修改NEW_USERNAME_LENGTH變量來完成你的patch. Google上大部分答案都忽略了Form和validators. 當使用Django Admin時, 這兩處是會抛出validation error的。
2. 建立一個app, 在該APP的__init__.py中執行該函數。 這樣是保證在初始化時打上patch, 又不會導緻settings.py的循環導入:
# coding=utf-8
from patches import (
patch_auth_username_field,
)
patch_auth_username_field()
3. 最後, 在settings.py裡install 該APP打上patch:
INSTALLED_APPS = (
'UI.monkeypatch',
...
)
注意, patch必須放在INSTALLED_APPS的最上面。 否則可能導緻部分app不生效。
大功告成。
PS: Patch之後由于資料最大長度發生變化, 需要migrate一下表結構。