根据 Django 数据库中的当前项目数生成默认值 - 循环导入问题
Generating a default value based on the current number of items from a Django database - Circular Import Problem
我在做一个 Django 项目,但我真的想不通如何避免 Django 模型和自定义 python 文件之间的循环导入 generators.py
accounts/models.py
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.utils import timezone
from core.generators import make_id_number
class UserManager(BaseUserManager):
def create_user(self, email, username, fname, lname, password, **others):
if not email:
raise ValueError(_('Please Provide Email Address!'))
if not username:
raise ValueError(_('Please Provide User Name!'))
if not fname:
raise ValueError(_('Please Provide First Name!'))
if not lname:
raise ValueError(_('Please Provide Last Name!'))
email = self.normalize_email(email)
user = self.model(
email=email,
username=username,
fname=fname,
lname=lname,
password=password,
**others
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, username, fname, lname, password, **others):
others.setdefault('is_staff', True)
others.setdefault('is_superuser', True)
others.setdefault('is_active', True)
if others.get('is_staff') is False:
raise ValueError(_('Superuser must have \'staff\' permissions!'))
if others.get('is_active') is False:
raise ValueError(_('Superuser must be active!'))
if others.get('is_superuser') is False:
raise ValueError(_('Superuser must have \'superuser\' permissions!'))
return self.create_user(email, username, fname, lname, password, **others)
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('Email'),max_length=150,unique=True)
username = models.CharField(_('Username'),max_length=150,unique=True)
fname = models.CharField(_('First Name'),max_length=150)
mname = models.CharField(_('Middle Name'),max_length=150,null=True,blank=True)
lname = models.CharField(_('Last Name'),max_length=150)
date_joined = models.DateTimeField(_('Date Joined'),default=timezone.now)
last_login = models.DateTimeField(_('Last Login'),auto_now=True)
is_staff = models.BooleanField(_('Staff'),default=False)
is_active = models.BooleanField(_('Active'),default=False)
is_superuser = models.BooleanField(_('Superuser'),default=False)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', 'fname' , 'lname']
def __str__(self):
return self.username
def get_full_name(self):
full_name = (f'{self.fname} {self.lname}')
return full_name.strip()
def get_short_name(self):
return self.fname
class UserIdentification(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
id_number = models.BigIntegerField(_('ID Number'), default=make_id_number, unique=True)
reg_code = models.CharField(_('Registration Code'), max_length=150, unique=True)
def __str__(self):
return (f'{self.user.username}-{self.id_number:09d}')
core/generators.py
import pytz
from django.utils.crypto import get_random_string
from django.utils import timezone
from django.conf import settings
from accounts.models import UserIdentification
# Generate a random password with the given length and given allowed_chars.
# The default value of allowed_chars does not have "I" or "O" or letters and digits that look similar to avoid confusion.
def make_random_password(length=12, allowed_chars='!@#$%^&*()_+-=abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
return get_random_string(length, allowed_chars)
def make_id_number(local_tz=settings.TIME_ZONE):
local_tz = pytz.timezone(local_tz)
local_date = local_tz.normalize(timezone.now()).date()
mm=int(local_date.month)
dd=int(local_date.day)
yy=int(local_date.year-2000)
ccc=int(UserIdentification.objects.all().count())
# ccc=0
id_number=int(f'{mm:02d}{dd:02d}{yy:02d}{ccc+1:03d}')
return id_number
如您所见,这肯定会导致循环导入问题,因为我正在从 core/generators.py
导入 make_id_number
,这将查询数据库以获取使用该模型的当前用户数 UserIdentification
来自 accounts/models.py
,这反过来会要求 core\generators.py
生成一个 ID 号。
如果有人能给我解决方案、提示或关于如何避免这种情况的教程,我将不胜感激。
在python
中有一个避免循环导入的技巧
只导入模块,不从模块导入
在models.py中:import core.generators
在generators.py中:import accounts.models
我知道了
感谢@caot 提醒我。我所做的是修改了 UserIdentificaton 模型 - 删除了 default=make_id_number
并添加了 def save(self, *args, **kwargs)
函数。
account/models.py
...
class UserIdentification(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
id_number = models.BigIntegerField(_('ID Number'), unique=True, blank=True)
reg_code = models.CharField(_('Registration Code'), max_length=150, unique=True, blank=True)
def __str__(self):
return (f'{self.user.username}-{self.id_number:09d}')
def save(self, *args, **kwargs):
ccc = UserIdentification.objects.all().count()+1
self.id_number = make_id_number(ccc)
super(UserIdentification, self).save(*args, **kwargs)
还从 core/generators.py
更新了 make_id_number
函数
core/generators.py
def make_id_number(ccc, local_tz=settings.TIME_ZONE):
local_tz = pytz.timezone(local_tz)
local_date = local_tz.normalize(timezone.now()).date()
mm=int(local_date.month)
dd=int(local_date.day)
yy=int(local_date.year-2000)
return int(f'{mm:02d}{dd:02d}{yy:02d}{ccc:03d}')
对此可能有更优化的答案。但我暂时坚持这个。
在代码中解决此类循环导入的最简单方法是在 core/generators.py
文件中的 make_id_number
函数中导入 UserIdentification
。这将打破模块循环导入,并且仅在调用时在函数范围内导入它。
因此您的文件应如下所示:
# core/generators.py
import pytz
from django.utils.crypto import get_random_string
from django.utils import timezone
from django.conf import settings
###### Remove it from here #####
# Generate a random password with the given length and given allowed_chars.
# The default value of allowed_chars does not have "I" or "O" or letters and digits that look similar to avoid confusion.
def make_random_password(length=12, allowed_chars='!@#$%^&*()_+-=abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
return get_random_string(length, allowed_chars)
def make_id_number(local_tz=settings.TIME_ZONE):
##### Import it here #####
from accounts.models import UserIdentification
local_tz = pytz.timezone(local_tz)
local_date = local_tz.normalize(timezone.now()).date()
mm=int(local_date.month)
dd=int(local_date.day)
yy=int(local_date.year-2000)
ccc=int(UserIdentification.objects.all().count())
# ccc=0
id_number=int(f'{mm:02d}{dd:02d}{yy:02d}{ccc+1:03d}')
return id_number
我在做一个 Django 项目,但我真的想不通如何避免 Django 模型和自定义 python 文件之间的循环导入 generators.py
accounts/models.py
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.utils import timezone
from core.generators import make_id_number
class UserManager(BaseUserManager):
def create_user(self, email, username, fname, lname, password, **others):
if not email:
raise ValueError(_('Please Provide Email Address!'))
if not username:
raise ValueError(_('Please Provide User Name!'))
if not fname:
raise ValueError(_('Please Provide First Name!'))
if not lname:
raise ValueError(_('Please Provide Last Name!'))
email = self.normalize_email(email)
user = self.model(
email=email,
username=username,
fname=fname,
lname=lname,
password=password,
**others
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, username, fname, lname, password, **others):
others.setdefault('is_staff', True)
others.setdefault('is_superuser', True)
others.setdefault('is_active', True)
if others.get('is_staff') is False:
raise ValueError(_('Superuser must have \'staff\' permissions!'))
if others.get('is_active') is False:
raise ValueError(_('Superuser must be active!'))
if others.get('is_superuser') is False:
raise ValueError(_('Superuser must have \'superuser\' permissions!'))
return self.create_user(email, username, fname, lname, password, **others)
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('Email'),max_length=150,unique=True)
username = models.CharField(_('Username'),max_length=150,unique=True)
fname = models.CharField(_('First Name'),max_length=150)
mname = models.CharField(_('Middle Name'),max_length=150,null=True,blank=True)
lname = models.CharField(_('Last Name'),max_length=150)
date_joined = models.DateTimeField(_('Date Joined'),default=timezone.now)
last_login = models.DateTimeField(_('Last Login'),auto_now=True)
is_staff = models.BooleanField(_('Staff'),default=False)
is_active = models.BooleanField(_('Active'),default=False)
is_superuser = models.BooleanField(_('Superuser'),default=False)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', 'fname' , 'lname']
def __str__(self):
return self.username
def get_full_name(self):
full_name = (f'{self.fname} {self.lname}')
return full_name.strip()
def get_short_name(self):
return self.fname
class UserIdentification(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
id_number = models.BigIntegerField(_('ID Number'), default=make_id_number, unique=True)
reg_code = models.CharField(_('Registration Code'), max_length=150, unique=True)
def __str__(self):
return (f'{self.user.username}-{self.id_number:09d}')
core/generators.py
import pytz
from django.utils.crypto import get_random_string
from django.utils import timezone
from django.conf import settings
from accounts.models import UserIdentification
# Generate a random password with the given length and given allowed_chars.
# The default value of allowed_chars does not have "I" or "O" or letters and digits that look similar to avoid confusion.
def make_random_password(length=12, allowed_chars='!@#$%^&*()_+-=abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
return get_random_string(length, allowed_chars)
def make_id_number(local_tz=settings.TIME_ZONE):
local_tz = pytz.timezone(local_tz)
local_date = local_tz.normalize(timezone.now()).date()
mm=int(local_date.month)
dd=int(local_date.day)
yy=int(local_date.year-2000)
ccc=int(UserIdentification.objects.all().count())
# ccc=0
id_number=int(f'{mm:02d}{dd:02d}{yy:02d}{ccc+1:03d}')
return id_number
如您所见,这肯定会导致循环导入问题,因为我正在从 core/generators.py
导入 make_id_number
,这将查询数据库以获取使用该模型的当前用户数 UserIdentification
来自 accounts/models.py
,这反过来会要求 core\generators.py
生成一个 ID 号。
如果有人能给我解决方案、提示或关于如何避免这种情况的教程,我将不胜感激。
在python
中有一个避免循环导入的技巧只导入模块,不从模块导入
在models.py中:import core.generators
在generators.py中:import accounts.models
我知道了
感谢@caot 提醒我。我所做的是修改了 UserIdentificaton 模型 - 删除了 default=make_id_number
并添加了 def save(self, *args, **kwargs)
函数。
account/models.py
...
class UserIdentification(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
id_number = models.BigIntegerField(_('ID Number'), unique=True, blank=True)
reg_code = models.CharField(_('Registration Code'), max_length=150, unique=True, blank=True)
def __str__(self):
return (f'{self.user.username}-{self.id_number:09d}')
def save(self, *args, **kwargs):
ccc = UserIdentification.objects.all().count()+1
self.id_number = make_id_number(ccc)
super(UserIdentification, self).save(*args, **kwargs)
还从 core/generators.py
make_id_number
函数
core/generators.py
def make_id_number(ccc, local_tz=settings.TIME_ZONE):
local_tz = pytz.timezone(local_tz)
local_date = local_tz.normalize(timezone.now()).date()
mm=int(local_date.month)
dd=int(local_date.day)
yy=int(local_date.year-2000)
return int(f'{mm:02d}{dd:02d}{yy:02d}{ccc:03d}')
对此可能有更优化的答案。但我暂时坚持这个。
在代码中解决此类循环导入的最简单方法是在 core/generators.py
文件中的 make_id_number
函数中导入 UserIdentification
。这将打破模块循环导入,并且仅在调用时在函数范围内导入它。
因此您的文件应如下所示:
# core/generators.py
import pytz
from django.utils.crypto import get_random_string
from django.utils import timezone
from django.conf import settings
###### Remove it from here #####
# Generate a random password with the given length and given allowed_chars.
# The default value of allowed_chars does not have "I" or "O" or letters and digits that look similar to avoid confusion.
def make_random_password(length=12, allowed_chars='!@#$%^&*()_+-=abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
return get_random_string(length, allowed_chars)
def make_id_number(local_tz=settings.TIME_ZONE):
##### Import it here #####
from accounts.models import UserIdentification
local_tz = pytz.timezone(local_tz)
local_date = local_tz.normalize(timezone.now()).date()
mm=int(local_date.month)
dd=int(local_date.day)
yy=int(local_date.year-2000)
ccc=int(UserIdentification.objects.all().count())
# ccc=0
id_number=int(f'{mm:02d}{dd:02d}{yy:02d}{ccc+1:03d}')
return id_number