首先,老样子对于django的AbstractUser的基础上进行扩展,扩展出了两张表。一张是AUser表,一张是BUser表。
但是这个时候发现在创建的时候,看到了满屏的错误,如下:
NO1002.AUser.groups: (fields.E304) Reverse accessor for 'AUser.groups' clashes with reverse accessor for 'BUser.groups'.
HINT: Add or change a related_name argument to the definition for 'AUser.groups' or 'BUser.groups'.
NO1002.AUser.groups: (fields.E304) Reverse accessor for 'AUser.groups' clashes with reverse accessor for 'User.groups'.
HINT: Add or change a related_name argument to the definition for 'AUser.groups' or 'User.groups'.
NO1002.AUser.user_permissions: (fields.E304) Reverse accessor for 'AUser.user_permissions' clashes with reverse accessor for 'BUser.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'AUser.user_permissions' or 'BUser.user_permissions'.
NO1002.AUser.user_permissions: (fields.E304) Reverse accessor for 'AUser.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'AUser.user_permissions' or 'User.user_permissions'.
NO1002.BUser.groups: (fields.E304) Reverse accessor for 'BUser.groups' clashes with reverse accessor for 'AUser.groups'.
HINT: Add or change a related_name argument to the definition for 'BUser.groups' or 'AUser.groups'.
NO1002.BUser.groups: (fields.E304) Reverse accessor for 'BUser.groups' clashes with reverse accessor for 'User.groups'.
HINT: Add or change a related_name argument to the definition for 'BUser.groups' or 'User.groups'.
NO1002.BUser.user_permissions: (fields.E304) Reverse accessor for 'BUser.user_permissions' clashes with reverse accessor for 'AUser.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'BUser.user_permissions' or 'AUser.user_permissions'.
NO1002.BUser.user_permissions: (fields.E304) Reverse accessor for 'BUser.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'BUser.user_permissions' or 'User.user_permissions'.
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'AUser.groups'.
HINT: Add or change a related_name argument to the definition for 'User.groups' or 'AUser.groups'.
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'BUser.groups'.
HINT: Add or change a related_name argument to the definition for 'User.groups' or 'BUser.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'AUser.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'AUser.user_permissions'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'BUser.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'BUser.user_permissions'.
很惊人是不是。于是在查阅之后发现是不能共用一个外键,因为比如permission这类的表都是靠着用户表的id来维护的,这样创建的话就会导致两张user表,关联的都是同一个permission啊,group表。
另外就是如何扩展的这个表,这里直接上代码:
class AUser(AbstractUser):
phoneNumber = models.CharField(_('phoneNumber'), max_length=11, blank=False)
signinposition = models.CharField(_('sign up position'), max_length=90, default='')
picture = models.ImageField(_('head_ico'), upload_to='Content_img', default=None)
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
另外要在settings.py中添加一条语句:
AUTH_USER_MODEL = ‘appname.classname’ (比如在我上方的就可以写成: mysite.AUser)
然后我又创建了BUser表,直接参考把A改成B即可。
弄好了之后,就看到了一开始看到的错误。
再我搜了一番之后,发现找到。有的人说干嘛需要维护两张呢?直接加一个列用于区分不就好了吗?其实我刚开始也是这样想的。但是考虑到未来当用户量多的时候,这样可能会很麻烦,于是我还是坚持建两张表。
怎么扩呢,在下不才,只能选择将配套的子类就行定制了,针对于B表,我为它独立copy出了一套,这样再执行./manager makemigrations OK
./manager migrate
done!
上代码:
import re
from django.db import models
from django.contrib import auth
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User, AbstractUser, BaseUserManager, Group, Permission, AbstractBaseUser, UserManager
from django.core.mail import send_mail
from django.utils import timezone
from django.utils.translation import gettext,gettext_lazy as _
from django.contrib.auth.validators import UnicodeUsernameValidator
phonenumber_regex = r'^[0-9]{11}$'
phonenumber_regex_compiled = re.compile(phonenumber_regex)
def B_update_last_login(sender, user, **kwargs):
"""
A signal receiver which updates the last_login date for
the user logging in.
"""
user.last_login = timezone.now()
user.save(update_fields=['last_login'])
class B_UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, username, phonenumber, email, password, **extra_fields):
"""
Create and save a user with the given username, email, and password.
"""
if not username:
raise ValueError('The given username must be set')
email = self.normalize_email(email)
phonenumber = self.normalize_phonenumber(phonenumber)
username = self.model.normalize_username(username)
user = self.model(username=username, email=email, phonenumber=phonenumber, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, username, phonenumber, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, phonenumber, email, password, **extra_fields)
def create_superuser(self, username, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(username, email, password, **extra_fields)
def normalize_phonenumber(cls, phonenumber):
if phonenumber_regex_compiled.match(phonenumber_regex) != None:
return phonenumber
raise ValueError('Enter a valid Chinese phone number. This value may contain only 11 numbers.')
class B_PermissionManager(models.Manager):
pass
class B_Permission(models.Model):
pass
class B_PermissionManager(models.Manager):
pass
class B_GroupManager(models.Manager):
pass
class B_Group(models.Model):
pass
def _user_get_all_permissions(user, obj):
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_all_permissions"):
permissions.update(backend.get_all_permissions(user, obj))
return permissions
class B_PermissionsMixin(models.Model):
"""
Add the fields and methods necessary to support the Group and Permission
models using the ModelBackend.
"""
class Meta:
abstract = True
def get_group_permissions(self, obj=None):
"""
Return a list of permission strings that this user has through their
groups. Query all available auth backends. If an object is passed in,
return only permissions matching this object.
"""
return None
def get_all_permissions(self, obj=None):
return _user_get_all_permissions(self, obj)
def has_perm(self, perm, obj=None):
"""
Return True if the user has the specified permission. Query all
available auth backends, but return 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, check
permissions for that object.
"""
# 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):
"""
Return True if the user has each of the specified permissions. If
object is passed, check if the user has all required perms for it.
"""
return all(self.has_perm(perm, obj) for perm in perm_list)
def has_module_perms(self, app_label):
"""
Return True if the user has any permissions in the given app label.
Use similar 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)
class B_AbstractUser(AbstractBaseUser, B_PermissionsMixin):
"""
An abstract base class implementing a fully featured User model with
admin-compliant permissions.
Username and password are required. Other fields are optional.
"""
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
validators=[username_validator],
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=150, 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 = B_UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class Meta:
verbose_name = _('B_user')
verbose_name_plural = _('B_users')
abstract = True
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""
Return 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):
"""Return the short name for the user."""
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
class AUser(AbstractUser):
phoneNumber = models.CharField(_('phoneNumber'), max_length=11, blank=False)
signinposition = models.CharField(_('sign up position'), max_length=90, default='')
picture = models.ImageField(_('head_ico'), upload_to='Content_img', default=None)
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
class BUser(B_AbstractUser):
phoneNumber = models.CharField(_('phoneNumber'), max_length=11, blank=False)
signinposition = models.CharField(_('sign up position'), max_length=90, default=None)
picture = models.ImageField(_('head_ico'), upload_to='Content_img', default=None)
class Meta(B_AbstractUser.Meta):
verbose_name = 'B_user'