from bm_system.models import User
from django.contrib import admin
from django.apps import apps
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.core.exceptions import ValidationError
# Register your models here.# 自动注册所有model
models = apps.get_models()for model in models:try:
admin.site.register(model)except admin.sites.AlreadyRegistered:pass# 修改user创建表单classUserCreationForm(forms.ModelForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)classMeta:
model = User
fields =('username','email','first_name','last_name')defclean_password2(self):# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")if password1 and password2 and password1 != password2:raise ValidationError("Passwords don't match")return password2
defsave(self, commit=True):# Save the provided password in hashed format
user =super().save(commit=False)
user.set_password(self.cleaned_data["password1"])if commit:
user.save()return user
# 修改user修改表单classUserChangeForm(forms.ModelForm):"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
disabled password hash display field.
"""
password = ReadOnlyPasswordHashField()classMeta:
model = User
fields =('username','email','password','first_name','last_name','is_active','is_superuser')# 修改creatersuperuser命令显示表单信息classUserAdmin(BaseUserAdmin):# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.# These override the definitions on the base UserAdmin# that reference specific fields on auth.User.
list_display =('username','email','first_name','last_name','is_active','is_superuser')
fieldsets =((None,{'fields':('username','email','password')}),('Personal info',{'fields':('first_name','last_name')}),('Permissions',{'fields':('is_active','is_superuser')}),)# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets =((None,{'classes':('wide',),'fields':('username','email','first_name','last_name','password1','password2'),}),)
search_fields =('email','username','first_name','last_name')
ordering =('email','username','first_name','last_name')
filter_horizontal =()
自定义app信息
# 根目录或app目录下均可from django.apps import AppConfig
classBMSystemConfig(AppConfig):
default_auto_field ='django.db.models.BigAutoField'# 修改表自增为BigAuto,默认Auto
name ='bm_system'# 设定app name
deflogin(request, user, backend=None):"""
Persist a user id and a backend in the request. This way a user doesn't
have to reauthenticate on every request. Note that data set during
the anonymous session is retained when the user logs in.
"""
session_auth_hash =''if user isNone:
user = request.user
ifhasattr(user,'get_session_auth_hash'):
session_auth_hash = user.get_session_auth_hash()# 查找现有请求中是否存在session信息,如果存在但信息和pk等信息不服则执行flush清除session,否则执行cycle_key新建或刷新sessionif SESSION_KEY in request.session:if _get_user_session_key(request)!= user.pk or(
session_auth_hash andnot constant_time_compare(request.session.get(HASH_SESSION_KEY,''), session_auth_hash)):# To avoid reusing another user's session, create a new, empty# session if the existing session corresponds to a different# authenticated user.
request.session.flush()else:# 在使用redis时调用此步骤将通过调用django/contrib/sessions/backends/base.py中cycle_key方法进行redis存储
request.session.cycle_key()# TODO 修改缓存存储内容# user_data = {'id': user.pk, 'username': user.username, 'email': user.email, 'is_active': user.is_active,# 'is_staff': user.is_staff, 'is_superuser': user.is_superuser}# request.session.cycle_key(user_data)try:
backend = backend or user.backend
except AttributeError:
backends = _get_backends(return_tuples=True)iflen(backends)==1:
_, backend = backends[0]else:raise ValueError('You have multiple authentication backends configured and ''therefore must provide the `backend` argument or set the ''`backend` attribute on the user.')else:ifnotisinstance(backend,str):raise TypeError('backend must be a dotted import path string (got %r).'% backend)
request.session[SESSION_KEY]= user._meta.pk.value_to_string(user)
request.session[BACKEND_SESSION_KEY]= backend
request.session[HASH_SESSION_KEY]= session_auth_hash
ifhasattr(request,'user'):
request.user = user
rotate_token(request)
user_logged_in.send(sender=user.__class__, request=request, user=user)
# django/contrib/sessions/backends/base.pydefcycle_key(self):"""
Create a new session key, while retaining the current session data.
"""
data = self._session
key = self.session_key
# 此处因为setting中session设置使用cache,所以会调用django/contrib/sessions/backends/cache.py中create方法
self.create()
self._session_cache = data
if key:
self.delete(key)
# django/contrib/sessions/backends/cache.pydefcreate(self):# Because a cache can fail silently (e.g. memcache), we don't know if# we are failing to create a new session because of a key collision or# because the cache is missing. So we try for a (large) number of times# and then raise an exception. That's the risk you shoulder if using# cache backing.for i inrange(10000):# 设置session
self._session_key = self._get_new_session_key()try:# 调用存储保存至redis
self.save(must_create=True)except CreateError:continue
self.modified =Truereturnraise RuntimeError("Unable to create a new session key. ""It is likely that the cache is unavailable.")defsave(self, must_create=False):if self.session_key isNone:return self.create()if must_create:
func = self._cache.add
# elif self._cache.get(self.cache_key) is not None:elif self._cache.get(self.session_key)isnotNone:
func = self._cache.setelse:raise UpdateError
# 此处func就是调用django-redis中set或add方法进行存储session至redis中,原会在cache_key中添加cache_key_prefix前缀,未避免麻烦可以在cache_key中修改# self.cache_key为传入redis key# self._get_session(no_load=must_create)为传入redis value,非必要不建议修改此值,本身可通过此对象_auth_user_id进去user表中查找对应用户信息,若需修改最好使用内部调用然后追加或合并结果的方式,不然在admin管理页面使用时将无法使用# self.get_expiry_age())为传入redis TTL
result = func(self.cache_key,
self._get_session(no_load=must_create),
self.get_expiry_age())if must_create andnot result:raise CreateError
修改logout
deflogout(request):"""
Remove the authenticated user's ID from the request and flush their session
data.
"""# Dispatch the signal before the user is logged out so the receivers have a# chance to find out *who* logged out.# TODO 直接发送登出接口时添加Authorization检测# 为了使logout在前后端分离中也可使用,分别检测header authorization 及cookie
authorization = request.META.get('HTTP_AUTHORIZATION')if authorization:
token = authorization
# 从Redis中取出token,前提是已经在settings中配置Redis
_cache = caches[settings.SESSION_CACHE_ALIAS]
user = _cache.get(token)if user:
user = bm_system.models.User.objects.filter(pk=user['_auth_user_id'])else:
user =None
user_logged_out.send(sender=user.__class__, request=request, user=user)
request.session.flush()else:
user =getattr(request,'user',None)ifnotgetattr(user,'is_authenticated',True):
user =None
user_logged_out.send(sender=user.__class__, request=request, user=user)
request.session.flush()ifhasattr(request,'user'):from django.contrib.auth.models import AnonymousUser
request.user = AnonymousUser()
修改session middleware
# django/contrib/sessions/middleware.pydefprocess_response(self, request, response):"""
If request.session was modified, or if the configuration is to save the
session every time, save the changes and set a session cookie or delete
the session cookie if the session has been emptied.
"""try:
accessed = request.session.accessed
modified = request.session.modified
empty = request.session.is_empty()except AttributeError:return response
# First check if we need to delete this cookie.# The session should be deleted only if the session is entirely empty.# 有cookie但是显示session为空则会删除cookieif settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
response.delete_cookie(
settings.SESSION_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN,
samesite=settings.SESSION_COOKIE_SAMESITE,)
patch_vary_headers(response,('Cookie',))else:if accessed:
patch_vary_headers(response,('Cookie',))if(modified or settings.SESSION_SAVE_EVERY_REQUEST)andnot empty:if request.session.get_expire_at_browser_close():
max_age =None
expires =Noneelse:
max_age = request.session.get_expiry_age()
expires_time = time.time()+ max_age
expires = http_date(expires_time)# Save the session data and refresh the client cookie.# Skip session save for 500 responses, refs #3881.if response.status_code !=500:try:# 每次session中间件均会检测刷新session,存储方式何在此处进行修改
request.session.save()# TODO 修改缓存存储内容# token = request.session.session_key# _cache = caches[settings.SESSION_CACHE_ALIAS]# user = _cache.get(token)# request.session.save(user=user)except UpdateError:raise SuspiciousOperation("The request's session was deleted before the ""request completed. The user may have logged ""out in a concurrent request, for example.")
response.set_cookie(
settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE orNone,
httponly=settings.SESSION_COOKIE_HTTPONLY orNone,
samesite=settings.SESSION_COOKIE_SAMESITE,)return response