01: 重写Django admin
目录:
- 1.1 重写Django admin项目各文件作用#
- 1.2 重写Django admin用户认证
- 1.3 将要显示的表注册到我们自己的kind_admin.py中
- 1.4 项目首页:显示注册的app名、表名(kind_admin_index.html 页面1)
- 1.5 展示表中各条数据(display_table_obj.html 页面2)
- 1.6 添加数据(table_obj_add.html 页面3)
- 1.7 修改数据(table_obj_change.html 页面4)
- 1.8 删除数据(table_obj_delete.html 页面5)
- 1.9 用户登录(login.html 页面6)
- 1.10 添加用户(rewrite_add_user_page.html)
- 1.11 用户重置密码
- 1.12 实现用户对各表增删改查的权限管理
1.1 重写Django admin项目各文件作用返回顶部
- C:
- └─DjangoAdminUser_0922
- │ db.sqlite3 #数据库
- │ manage.py #Django管理
- │
- ├─DjangoAdminUser_0922
- │ │ settings.py #指定使用哪个表代替Django Admin的User表,指定注销后跳转页面
- │ │ urls.py #项目总url路径
- │
- ├─crm
- │ │ admin.py #原生Django Admin管理配置
- │ │ models.py #使用自定义的UserProfile表代替Django Admin原生的User表
- │
- ├─kind_admin
- │ │ forms.py #动态生成ModelForm类,用来对数据做验证
- │ │ register_admin.py #定义register函数,注册"xxxAdmin"
- │ │ kind_admin.py #重写Django Admin
- │ │ urls.py #kind_admin这个APP中自己的url路径
- │ │ utils.py #更具过滤,搜索,排序条件返回执行内容
- │ │ views.py #kind_admin增删改查,登录注销的管理函数
- │ │
- │ ├─permissions
- │ │ │ permission.py #判断是否有权限
- │ │ │ permission_list.py #定义权限字典
- │ │
- │ ├─templatetags
- │ │ │ tags.py #展示表中数据,分页,在前端生成搜索,过滤框
- │
- ├─static
- │ ├─css
- │ │ bootstrap-theme.css #对 bootstrap.css 基础样式的重定义(可以没有)
- │ │ bootstrap.css #bootstrap全局css样式
- │ │ dashboard.css #下载bootstrap后台模板时的css样式
- │ │
- │ ├─fonts #bootstrap图标样式,必须有但不必在文件中引入
- │ │ glyphicons-halflings-regular.eot
- │ │ glyphicons-halflings-regular.svg
- │ │ glyphicons-halflings-regular.ttf
- │ │ glyphicons-halflings-regular.woff
- │ │ glyphicons-halflings-regular.woff2
- │ │
- │ └─js
- │ bootstrap.js #bootstrap的js文件
- │ jquery-1.12.4.js #jquery的js文件,引入时必须显示引入jQuery的js文件
- │
- └─templates
- └─kind_admin
- base.html #仅引入必要的css和js文件路径
- kind_admin_index.html #展示APP名和表名
- display_table_obj.html #展示表中具体信息
- table_obj_add.html #增加
- table_obj_change.html #修改
- table_obj_delete.html #删除
- login.html #kind_admin登录界面
- password_reset.html #修改Django Admin用户表的密码
- rewrite_add_user_page.html #添加Django Admin用户时密码明文,用独立页面解决
重写Django admin项目中各文件及作用
- from django.conf.urls import url,include
- from django.contrib import admin
- urlpatterns = [
- url(r'^admin/', admin.site.urls),
- url(r'^kind_admin/', include('kind_admin.urls')),
- ]
Project/urls.py 项目总url
- from django.conf.urls import url
- from kind_admin import views
- urlpatterns = [
- url(r'^$', views.kind_admin_index,name='kind_admin_index'),
- url(r'^(\w+)/(\w+)/$', views.display_table_obj,name='display_table_obj'),
- url(r'^(\w+)/(\w+)/add/$', views.table_obj_add,name='table_obj_add'),
- url(r'^(\w+)/(\w+)/(\d+)/change/$', views.table_obj_change,name='table_obj_change'),
- url(r'^(\w+)/(\w+)/(\d+)/change/password/$', views.password_reset,name='password_reset'), #点击某条进行修改
- url(r'^(\w+)/(\w+)/(\d+)/delete/$', views.table_obj_delete,name='table_obj_delete'),
- url(r'^login/$', views.acc_login),
- url(r'^lougout/$', views.acc_logout, name='acc_logout'),
- ]
kind_admin/urls.py
1.2 重写Django admin用户认证返回顶部
1、说明
1. Django Admin中通过python manage.py createsuperuser创建的用户默认存储在自己的User表中
2. 很多时候我们想要借助这个用户认证,但是Django中自带的User表我们是无法添加其他字段的
3. 所以为了更方便的使用Django admin的认证功能,可以使用我们自己的UserProfile表代替Django Admin的User表
2、重写步骤
1、models.py中定义表结构
2、admin.py中注册UserProfile表,并定制UserProfileAdmin
注:如果只定义表结构而没有定制UserProfileAdmin,在页面创建的用户密码为明文,无法登陆admin后台
3、一定要记得到settings.py指定使用我们自定义的UserProfile表做登录验证
4、执行创建表命令
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
5、此时就可以登陆admin后台创建用户,修改面等操作了
- from django.db import models
- from django.utils.translation import ugettext_lazy as _ #国际化
- from django.utils.safestring import mark_safe
- from django.contrib.auth.models import (
- BaseUserManager, AbstractBaseUser,PermissionsMixin
- )
- #1. 创建用户时调用这个类
- class UserProfileManager(BaseUserManager): #这个方法用来创建普通用户
- def create_user(self, email, name, password=None):
- if not email:
- raise ValueError('Users must have an email address')
- user = self.model( #验证email
- email=self.normalize_email(email),
- name=name,
- )
- user.set_password(password) #让密码更安全,设置密码,给密码加盐
- self.is_active = True #指定创建用户默认是active
- user.save(using=self._db) #保存创建信息
- return user
- def create_superuser(self, email, name, password): #这个方法用来创建超级用户
- user = self.create_user(
- email,
- password=password,
- name=name,
- )
- user.is_active = True
- user.is_admin = True
- user.save(using=self._db)
- return user
- #2 创建UserProfile表替代Django admin中的user表做用户登录
- class UserProfile(AbstractBaseUser,PermissionsMixin):
- email = models.EmailField(
- verbose_name='email address',
- max_length=255,
- unique=True,
- null=True
- )
- password = models.CharField(_('password'),
- max_length=128,help_text=mark_safe('''<a href='password/'>修改密码</a>'''))
- name = models.CharField(max_length=32)
- is_active = models.BooleanField(default=True)
- is_admin = models.BooleanField(default=False)
- #roles = models.ManyToManyField("Role",blank=True)
- objects = UserProfileManager() #创建用户时会调用这里类
- USERNAME_FIELD = 'email' #自己指定那个字段作为用户名
- REQUIRED_FIELDS = ['name'] #那些字段是必须的
- # 下面这些是默认方法不必修改它
- def get_full_name(self): # The user is identified by their email address
- return self.email
- def get_short_name(self): # The user is identified by their email address
- return self.email
- def __str__(self): # __unicode__ on Python 2
- return self.email
- def has_perm(self, perm, obj=None): #对用户授权(如果注释掉用户登录后没任何表权限)
- return True
- def has_module_perms(self, app_label): #对用户授权(如果注释掉用户登录后没任何表权限)
- return True
- @property
- def is_staff(self):
- #return self.is_admin #这个必须是指定admin才能登陆Django admin后台
- return self.is_active #这个只要用户时is_active的即可登陆Django admin后台
crm/models.py
- #解决我们改写的Django admin 中user表验证时密码明文问题
- from django.contrib import admin
- from django import forms
- from django.contrib.auth.models import Group
- from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
- from django.contrib.auth.forms import ReadOnlyPasswordHashField
- from app01 import models
- #1、不必改什么(创建用户时调用这个类)
- class UserCreationForm(forms.ModelForm):
- password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
- password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
- class Meta:
- model = models.UserProfile
- fields = ('email', 'name')
- def clean_password2(self):
- password1 = self.cleaned_data.get("password1")
- password2 = self.cleaned_data.get("password2")
- if password1 and password2 and password1 != password2:
- raise forms.ValidationError("Passwords don't match")
- return password2
- def save(self, commit=True):
- # Save the provided password in hashed format
- user = super(UserCreationForm, self).save(commit=False)
- user.set_password(self.cleaned_data["password1"])
- if commit:
- user.save()
- return user
- #2、不必改什么(修改用户时调用这个类)
- class UserChangeForm(forms.ModelForm):
- password = ReadOnlyPasswordHashField()
- class Meta:
- model = models.UserProfile
- fields = ('email', 'password', 'name', 'is_active', 'is_admin')
- def clean_password(self):
- return self.initial["password"]
- #3、定制UserProfile表
- class UserProfileAdmin(BaseUserAdmin):
- # The forms to add and change user instances
- form = UserChangeForm
- add_form = UserCreationForm
- list_display = ('email', 'name', 'is_admin',"is_staff",'password')
- list_filter = ('is_admin',)
- fieldsets = (
- (None, {'fields': ('email', 'password')}),
- ('Personal', {'fields': ('name',)}),
- ('Permissions', {'fields': ('is_admin',"is_active","user_permissions",'groups')}),
- # ('Permissions', {'fields': ('is_admin',"roles","is_active","user_permissions",'groups')}),
- )
- #Permissions后的字典记得加上,is_admin,is_active否则我们无法再前端勾选,那么我们自己新建的用户无法登陆Django Admin后台
- add_fieldsets = (
- (None, {
- 'classes': ('wide',),
- 'fields': ('email', 'name', 'password1', 'password2')}
- # 'fields': ("roles",'email', 'name', 'password1', 'password2')}
- ),
- )
- search_fields = ('email',)
- ordering = ('email',)
- filter_horizontal = ("user_permissions",'groups',) #显示多对多的选项框
- admin.site.register(models.UserProfile, UserProfileAdmin)
- admin.site.unregister(Group)
crm/admin.py
- AUTH_USER_MODEL = 'app01.UserProfile' #app名.表名
settings.py
1.3 将要显示的表注册到我们自己的kind_admin.py中返回顶部
1、说明
1、Django admin中默认提供了将model表注册功能
2、注册过的表就可以通过 http://127.0.0.1:8001/admin/ 登录后台进行管理
3、但是Django admin默认后台管理界面样式很单一,只能进行一些简单定制,无法很好嵌入到我们的项目中
4、在这里我就自己写了一个register函数,动态将model表注册到我们的kind_admin中
- from django.contrib import admin
- from app01 import models
- class UserAdmin(admin.ModelAdmin):
- list_display = ('username','pwd','ut','ctime',)
- list_filter = ('source','consultant','date') #过滤字段
- search_fields = ('qq','name') #搜索匹配字段
- raw_id_fields = ('consult_course',)
- filter_horizontal = ('tags',) #多对多字段显示
- list_per_page = 1 #每页显示几条数据
- list_editable = ('source',) #可编辑的字段
- readonly_fields = ('qq',) #只读字段
- exclude = ('name',) # 添加和修改时那些界面不显示
- date_hierarchy = 'ctime' # 详细时间分层筛选
- actions = ['test_action',] #之定义的action函数
- def test_action(self, request, arg2): # 自定义action函数
- '''
- :param self: crm.CustomerAdmin类本身
- :param request: 客户端request请求
- :param arg2: 前端选中的数据实例
- '''
- admin.site.register(models.User,UserAdmin)
- admin.site.site_header = '重写DjangoAdmin管理系统' # 修改系统显示名称
- admin.site.site_title = '我的后台管理界面' # 修改页面 title
自带Admin自定制常用参数
2、重写步骤
- from crm import models
- from django.shortcuts import render,HttpResponse,redirect
- enabled_admins = {}
- class BaseAdmin(object):
- using_add_func = True #如果需要有单独的添加页面继承时改为false
- using_change_func = True #如果需要有单独的修改页面继承时改为false
- list_display = []
- readonly_fields = []
- list_filter = []
- search_fields = []
- actions = ['delete_selected_objs']
- list_per_page = 5
- modelform_exclude_fields = []
- readonly_table = False
- filter_horizontal =[]
- def delete_selected_objs(self, request, selected_ids):
- app_name = self.model._meta.app_label
- table_name = self.model._meta.model_name
- if self.readonly_table:
- errors = {"readonly_table": "table is readonly,cannot be deleted" }
- else:
- errors = {}
- if request.POST.get("delete_confirm") == "yes":
- if not self.readonly_table: #整张表readonly时不能删除
- objs = self.model.objects.filter(id__in=selected_ids).delete()
- return redirect("/kind_admin/%s/%s/"%(app_name,table_name)) #删除后返回到/kind_admin/crm/customer/页面
- # 这里是通过table_obj_delete.html页面向 /kind_admin/crm/customer/ 的url传送post请求
- return render(request, 'kind_admin/table_obj_delete.html',
- {'app_name': app_name,
- 'table_name': table_name,
- 'selected_ids': ','.join(selected_ids),
- 'action': request._admin_action})
- def default_form_validation(self): # clean钩子对整体验证
- ''' 每个class_admin都可以重写这个方法来对整体验证'''
- def register(model_class,admin_class=BaseAdmin):
- app_name = model_class._meta.app_label
- table_name = model_class._meta.model_name
- if app_name not in enabled_admins:
- enabled_admins[app_name] = {}
- admin_class.model = model_class
- enabled_admins[app_name][table_name] = admin_class
- '''
- enabled_admins = {'crm':{
- 'customer': "<class 'king_admin.kind_admin.CustomerAdmin'>",
- 'customerfollowup': "<class 'king_admin.kind_admin.CustomerFollowUpAdmin'>"}
- }
- '''
kind_admin/register_admin.py 定义register函数,注册"xxxAdmin"
- from kind_admin.register_admin import BaseAdmin
- from kind_admin.register_admin import register
- from kind_admin.register_admin import enabled_admins
- from django.shortcuts import render,HttpResponse,redirect
- from crm import models
- class UserProfileAdmin(BaseAdmin):
- using_add_func = False
- list_display = ('email','name','is_admin','enroll')
- readonly_fields = ('password',)
- modelform_exclude_fields = ["last_login",]
- filter_horizontal = ('user_permissions',)
- search_fields = ['name',]
- modelform_exclude_fields = ['last_login','groups','roles','is_superuser']
- def rewrite_add_page(self,request,app_name,table_name,model_form_class):
- errors = {}
- if request.method == 'POST':
- _password1 = request.POST.get("password")
- _password2 = request.POST.get("password2")
- if _password1 == _password2:
- if len(_password2) > 5:
- form_obj = model_form_class(request.POST)
- if form_obj.is_valid():
- obj = form_obj.save()
- print('obj',obj,type(obj))
- obj.set_password(_password1) #
- obj.save()
- return redirect(request.path.replace("/add/", '/'))
- else:
- errors['password_too_short'] = "muset not less than 6 letters"
- else:
- errors['invalid_password'] = "passwords are not the same"
- form_obj = model_form_class()
- return render(request, 'kind_admin/rewrite_add_user_page.html',
- {'form_obj': form_obj,
- 'app_name': app_name,
- 'table_name': table_name,
- 'table_name_detail': self.model._meta.verbose_name_plural,
- 'admin_class': self,
- 'errors': errors,
- })
- # 在前端显示数据库中不存在的字段
- def enroll(self):
- if self.instance == 1:
- link_name = "报名新课程"
- else:
- link_name = "报名"
- return '''<a href="/crm/customer/%s/enrollment/"> %s </a>''' %(self.instance.id,link_name)
- enroll.display_name = "报名链接"
- def clean_name(self): # clean_字段名 是字段钩子(每个字段都有对应的这个钩子)
- print("name clean validation:", self.cleaned_data["name"])
- if not self.cleaned_data["name"]:
- self.add_error('name', "cannot be null")
- else:
- return self.cleaned_data["name"]
- register(models.UserProfile,UserProfileAdmin)
kind_admin/kind_admin.py 相当于自带的admin.py,注册model表
- from django.shortcuts import render,HttpResponse
- #1 将表注册到我们的kind_admin中
- from kind_admin import kind_admin
- def kind_admin_index(request):
- '''在前端展示app和表名'''
- print('kind_admin',kind_admin.enabled_admins)
- return HttpResponse('ok')
kind_admin/views.py 获取注册的字典enabled_admins
3、使用models类获取表名和APP名称
- pyton manage.py shell #进入有当前Django环境的Python交互器
- from crm import models #在这种环境下就可以导入APP crm的models文件了
- # 1. 通过表格models类获取app名称
- >>> models.UserProfile._meta.app_label # 'crm'
- # 2. 通过表格models类获取对应表名
- >>> models.UserProfile._meta.model_name # 'userprofile'
- # 3. 显示Meta中重新定义的 表名
- >>> models.UserProfile._meta.verbose_name_plural # '账号表'
- # admin_class.model._meta.verbose_name_plural #获取Meta中定义的中文表名
- # 4. 根据字符串的app名字反射导入app中的models模块
- >>> import importlib
- >>> m = importlib.import_module('crm.models') #使用字符串即可导入模块
- <module 'crm.models' from 'C:\\Users\\tom\\PycharmProjects\\MyCRM\\crm\\models.py'>
- >>> m.UserProfile
- <class 'crm.models.UserProfile'>
- def display_table_objs(request,app_name,table_name):
- models_module =importlib.import_module('%s.models'%(app_name))
- model_obj = getattr(models_module,table_name) #通过字符串的表名反射出表对象
使用models类获取表名和APP名称
1.4 项目首页:显示注册的app名、表名(kind_admin_index.html 页面1)返回顶部
1、首页效果图
2、将app名、表名显示到页面中
- urlpatterns = [
- url(r'^$', views.kind_admin_index,name='kind_admin_index'),
- ]
kind_admin/urls.py
- from django.shortcuts import render
- #1 将表注册到我们的kind_admin中
- from kind_admin import kind_admin
- def kind_admin_index(request):
- '''在前端展示app和表名'''
- return render(request,'kind_admin/kind_admin_index.html',{"table_list":kind_admin.enabled_admins})
kind_admin/views.py
- <div style="width: 80%;margin-left: 100px; margin-top: 40px">
- {% block container %}
- <div class="bs-example" data-example-id="simple-table">
- {% for app_name,app_tables in table_list.items %}
- <table class="table table-hover">
- <thead>
- <tr>
- <div class="panel panel-info">
- <div class="panel-heading">
- <h3 class="panel-title">{{ app_name }}</h3>
- </div>
- </div>
- </tr>
- </thead>
- <tbody>
- {% for table_name,admin_class in app_tables.items %}
- <tr>
- <td><a href="{% url 'display_table_obj' app_name table_name %}">{% render_table_name admin_class %}</a></td>
- <td><a href="{% url 'table_obj_add' app_name table_name %}">ADD</a></td>
- <td><a href="{% url 'display_table_obj' app_name table_name %}">CHANGE</a></td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- {% endfor %}
- </div>
- {% endblock %}
- </div>
kind_admin_index.html
- # 显示app中表的详细名称
- @register.simple_tag
- def render_table_name(admin_class):
- # admin_class.model = models.UserProfile # 找到对应表的类
- return admin_class.model._meta.verbose_name_plural
templatetags\tags.py 通过传入的models表类返回对应的表名
1.5 展示表中各条数据(display_table_obj.html 页面2)返回顶部
1、功能点&效果图
- from django.shortcuts import render,HttpResponse,redirect
- from crm import models
- import os
- #1 将表注册到我们的kind_admin中
- from kind_admin import kind_admin
- #2 过滤 排序
- from kind_admin.utils import select_filter,search_filter,table_sort
- #3 分页
- from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
- #4 动态生成ModelForm类
- from kind_admin.forms import create_model_form
- #5 这里是登录和注销需要的模块
- from django.contrib.auth import login,authenticate,logout
- from django.contrib.auth.decorators import login_required #装饰器,用来验证用户是否登录
- #6 导入我们自己写的 通用权限管理组件
- from kind_admin.permissions import permission
- @login_required
- def kind_admin_index(request):
- '''在前端展示app和表名'''
- return render(request,'kind_admin/kind_admin_index.html',{"table_list":kind_admin.enabled_admins})
- # @permission.check_permisssion
- @login_required
- def display_table_obj(request,app_name,table_name):
- '''分页展示每个表中具体内容'''
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- #1. 对select下拉菜单过滤
- obj_list,filter_conditions = select_filter(request,admin_class)
- #2. 对search搜索过滤
- obj_list = search_filter(request,admin_class,obj_list)
- #3. 对表头排序
- obj_list,orderby_key = table_sort(request,admin_class,obj_list)
- search_text = request.GET.get('_q','')
- if not search_text: #没有搜索时显示能够匹配搜索的字段
- search_text = "serach by: %s"%(','.join(admin_class.search_fields))
- if request.method == 'POST': #分发Django admin的action操作
- action = request.POST.get('action')
- selected_ids = request.POST.get('selected_ids')
- if selected_ids:
- selected_ids = selected_ids.split(',')
- selected_objs = admin_class.model.objects.filter(id__in=selected_ids)
- else:
- raise KeyError("No object selected.")
- if hasattr(admin_class,action):
- action_func = getattr(admin_class,action)
- request._admin_action = action
- return action_func(admin_class,request,selected_ids)
- #分页
- paginator = Paginator(obj_list, admin_class.list_per_page) # Show 25 contacts per page
- page = request.GET.get('page')
- try:
- contacts = paginator.page(page)
- except PageNotAnInteger: # 页数为负数(或不是整数)返回第一页
- contacts = paginator.page(1)
- except EmptyPage:
- contacts = paginator.page(paginator.num_pages) # 页数超出范围返回最后一页
- return render(request,'kind_admin/display_table_obj.html',
- {'admin_class':admin_class,
- 'obj_list': contacts,
- 'filter_conditions':filter_conditions,
- 'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail':admin_class.model._meta.verbose_name_plural,
- 'search_text':search_text,
- 'search_filter_text':request.GET.get('_q',''),
- 'orderby_key':orderby_key,
- 'previous_orderby':request.GET.get('o',''),
- })
- # @permission.check_permisssion
- @login_required
- def table_obj_add(request,app_name,table_name):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- admin_class.is_add_form = True
- model_form_class = create_model_form(request,admin_class)
- if not admin_class.using_add_func:
- rewrite_add_page = admin_class.rewrite_add_page(admin_class,request,app_name,table_name,model_form_class)
- return rewrite_add_page
- if request.method == 'POST':
- form_obj = model_form_class(request.POST)
- if form_obj.is_valid():
- form_obj.save()
- return redirect(request.path.replace("/add/",'/'))
- form_obj = model_form_class()
- return render(request,'kind_admin/table_obj_add.html',
- {'form_obj':form_obj,
- 'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'admin_class': admin_class,
- })
- # @permission.check_permisssion
- @login_required
- def table_obj_change(request,app_name,table_name,obj_id):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- model_form_class = create_model_form(request,admin_class)
- obj = admin_class.model.objects.get(id=obj_id)
- form_obj = model_form_class(instance=obj)
- if request.method == 'POST':
- form_obj = model_form_class(request.POST,instance=obj)
- if form_obj.is_valid():
- form_obj.save()
- else:
- print('errors',form_obj.errors)
- return render(request,'kind_admin/table_obj_change.html',
- {'form_obj':form_obj,
- 'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'admin_class':admin_class,
- 'obj_id':obj_id})
- @login_required
- def password_reset(request,app_name,table_name,obj_id):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- model_form_class = create_model_form(request,admin_class)
- obj = admin_class.model.objects.get(id=obj_id)
- errors = {}
- if request.method == 'POST':
- _password1 = request.POST.get("password1")
- _password2 = request.POST.get("password2")
- if _password1 == _password2:
- if len(_password2) >5:
- print('obj reset',obj,type(obj))
- obj.set_password(_password1) #
- obj.save()
- return redirect(request.path.rstrip('password/'))
- else:
- errors['password_too_short'] = "muset not less than 6 letters"
- else:
- errors['invalid_password'] = "passwords are not the same"
- return render(request,'kind_admin/password_reset.html',{'obj':obj,
- 'errors':errors,
- 'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'obj_id':obj_id
- })
- # @permission.check_permisssion
- @login_required
- def table_obj_delete(request,app_name,table_name,obj_id):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- obj = admin_class.model.objects.get(id=obj_id)
- if request.method == "POST":
- obj.delete()
- return redirect("/kind_admin/%s/%s/"%(app_name,table_name))
- return render(request,'kind_admin/table_obj_delete.html',
- {'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'obj_id':obj_id})
- ############################# 下面这一块用来做用户登录注销功能 #####################################
- def acc_login(request):
- errors = {}
- if request.method == 'POST':
- _email = request.POST.get('email')
- _password = request.POST.get('password')
- user = authenticate(username= _email, password=_password) #通过验证会返回一个user对象
- print('user',user)
- if user:
- login(request,user) #Django自动登录,然后创建session
- next_url = request.GET.get("next","/kind_admin/") #登录成功后会跳转的页面,没有next时是/kind_admin/
- #未登录时直接输入url时跳转到登录界面是会加上"next"参数
- return redirect(next_url)
- else:
- errors['error'] = "Wrong username or password!"
- return render(request,'kind_admin/login.html',{'errors':errors})
- def acc_logout(request):
- logout(request)
- return redirect("/kind_admin/login/")
kind_admin/views.py 整体视图函数
2、功能1:对表中一对多字段下拉菜单过滤(list_filter)
- <form>
- {% if admin_class.list_filter %}
- <div class="select-filter-div">
- {% for filter_field in admin_class.list_filter %}
- <div>
- <div class="select-label">{{ filter_field }}: </div>
- {% render_filter_ele filter_field admin_class filter_conditions %}
- </div>
- {% endfor %}
- <button type="submit" class="btn btn-default btn-format">检索</button>
- </div>
- {% endif %}
- </form>
display_table_obj.html
- #搜索功能(下拉框)
- @register.simple_tag
- def render_filter_ele(filter_field,admin_class,filter_conditions):
- '''
- :param filter_field: 一对多字段名(如:user_type)
- :param admin_class: 通过admin_class.model可以获取到表对应的类
- :param filter_conditions: 其他过滤信息的字典格式
- '''
- select_ele = '<select name="{filter_field}" class="form-control select-width">'
- select_ele += '''<option value=''>-----------</ option >'''
- field_obj = admin_class.model._meta.get_field(filter_field)
- if field_obj.choices: # 一对多的choices类型
- for choice_item in field_obj.choices:
- selected = ''
- if filter_conditions.get(filter_field) == str(choice_item[0]):
- selected = 'selected'
- option_ele = '''<option value='%s' %s>%s</ option >'''%(choice_item[0],selected,choice_item[1])
- select_ele += option_ele
- if type(field_obj).__name__ == 'ForeignKey': #一对多字段
- for choice_item in field_obj.get_choices()[1:]:
- selected = ''
- if filter_conditions.get(filter_field) == str(choice_item[0]):
- selected = 'selected'
- option_ele = '''<option value='%s' %s>%s</ option >'''%(choice_item[0],selected,choice_item[1])
- select_ele += option_ele
- if type(field_obj).__name__ in ['DateTimeField', 'DateField']: # 时间类型
- date_els = []
- today_ele = datetime.now().date()
- date_els.append(['今天', datetime.now().date()])
- date_els.append(['昨天', today_ele - timedelta(days=1)])
- date_els.append(['近七天', today_ele - timedelta(days=7)])
- date_els.append(['本月', today_ele.replace(day=1)])
- date_els.append(['近30天', today_ele - timedelta(days=30)])
- date_els.append(['近90天', today_ele - timedelta(days=90)])
- date_els.append(['近180天', today_ele - timedelta(days=180)])
- date_els.append(['本年', today_ele.replace(month=1, day=1)])
- date_els.append(['近365天', today_ele - timedelta(days=365)])
- selected = ''
- for item in date_els:
- if filter_conditions.get('date__gte') == str(item[1]): # choice_item[0]选中的choices字段值的id
- selected = 'selected'
- select_ele += '''<option value='%s' %s>%s</option>''' % (item[1], selected, item[0])
- selected = ''
- filter_field_name = "%s__gte" % filter_field
- else:
- filter_field_name = filter_field
- print('')
- select_ele += '</select>'
- select_ele = select_ele.format(filter_field=filter_field_name)
- return mark_safe(select_ele)
templatetags\tags.py
- #2 过滤
- from kind_admin.utils import select_filter
- def display_table_obj(request,app_name,table_name):
- '''分页展示每个表中具体内容'''
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- #1. 对select下拉菜单过滤
- obj_list,filter_conditions = select_filter(request,admin_class)
kind_admin/views.py 调用utils.py中对应函数进行过滤
- # 将前端下拉菜单过滤条件变成字典形式(并去除关键字)
- def select_filter(request,admin_class):
- filter_conditions = {}
- keywords = ['_q','page','o']
- for k,v in request.GET.items():
- if k in keywords:
- continue
- if v:
- filter_conditions[k]=v
- return admin_class.model.objects.filter(**filter_conditions),filter_conditions
- # 返回的是过滤条件字典:{'user_type_id': '1'}
kind_admin/utils.py 中定义过滤函数,返回过滤内容、过滤条件字典
- <div>
- <div class="select-label">user_type_id: </div>
- <select name="user_type_id" class="form-control select-width">
- <option value="">-----------</option>
- <option value="1">超级用户</option>
- <option value="2">普通用户</option>
- <option value="6">普普通用户</option>
- </select>
- </div>
注1:生成的过滤下拉菜单HTML文件
- >>> c = models.Customer.objects.all()[0] # 必须先获取一条数据,通过字符串字段名获取值
- # 2.1 通过字符串的字段名获取到对应 普通字段的值
- >>> getattr(c,'name')
- 'alex'
- # 2.2 通过字符串的字段名获取到对应 choces字段 的值(s1.choices不为空就是choices字段)
- >>> s1 = c._meta.get_field('source')
- >>> s1.choices
- ((1, '转介绍'), (2, 'qq群'), (3, '官网'), (4, '百度推广'), (5, '51CTO'), (6, '市场推广'))
- >>> getattr(c,'get_source_display')() #找到choices字段中选中的值:source是字段名
- 'QQ群'
- # 2.3 判断多对多字段
- >>> m2m = getattr(c, 'tags') #判断字段是多对多字段
- >>> hasattr(m2m,"select_related")
- True
- >>> m2m.all() #获取多对多选中的值
- <QuerySet [<Tag: tag01>]>
- # 2.4 通过字符串的字段名获取到对应 时间字段 的值
- >>> type(c.date).__name__ #判断表中的date字段是否是日期字段
- 'datetime'
- >>> c.date.strftime("%Y-%m-%d") #如果是日期字段就转换成中文的时间格式
- '2017-08-11'
注2:通过字符串的字段名获取到对应字段的值
3、功能2:指定对表的那些字段进行匹配搜索(search_fields)
- <form>
- {% if admin_class.search_fields %}
- <div class="select-filter-div">
- <div style="float: left;margin-left: 47px;line-height: 30px">
- <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
- </div>
- <div class="form-group" style="width: 30%;margin-left: 10px;float: left;">
- <input type="text" name="_q" class="form-control" placeholder="{{ search_text }}" value="{{ search_filter_text }}">
- </div>
- <button type="submit" class="btn btn-default btn-format" style="float: left">搜索</button>
- </div>
- {% endif %}
- </form>
display_table_obj.html
- from django.shortcuts import render,HttpResponse,redirect
- #1 将表注册到我们的kind_admin中
- from kind_admin import kind_admin
- #2 过滤 排序
- from kind_admin.utils import select_filter,search_filter
- def display_table_obj(request,app_name,table_name):
- '''分页展示每个表中具体内容'''
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- #1. 对select下拉菜单过滤
- obj_list,filter_conditions = select_filter(request,admin_class)
- #2. 对search搜索过滤
- obj_list = search_filter(request,admin_class,obj_list)
kind_admin/views.py 调用utils.py中对应函数进行搜索
- from django.db.models import Q
- # 返回input搜索框过滤后的内容
- def search_filter(request,admin_class,obj_list):
- search_key = request.GET.get('_q','')
- q_obj = Q()
- q_obj.connector = "OR"
- for column in admin_class.search_fields:
- q_obj.children.append(('%s__contains'%column,search_key))
- return obj_list.filter(q_obj)
kind_admin/utils.py 中在指定字段中进行内容的搜索
4、功能3:定义action对选中多条数据操作:如删除(actions)
- <form method="post" onsubmit="return ActionSubmit(this);">
- <div class="select-filter-div">
- <div style="float: left;margin-left: 20px;line-height: 30px">
- Action:
- </div>
- <div class="form-group" style="width: 30%;margin-left: 10px;float: left;">
- <select class="form-control" name="action" id="action_choosed">
- <option value="">-----------</option>
- {% for action in admin_class.actions %}
- <option value="{{ action }}">{{ action }}</option>
- {% endfor %}
- </select>
- </div>
- <button type="submit" class="btn btn-default btn-format" style="float: left">GO</button>
- </div>
- </form>
- <script>
- // 提交前生成:<input name="selected_ids" value="1,2" type="hidden"> append到form中
- function ActionSubmit(ths) {
- var selected_ids = [];
- $('input[tag="obj_checkbox"]:checked').each(function () {
- selected_ids.push($(this).val())
- });
- var action_choosed = $("#action_choosed").val();
- if (!selected_ids.length){
- alert('必须选择操作条目!');
- return false;
- }
- if (!action_choosed.length){
- alert('必须选择要操作的action!');
- return false;
- }
- var inp = document.createElement('input');
- inp.value = selected_ids.toString();
- inp.setAttribute('name','selected_ids');
- inp.setAttribute('type','hidden')
- $(ths).append(inp);
- return true;
- }
- </script>
display_table_obj.html 生成action的下拉菜单
- <table class="table table-hover">
- <thead>
- <tr>
- <th><input type="checkbox" onclick="CheckAllSelect(this);"></th>
- {% for column in admin_class.list_display %}
- {% build_table_header_column column admin_class filter_conditions search_filter_text orderby_key %}
- {% endfor %}
- </tr>
- </thead>
- <tbody>
- {% for obj in obj_list %}
- <tr>
- <td><input tag="obj_checkbox" class="obj_ck" type="checkbox" name="choose_name" value="{{ obj.id }}"></td>
- {% build_table_row request obj admin_class search_filter_text %}
- </tr>
- {% endfor %}
- </tbody>
- </table>
- <script>
- function CheckAllSelect(ths) {
- if ($(ths).prop('checked')) {
- $('input[tag="obj_checkbox"]').prop('checked',true);
- }
- else {
- $('input[tag="obj_checkbox"]').prop('checked',false);
- }
- }
- </script>
display_table_obj.html 点击全部选中
- from django.shortcuts import render,HttpResponse,redirect
- #1 将表注册到我们的kind_admin中
- from kind_admin import kind_admin
- def display_table_obj(request,app_name,table_name):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- if request.method == 'POST': #分发Django admin的action操作
- action = request.POST.get('action')
- selected_ids = request.POST.get('selected_ids')
- if selected_ids:
- selected_ids = selected_ids.split(',')
- selected_objs = admin_class.model.objects.filter(id__in=selected_ids)
- else:
- raise KeyError("No object selected.")
- if hasattr(admin_class,action):
- action_func = getattr(admin_class,action)
- request._admin_action = action
- return action_func(admin_class,request,selected_ids)
kind_admin/views.py 通过字符串action名通过反射调用类中对应的处理action方法
- from django.shortcuts import render,HttpResponse,redirect
- class BaseAdmin(object):
- actions = ['delete_selected_objs']
- def delete_selected_objs(self, request, selected_ids):
- app_name = self.model._meta.app_label
- table_name = self.model._meta.model_name
- if self.readonly_table:
- errors = {"readonly_table": "table is readonly,cannot be deleted" }
- else:
- errors = {}
- if request.POST.get("delete_confirm") == "yes":
- if not self.readonly_table: #整张表readonly时不能删除
- objs = self.model.objects.filter(id__in=selected_ids).delete()
- return redirect("/kind_admin/%s/%s/"%(app_name,table_name)) #删除后返回到/kind_admin/crm/customer/页面
- # 这里是通过table_obj_delete.html页面向 /kind_admin/crm/customer/ 的url传送post请求
- return render(request, 'kind_admin/table_obj_delete.html',
- {'app_name': app_name,
- 'table_name': table_name,
- 'selected_ids': ','.join(selected_ids),
- 'action': request._admin_action})
kind_admin\register_admin.py BaseAdmin类中定义默认action、删除多条数据
5、功能4:将表中数据展示到页面
- <table class="table table-hover">
- <thead>
- <tr>
- <th><input type="checkbox" onclick="CheckAllSelect(this);"></th>
- {% for column in admin_class.list_display %}
- {% build_table_header_column column admin_class filter_conditions search_filter_text orderby_key %}
- {% endfor %}
- </tr>
- </thead>
- <tbody>
- {% for obj in obj_list %}
- <tr>
- <td><input tag="obj_checkbox" class="obj_ck" type="checkbox" name="choose_name" value="{{ obj.id }}"></td>
- {% build_table_row request obj admin_class search_filter_text %}
- </tr>
- {% endfor %}
- </tbody>
- </table>
display_table_obj.html 生成表头和表中数据
- # 显示表字段名字 与 点击字段名进行排序
- @register.simple_tag
- def build_table_header_column(column,admin_class,filter_conditions,search_filter_text,orderby_key,):
- filters = ''
- for k,v in filter_conditions.items():
- filters += "&%s=%s"%(k,v)
- if orderby_key:
- if orderby_key.startswith('-'):
- sort_icon = '''<span class="glyphicon glyphicon-menu-up" aria-hidden="true"></span>'''
- else:
- sort_icon = '''<span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span>'''
- if orderby_key.strip('-') == column:
- orderby_key = orderby_key
- else:
- orderby_key = column
- sort_icon = ''
- else:
- sort_icon = ''
- orderby_key = column
- try: #这里的try是因为显示数据库未定义字段时会报错
- column_verbose_name = admin_class.model._meta.get_field(column).verbose_name.upper()
- ele = '''<th><a href="?o={orderby_key}&_q={search_filter_text}&{filters}">{column_verbose_name}</a>{sort_icon}</th>'''\
- .format(orderby_key=orderby_key,filters=filters,column_verbose_name=column_verbose_name,sort_icon=sort_icon,search_filter_text=search_filter_text)
- except FieldDoesNotExist as e: # 在前端显示数据库中不存在的字段
- column_verbose_name = getattr(admin_class, column).display_name.upper()
- ele = ''' <th><a href="javascript:void(0);">%s</a></th>''' % column_verbose_name
- return mark_safe(ele)
templatetags\tags.py 生成表头,并在表头中添加href属性的a标签,进行排序
- # 显示list_display要显示字段具体内容
- @register.simple_tag
- def build_table_row(request,obj,admin_class,search_filter_text):
- ele = ''
- for index,colunm in enumerate(admin_class.list_display):
- try: #这里的try是因为显示数据库未定义字段时会报错
- field_obj = obj._meta.get_field(colunm)
- field_val = getattr(obj,colunm)
- if field_obj.choices:
- column_data = getattr(obj,'get_%s_display'%colunm)()
- elif hasattr(field_val,"select_related"):
- data_list = []
- column_data = field_val.all().values_list()
- for data in column_data:
- data_list.append(data[1])
- column_data = ' '.join(data_list)
- else:
- column_data = getattr(obj, colunm)
- if type(column_data).__name__ == 'datetime':
- column_data = column_data.strftime("%Y-%m-%d")
- if len(str(column_data)) > 20:
- column_data = str(column_data[0:20])+'...'
- if colunm in admin_class.search_fields:
- if column_data:
- column_data = re.sub(search_filter_text,'''<span style="color: red">%s</span>'''%search_filter_text,column_data)
- if index == 0:
- ele += '''<td><a href="{request_path}{obj_id}/change/">{column_data}</a></td>'''.format(request_path=request.path,obj_id=obj.id,column_data=column_data)
- else:
- ele += '''<td>{column_data}</td>'''.format(column_data=column_data)
- except FieldDoesNotExist as e: # 在前端显示数据库中不存在的字段
- if hasattr(admin_class, colunm):
- column_func = getattr(admin_class, colunm)
- admin_class.instance = obj
- admin_class.request = request
- column_data = column_func()
- ele += "<td>%s</td>" % column_data
- return mark_safe(ele)
templatetags\tags.py 生成表中数据、每条数据添加修改的a标签
6、功能5: 根据字段名进行排序
- <table class="table table-hover">
- <thead>
- <tr>
- <th><a href="?o=email&_q=tom&user_type_id=1">EMAIL ADDRESS</a></th>
- <th><a href="?o=name&_q=tom&user_type_id=1">NAME</a></th>
- <th><a href="javascript:void(0);">报名链接</a></th>
- </tr>
- </thead>
- </table>
display_table_obj.html 表头中排序的html
- from django.shortcuts import render,HttpResponse,redirect
- from kind_admin import kind_admin
- from kind_admin.utils import select_filter,search_filter,table_sort
- def display_table_obj(request,app_name,table_name):
- '''分页展示每个表中具体内容'''
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- #1. 对select下拉菜单过滤
- obj_list,filter_conditions = select_filter(request,admin_class)
- #2. 对search搜索过滤
- obj_list = search_filter(request,admin_class,obj_list)
- #3. 对表头排序
- obj_list,orderby_key = table_sort(request,admin_class,obj_list)
kind_admin/views.py 调用utils.py中对应函数进行排序
- # 将过滤完成的数据进行排序
- def table_sort(request,admin_class,obj_list):
- orderby_key = request.GET.get('o','')
- if orderby_key:
- res = obj_list.order_by(orderby_key)
- if orderby_key.startswith("-"):
- orderby_key = orderby_key.strip("-")
- else:
- orderby_key = "-%s"%(orderby_key)
- else:
- res = obj_list.order_by('-id')
- return res,orderby_key
kind_admin/utils.py 排序
7、功能6:分页
- from django.shortcuts import render,HttpResponse,redirect
- from crm import models
- from kind_admin import kind_admin
- # 分页
- from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
- def display_table_obj(request,app_name,table_name):
- '''分页展示每个表中具体内容'''
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- #分页
- paginator = Paginator(obj_list, admin_class.list_per_page) # Show 25 contacts per page
- page = request.GET.get('page')
- try:
- contacts = paginator.page(page)
- except PageNotAnInteger: # 页数为负数(或不是整数)返回第一页
- contacts = paginator.page(1)
- except EmptyPage:
- contacts = paginator.page(paginator.num_pages) # 页数超出范围返回最后一页
kind_admin/views.py 返回对应第几页的数据
- <div class="pagination">
- <nav aria-label="Page navigation">
- <ul class="pagination">
- {% if obj_list.has_previous %}
- <li><a href="?page={{ obj_list.previous_page_number }}&_q={{ search_filter_text }}&o={{ previous_orderby }}{% render_filter_conditions filter_conditions %}">上一页</a></li>
- {% endif %}
- {% for loop_counter in obj_list.paginator.page_range %}
- {% render_page_ele loop_counter obj_list filter_conditions search_filter_text previous_orderby %}
- {% endfor %}
- {% if obj_list.has_next %}
- <li><a href="?page={{ obj_list.next_page_number }}&_q={{ search_filter_text }}&o={{ previous_orderby }}{% render_filter_conditions filter_conditions %}">下一页</a></li>
- {% endif %}
- </ul>
- </nav>
- </div>
display_table_obj.html 生成分页的标签
- #获取下拉菜单过滤后拼接的url格式字符串格式
- @register.simple_tag
- def render_filter_conditions(filter_conditions):
- filters = ''
- for k, v in filter_conditions.items():
- filters += "&%s=%s" % (k, v)
- return filters
templatetags\tags.py 点击上一页时在href属性中添加现有的过滤条件
- #分页
- @register.simple_tag
- def render_page_ele(loop_counter,query_sets,filter_conditions,search_text,previous_orderby):
- #query_sets.number 获取当前页
- #loop_counter循环到第几页
- filters = ''
- for k,v in filter_conditions.items():
- filters += "&%s=%s"%(k,v)
- if abs(query_sets.number -loop_counter) <= 3:
- ele_class = ""
- if query_sets.number == loop_counter:
- ele_class = 'active'
- ele = '''<li class="%s"><a href="?page=%s&_q=%s&o=%s&%s">%s</a></li>'''\
- %(ele_class,loop_counter,search_text,previous_orderby,filters,loop_counter)
- return mark_safe(ele)
- return ''
templatetags\tags.py 当页数大于某值时中间页码使用省略号代替
8、功能7:显示表中不存在字段
- <thead>
- <tr>
- <th><input type="checkbox" onclick="CheckAllSelect(this);"></th>
- {% for column in admin_class.list_display %}
- {% build_table_header_column column admin_class filter_conditions search_filter_text orderby_key %}
- {% endfor %}
- </tr>
- </thead>
display_table_obj.html 生成表头的html
- # 显示表字段名字 与 点击字段名进行排序
- @register.simple_tag
- def build_table_header_column(column,admin_class,filter_conditions,search_filter_text,orderby_key,):
- filters = ''
- for k,v in filter_conditions.items():
- filters += "&%s=%s"%(k,v)
- if orderby_key:
- if orderby_key.startswith('-'):
- sort_icon = '''<span class="glyphicon glyphicon-menu-up" aria-hidden="true"></span>'''
- else:
- sort_icon = '''<span class="glyphicon glyphicon-menu-down" aria-hidden="true"></span>'''
- if orderby_key.strip('-') == column:
- orderby_key = orderby_key
- else:
- orderby_key = column
- sort_icon = ''
- else:
- sort_icon = ''
- orderby_key = column
- try: #这里的try是因为显示数据库未定义字段时会报错
- column_verbose_name = admin_class.model._meta.get_field(column).verbose_name.upper()
- ele = '''<th><a href="?o={orderby_key}&_q={search_filter_text}&{filters}">{column_verbose_name}</a>{sort_icon}</th>'''\
- .format(orderby_key=orderby_key,filters=filters,column_verbose_name=column_verbose_name,sort_icon=sort_icon,search_filter_text=search_filter_text)
- except FieldDoesNotExist as e: # 在前端显示数据库中不存在的字段
- column_verbose_name = getattr(admin_class, column).display_name.upper()
- ele = ''' <th><a href="javascript:void(0);">%s</a></th>''' % column_verbose_name
- return mark_safe(ele)
templatetags\tags.py 获取数据库中不存在的字段名
1.6 添加数据(table_obj_add.html 页面3)返回顶部
1、功能点&效果图
1、功能1:动态生成ModelForm类
2、功能2:使用ModelForm类生成表单
3、功能3:判断如果是多对多字段特殊处理
4、功能4:如果整张表只读、使用默认钩子验证数据是否修改
2、功能1:动态生成ModelForm类
- def table_obj_add(request,app_name,table_name):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- admin_class.is_add_form = True
- model_form_class = create_model_form(request,admin_class)
- if not admin_class.using_add_func:
- rewrite_add_page = admin_class.rewrite_add_page(admin_class,request,app_name,table_name,model_form_class)
- return rewrite_add_page
- if request.method == 'POST':
- form_obj = model_form_class(request.POST)
- if form_obj.is_valid():
- form_obj.save()
- return redirect(request.path.replace("/add/",'/'))
- form_obj = model_form_class()
- return render(request,'kind_admin/table_obj_add.html',
- {'form_obj':form_obj,
- 'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'admin_class': admin_class,
- })
kind_admin/views.py
- from django.forms import ModelForm,ValidationError
- from django.utils.translation import ugettext as _ #国际化
- #创建动态生成ModelForm类的函数
- def create_model_form(request,admin_class):
- '''
- 创建动态生成ModelForm类的函数
- :param request:
- :param admin_class:
- :return:
- '''
- def default_clean(self):
- '''给所有form默认加一个clean验证:readonly字段验证,对整张只读表验证,clean钩子对整体验证'''
- error_list = []
- if self.instance.id: #这是一个修改的表单,如果为空就是一个添加表单,才判断字段字段值是否改变
- for field in admin_class.readonly_fields:
- field_val = getattr(self.instance,field) #从数据库中取到对应字段的值
- if hasattr(field_val,"select_related"): #多对多字段只读
- m2m_objs = getattr(field_val,"select_related")().select_related()
- m2m_vals = [i[0] for i in m2m_objs.values_list('id')]
- set_m2m_vals = set(m2m_vals) # print("cleaned data",self.cleaned_data)
- set_m2m_vals_from_frontend = set([i.id for i in self.cleaned_data.get(field)])
- if set_m2m_vals != set_m2m_vals_from_frontend: #1 判断多对多字段是否修改
- self.add_error(field,"readonly field")
- continue
- field_val_from_frontand = self.cleaned_data.get(field)
- if field_val != field_val_from_frontand: #2 判断非多对多字段是否修改
- error_list.append(
- ValidationError(
- _('Field %(field)s is readonly,data should be %(val)s'),
- code='invalid',
- params={'field':field,'val':field_val},
- ))
- #readonly_table check
- # if admin_class.readonly_table: #3 防止黑客自己写提交按钮提交整张表都是只读权限的表
- if admin_class.readonly_table: #3 防止黑客自己写提交按钮提交整张表都是只读权限的表
- raise ValidationError(
- _('Table is readonly,cannot be modified ro added'),
- code='invalid',
- )
- self.ValidationError = ValidationError #这样用户自己验证时就可以不必导入了
- #在这个cleaned方法中定义一个允许用户自己定义的方法做验证
- response = admin_class.default_form_validation(self) #4 clean钩子对整体验证
- if response:
- error_list.append(response)
- if error_list:
- raise ValidationError(error_list)
- def __new__(cls,*args,**kwargs):
- '''在创建form时添加样式,为每个字段预留钩子'''
- for field_name,field_obj in cls.base_fields.items():
- field_obj.widget.attrs['class'] = "form-control"
- if not hasattr(admin_class,"is_add_form"):
- if field_name in admin_class.readonly_fields:
- field_obj.widget.attrs['disabled'] = "disabled"
- # clean_字段名 是字段钩子(每个字段都有对应的这个钩子)
- if hasattr(admin_class, "clean_%s" % field_name): # 用户自定义字段验证
- field_clean_func = getattr(admin_class, "clean_%s" % field_name)
- setattr(cls, "clean_%s" % field_name, field_clean_func)
- return ModelForm.__new__(cls) #调用一下ModelForm的__new__方法否则不往下走
- '''动态生成ModelForm'''
- class Meta:
- model = admin_class.model
- fields = "__all__"
- exclude = admin_class.modelform_exclude_fields # 那些字段不显示
- # exclude = ("qq",)
- attrs = {'Meta':Meta}
- _model_form_class = type("DynamicModelForm",(ModelForm,),attrs)
- setattr(_model_form_class,"__new__",__new__)
- setattr(_model_form_class,'clean',default_clean) #动态将_default_clean__函数添加到类中
- return _model_form_class
kind_admin/forms.py 动态生成ModelForm类
3、功能2:使用ModelForm类生成表单
- <div style="margin-bottom: 200px">
- <!-- 1、显示ModelForm验证错误信息 -->
- <span style="color: red;"> {{ form_obj.errors }} </span>
- <form method="POST">
- <!-- 2、循环ModelForm类生成表单 -->
- {% for field in form_obj %}
- <!-- 3、必填字段颜色标记 -->
- <label>
- {% if field.field.required %}
- <span style="font-weight: bold;color: gold">{{ field.label }}</span>
- {% else %}
- {{ field.label }}
- {% endif %}
- </label>
- <div class="col-sm-10">
- <!-- 4、如果是多对多字段特殊处理 -->
- {% if field.name in admin_class.filter_horizontal %}
- <div>
- 若果是多对多字段需要特殊处理
- </div>
- <!-- 5、不是多对多字段直接显示 -->
- {% else %}
- {{ field }} {# 非多对多字段复选框显示 #}
- <span style="color: gray">{{ field.help_text }}</span>
- <span style="color: red">{{ field.errors.as_text }}</span>
- {% endif %}
- </div>
- {% endfor %}
- <!-- 6、整张表只读隐藏delete/save按钮 -->
- {% if not admin_class.readonly_table %}
- <div style="margin-top: 50px;margin-left: 200px">
- {% block table_obj_delete %}
- <button type="submit" class="btn btn-danger pull-left">
- <a href="{% url 'table_obj_delete' app_name table_name obj_id %}">Delete</a>
- {% endblock %}
- </button>
- <button type="submit" class="btn btn-success pull-right">Save</button>
- </div>
- {% endif %}
- </form>
- </div>
display_table_obj.html 生成html表单
4、功能3:判断如果是多对多字段特殊处理
- <form class="form-horizontal" method="POST" onsubmit="return SelectAllChosenData();">
- {% for field in form_obj %}
- <div class="col-sm-10">
- {% if field.name in admin_class.filter_horizontal %}
- <div class="col-md-5" >
- {% get_m2m_obj_list admin_class field form_obj as m2m_obj_list %}
- <select multiple class="filter-select-box" id="id_{{ field.name }}_from">
- {% if field.name in admin_class.readonly_fields and not admin_class.is_add_form %}
- {% for obj in m2m_obj_list %} <!-- 如果多对多是readonly字段就不绑定点击事件 -->
- <option value="{{ obj.id }}" disabled>{{ obj }}</option>
- {% endfor %}
- {% else %}
- {% for obj in m2m_obj_list %}
- <option ondblclick="MoveElementTo(this,'id_{{ field.name }}_to','id_{{ field.name }}_from');" value="{{ obj.id }}">{{ obj }}</option>
- {% endfor %}
- {% endif %}
- </select>
- </div>
- <div class="col-md-1">
- 箭头
- </div>
- <div class="col-md-5"> <!-- 多对多字段显示已经选中的 -->
- {% get_m2m_selected_obj_list form_obj field as selected_obj_list %}
- <select multiple class="filter-select-box" id="id_{{ field.name }}_to" tag="chosen_list" name="{{ field.name }}">
- {% if field.name in admin_class.readonly_fields %}
- {% for obj in selected_obj_list %} <!-- 如果多对多是readonly字段就不绑定点击事件 -->
- <option disabled value="{{ obj.id }}">{{ obj }}</option>
- {% endfor %}
- {% else %}
- {% for obj in selected_obj_list %}
- <option ondblclick="MoveElementTo(this,'id_{{ field.name }}_from','id_{{ field.name }}_to');" value="{{ obj.id }}">{{ obj }}</option>
- {% endfor %}
- {% endif %}
- {{ field.error }}
- </select>
- </div>
- <span style="color: red">{{ field.errors.as_text }}</span>
- {% else %}
- {{ field }} {# 非多对多字段复选框显示 #}
- <span style="color: gray">{{ field.help_text }}</span>
- <span style="color: red">{{ field.errors.as_text }}</span>
- {% endif %}
- </div>
- </div>
- {% endfor %}
- </form>
- <script>
- function SelectAllChosenData() {
- $("select[tag='chosen_list'] option").each(function () {
- $(this).prop('selected',true); // 提交表单前将右边复选框内容全部选中
- });
- //remove all disabled attrs
- var tag = $('form').find("[disabled]").removeAttr('disabled');
- return true
- }
- function MoveElementTo(ele,target_id,new_target_id) {
- var opt_ele = "<option ondblclick='MoveElementTo(this," + '"' + new_target_id + '",' + '"' + target_id + '",' + ")' value=" + $(ele).val() + ">" + $(ele).text() + "</option>";
- $("#" + target_id).append(opt_ele);
- $(ele).remove();
- }
- </script>
display_table_obj.html 对多对字段特殊处理
- # 返回已选中的m2m数据(filter_horizontal)
- @register.simple_tag
- def get_m2m_selected_obj_list(form_obj,field):
- '''
- :param form_obj: 要修改的那条ModelForm实例
- :param field: ModelForm对应字段类(field.name获取字段名)
- :return:
- '''
- if form_obj.instance.id:
- field_obj = getattr(form_obj.instance,field.name)
- return field_obj.all()
- # 返回所有未选中m2m数据(filter_horizontal)
- @register.simple_tag
- def get_m2m_obj_list(admin_class,field,form_obj):
- '''
- :param admin_class:
- :param field: 多选select标签(field.name获取字段名)
- :param form_obj: 自动生成的ModelForm类
- :return: 返回m2m字段所有为选中数据
- '''
- field_obj = getattr(admin_class.model, field.name) #获取表结构中的字段类
- all_obj_list = field_obj.rel.to.objects.all() #取出所有数据(多对多字段)
- #单条数据的对象中的某个字段
- if form_obj.instance.id: #这样就要求我们创建的表必须有id字段
- obj_instance_field = getattr(form_obj.instance,field.name) #获取单条数据的某个字段instance
- selected_obj_list = obj_instance_field.all() #取出所有已选数据(多对多字段)
- else: #代表这是创建一条心数据
- return all_obj_list
- standby_obj_list = []
- for obj in all_obj_list:
- if obj not in selected_obj_list: #selected_obj_list:所有已选数据
- standby_obj_list.append(obj)
- return standby_obj_list
templatetags\tags.py 获取多对多中已选中和未选中数据
1.7 修改数据(table_obj_change.html 页面4)返回顶部
注:修改数据和添加数据html页面相同、仅仅是视图函数中处理有些许差别
- @login_required
- def table_obj_change(request,app_name,table_name,obj_id):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- model_form_class = create_model_form(request,admin_class)
- obj = admin_class.model.objects.get(id=obj_id)
- form_obj = model_form_class(instance=obj)
- if request.method == 'POST':
- form_obj = model_form_class(request.POST,instance=obj)
- if form_obj.is_valid():
- form_obj.save()
- else:
- print('errors',form_obj.errors)
- return render(request,'kind_admin/table_obj_change.html',
- {'form_obj':form_obj,
- 'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'admin_class':admin_class,
- 'obj_id':obj_id})
kind_admin/views.py 处理修改的数据
1.8 删除数据(table_obj_delete.html 页面5)返回顶部
1、功能点&效果图
2、删除数据code
- {% if not admin_class.readonly_table %}
- <div style="margin-top: 50px;margin-left: 200px">
- {% block table_obj_delete %}
- <button type="submit" class="btn btn-danger pull-left">
- <a href="{% url 'table_obj_delete' app_name table_name obj_id %}">Delete</a>
- {% endblock %}
- </button>
- <button type="submit" class="btn btn-success pull-right">Save</button>
- </div>
- {% endif %}
table_obj_add.html 添加页面的删除按钮
- def table_obj_delete(request,app_name,table_name,obj_id):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- obj = admin_class.model.objects.get(id=obj_id)
- if request.method == "POST":
- obj.delete()
- return redirect("/kind_admin/%s/%s/"%(app_name,table_name))
- return render(request,'kind_admin/table_obj_delete.html',
- {'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'obj_id':obj_id})
kind_admin/views.py 视图函数删除提交的条目
- {% extends 'kind_admin/kind_admin_index.html' %}
- {% load tags %}
- {% block url_block %}
- <span> > </span> <a href="/kind_admin/" style="color: white;font-weight: bold">{{ app_name }}</a>
- <span> > </span> <a href="/kind_admin/{{ app_name }}/{{ table_name }}/" style="color: white;font-weight: bold">{{ table_name_detail }}</a>
- <span> > delete</span>
- {% endblock %}
- {% block container %}
- <div style="margin-bottom: 200px">
- <form class="form-horizontal" method="POST">
- <h1>此条数据有多项关联,确定删除?</h1>
- <input type="hidden" name="selected_ids" value="{{ selected_ids }}">
- <input type="hidden" value="yes" name="delete_confirm">
- <input type="hidden" value="{{ action }}" name="action">
- <input type="submit" class="btn btn-danger" value="Yes, I'am sure">
- <a class="btn btn-info" href="{% url 'display_table_obj' app_name table_name %}">No, Tack me back</a>
- </form>
- </div>
- {% endblock %}
table_obj_delete.html 删除页面
1.9 用户登录(login.html 页面6)返回顶部
1、功能点&效果图
2、相关code
- ############################# 下面这一块用来做用户登录注销功能 #####################################
- def acc_login(request):
- errors = {}
- if request.method == 'POST':
- _email = request.POST.get('email')
- _password = request.POST.get('password')
- user = authenticate(username= _email, password=_password) #通过验证会返回一个user对象
- if user:
- login(request,user) #Django自动登录,然后创建session
- next_url = request.GET.get("next","/kind_admin/") #登录成功后会跳转的页面,没有next时是/kind_admin/
- #未登录时直接输入url时跳转到登录界面是会加上"next"参数
- return redirect(next_url)
- else:
- errors['error'] = "Wrong username or password!"
- return render(request,'kind_admin/login.html',{'errors':errors})
- def acc_logout(request):
- logout(request)
- return redirect("/kind_admin/login/")
kind_admin/views.py 视图函数登录和注销功能
- LOGIN_URL = '/kind_admin/login/' # 告诉Django注销后挑传到那个路径
settings.py 告诉Django注销后挑传到那个路径
- {% extends 'kind_admin/base.html' %}
- {% block body %}
- <div class="container col-lg-offset-4">
- <form class="form-signin col-md-4" method="post" action="">
- <h2 class="form-signin-heading">My CRM </h2>
- <label for="inputEmail" class="sr-only">Email address</label>
- <input name="email" type="email" id="inputEmail" class="form-control" placeholder="Email address" required="" autofocus="">
- <label for="inputPassword" class="sr-only">Password</label>
- <input name="password" type="password" id="inputPassword" class="form-control" placeholder="Password" required="">
- {% if errors %}
- <span style="color: red">{{ errors.error }}</span>
- {% endif %}
- <div class="checkbox">
- <label>
- <input type="checkbox" value="remember-me"> Remember me
- </label>
- </div>
- <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
- </form>
- </div> <!-- /container -->
- {% endblock %}
login.html
1.10 添加用户(rewrite_add_user_page.html) 返回顶部
说明:由于上面的table_obj_add.html是通用的表管理、这种有密码的用户注册专门写了一个页面
- from kind_admin.register_admin import BaseAdmin
- from kind_admin.register_admin import register
- from kind_admin.register_admin import enabled_admins
- from django.shortcuts import render,HttpResponse,redirect
- from crm import models
- class UserProfileAdmin(BaseAdmin):
- using_add_func = False
- def rewrite_add_page(self,request,app_name,table_name,model_form_class):
- errors = {}
- if request.method == 'POST':
- _password1 = request.POST.get("password")
- _password2 = request.POST.get("password2")
- if _password1 == _password2:
- if len(_password2) > 5:
- form_obj = model_form_class(request.POST)
- if form_obj.is_valid():
- obj = form_obj.save()
- print('obj',obj,type(obj))
- obj.set_password(_password1) #
- obj.save()
- return redirect(request.path.replace("/add/", '/'))
- else:
- errors['password_too_short'] = "muset not less than 6 letters"
- else:
- errors['invalid_password'] = "passwords are not the same"
- form_obj = model_form_class()
- return render(request, 'kind_admin/rewrite_add_user_page.html',
- {'form_obj': form_obj,
- 'app_name': app_name,
- 'table_name': table_name,
- 'table_name_detail': self.model._meta.verbose_name_plural,
- 'admin_class': self,
- 'errors': errors,
- })
kind_admin/views.py 如果xxxAdmin类中有using_add_func = False配置就使用这个页面添加
- class UserProfileAdmin(BaseAdmin):
- using_add_func = False
kind_admin/kind_admin.py 中配置使用这个页面添加
- {% extends 'kind_admin/kind_admin_index.html' %}
- {% load tags %}
- {% block css %}
- <style>
- .filter-select-box{
- height: 300px!important;
- width: 100%;
- border-radius: 5px;
- }
- </style>
- {% endblock %}
- {% block url_block %}
- <span> > </span> <a href="/kind_admin/" style="color: white;font-weight: bold">{{ app_name }}</a>
- <span> > </span> <a href="/kind_admin/{{ app_name }}/{{ table_name }}/" style="color: white;font-weight: bold">{{ table_name_detail }}</a>
- <span> > change</span>
- {% endblock %}
- {% block container %}
- <div style="margin-bottom: 200px">
- <span style="color: red;"> {{ form_obj.errors }} </span>
- <form class="form-horizontal" method="POST" onsubmit="return SelectAllChosenData();">
- {% for field in form_obj %}
- <div class="form-group">
- <label for="inputEmail3" class="col-sm-2 control-label" style="font-weight: normal">
- {% if field.field.required %}
- <span style="font-weight: bold;color: gold">{{ field.label }}</span>
- {% else %}
- {{ field.label }}
- {% endif %}
- </label>
- <div class="col-sm-10">
- {% if field.name in admin_class.filter_horizontal %}
- <div class="col-md-5" >
- {% get_m2m_obj_list admin_class field form_obj as m2m_obj_list %}
- <select multiple class="filter-select-box" id="id_{{ field.name }}_from">
- {% if field.name in admin_class.readonly_fields and not admin_class.is_add_form %}
- {% for obj in m2m_obj_list %} <!-- 如果多对多是readonly字段就不绑定点击事件 -->
- <option value="{{ obj.id }}" disabled>{{ obj }}</option>
- {% endfor %}
- {% else %}
- {% for obj in m2m_obj_list %}
- <option ondblclick="MoveElementTo(this,'id_{{ field.name }}_to','id_{{ field.name }}_from');" value="{{ obj.id }}">{{ obj }}</option>
- {% endfor %}
- {% endif %}
- </select>
- </div>
- <div class="col-md-1">
- 箭头
- </div>
- <div class="col-md-5"> <!-- 多对多字段显示已经选中的 -->
- {% get_m2m_selected_obj_list form_obj field as selected_obj_list %}
- <select multiple class="filter-select-box" id="id_{{ field.name }}_to" tag="chosen_list" name="{{ field.name }}">
- {% if field.name in admin_class.readonly_fields %}
- {% for obj in selected_obj_list %} <!-- 如果多对多是readonly字段就不绑定点击事件 -->
- <option disabled value="{{ obj.id }}">{{ obj }}</option>
- {% endfor %}
- {% else %}
- {% for obj in selected_obj_list %}
- <option ondblclick="MoveElementTo(this,'id_{{ field.name }}_from','id_{{ field.name }}_to');" value="{{ obj.id }}">{{ obj }}</option>
- {% endfor %}
- {% endif %}
- {{ field.error }}
- </select>
- </div>
- <span style="color: red">{{ field.errors.as_text }}</span>
- {% else %}
- {% if field.name == 'password' %}
- <div class="form-group">
- <div class="col-sm-12">
- <input placeholder="输入密码" type="password" name="password" required="" id="id_password" class="form-control" maxlength="128">
- <span style="color: red">{{ field.errors.as_text }}</span>
- </div>
- </div>
- <div class="form-group">
- <div class="col-sm-12">
- <input placeholder="重复密码" type="password" name="password2" required="" id="id_password" class="form-control" maxlength="128">
- <span style="color: red"></span>
- </div>
- </div>
- {% else %}
- {{ field }} {# 非多对多字段复选框显示 #}
- <span style="color: gray">{{ field.help_text }}</span>
- <span style="color: red">{{ field.errors.as_text }}</span>
- {% endif %}
- {% endif %}
- </div>
- </div>
- {% endfor %}
- {% if not admin_class.readonly_table %}
- <div style="margin-top: 50px;margin-left: 200px">
- <button type="submit" class="btn btn-success pull-right">Save</button>
- </div>
- {% endif %}
- <div>
- <ul>
- {% for k,v in errors.items %}
- <li style="color: red;">{{ k }}:{{ v }}</li>
- </ul>
- {% endfor %}
- </div>
- </form>
- </div>
- {% endblock %}
- {% block js %}
- <script>
- function SelectAllChosenData() {
- $("select[tag='chosen_list'] option").each(function () {
- $(this).prop('selected',true); // 提交表单前将右边复选框内容全部选中
- });
- //remove all disabled attrs
- var tag = $('form').find("[disabled]").removeAttr('disabled');
- return true
- }
- function MoveElementTo(ele,target_id,new_target_id) {
- var opt_ele = "<option ondblclick='MoveElementTo(this," + '"' + new_target_id + '",' + '"' + target_id + '",' + ")' value=" + $(ele).val() + ">" + $(ele).text() + "</option>";
- $("#" + target_id).append(opt_ele);
- $(ele).remove();
- }
- </script>
- {% endblock %}
rewrite_add_user_page.html
1.11 用户重置密码返回顶部
1、功能点&效果图
2、相关code
- class UserProfile(AbstractBaseUser,PermissionsMixin):
- password = models.CharField(_('password'),
- max_length=128,help_text=mark_safe('''<a href='password/'>修改密码</a>'''))
crm/models.py 添加help_text生成页面"修改密码"连接
- def password_reset(request,app_name,table_name,obj_id):
- admin_class = kind_admin.enabled_admins[app_name][table_name]
- model_form_class = create_model_form(request,admin_class)
- obj = admin_class.model.objects.get(id=obj_id)
- errors = {}
- if request.method == 'POST':
- _password1 = request.POST.get("password1")
- _password2 = request.POST.get("password2")
- if _password1 == _password2:
- if len(_password2) >5:
- print('obj reset',obj,type(obj))
- obj.set_password(_password1) #
- obj.save()
- return redirect(request.path.rstrip('password/'))
- else:
- errors['password_too_short'] = "muset not less than 6 letters"
- else:
- errors['invalid_password'] = "passwords are not the same"
- return render(request,'kind_admin/password_reset.html',{'obj':obj,
- 'errors':errors,
- 'app_name':app_name,
- 'table_name':table_name,
- 'table_name_detail': admin_class.model._meta.verbose_name_plural,
- 'obj_id':obj_id
- })
kind_admin/views.py视图函数处理修改密码请求
- {% extends 'kind_admin/kind_admin_index.html' %}
- {% load tags %}
- {% block url_block %}
- <span> > </span> <a href="/kind_admin/" style="color: white;font-weight: bold">{{ app_name }}</a>
- <span> > </span> <a href="/kind_admin/{{ app_name }}/{{ table_name }}/" style="color: white;font-weight: bold">{{ table_name_detail }}</a>
- <span> > </span> <a href="/kind_admin/{{ app_name }}/{{ table_name }}/{{ obj_id }}/change" style="color: white;font-weight: bold">change</a>
- <span> > 修改密码</span>
- {% endblock %}
- {% block container %}
- <div style="width: 1000px;margin: 0 auto">
- <h3>重置【{{ obj.name }}】密码</h3>
- <div class="panel-body col-md-10">
- <form method="post" class="form-horizontal" style="color: gold;font-weight: bolder;font-size: 16px">
- <div class="form-group">
- <label class="col-sm-2 control-label">
- 用户名:
- </label>
- <div class="col-sm-6">
- <input class="form-control" type="text" name="name" value="{{ obj.email }}" disabled>
- </div>
- </div>
- <div class="form-group">
- <label class="col-sm-2 control-label">
- 密码:
- </label>
- <div class="col-sm-6">
- <input class="form-control" type="password" name="password1">
- </div>
- </div>
- <div class="form-group">
- <label class="col-sm-2 control-label">
- 密码(重复):
- </label>
- <div class="col-sm-6">
- <input class="form-control" type="password" name="password2">
- </div>
- </div>
- <div>
- <ul>
- {% for k,v in errors.items %}
- <li style="color: red;">{{ k }}:{{ v }}</li>
- </ul>
- {% endfor %}
- </div>
- <input type="submit" class="btn btn-info center-block" value="提交">
- </form>
- </div>
- </div>
- {% endblock %}
password_reset.html
1.12 实现用户对各表增删改查的权限管理返回顶部
1、原理说明
1. 由于1.2中重写了Django admin用户认证,使用UserProfile表代替原有的的User表进行认证
2. Django中默认有一张auth_permission表,在这张表中可以对Django默认的表进行权限管理
3. 我们自定义的权限字典只要写到任意表的Meta属性里(必须与权限名相同),就可以显示到auth_permisson表中管理
2、使用方法
1、在kind_admin\permissions\permission_list.py文件中定义权限字典
2、在任意表的Meta中定义权限与权限名对应关系
3、在kind_admin\permissions\permission.py文件中定义装饰器,并在装饰器函数中调用perm_check方法判断权限
在perm_check方法中获取request.path和request.method,是否在权限字典中
如果权限字典中有这个条目用request.user.has_perm(permission_name) #判断当前用户是否有这个权限
4、在kind_admin/views.py中需要权限认证的视图函数都使用这个装饰器装饰即可实现表的权限管理
- #url type: 0 = related, (代表是相对路径)
- #url type: 1 = absolute(代表是绝对路径)
- # 用户与权限如何关联
- #1. 将perm_dic里的权限名,写到任意表的Meta属性里(必须与权限名相同)
- #2. 然后执行 python manage.py makemigrations 命令更新数据库中的表
- #3. 更新完成后就可以在我们重写的Django Admin的User表(UserProfile表)中看到可选权限了
- perm_dic = {
- 'crm.can_access_userprofile_list': { #可以看到UserProfile表条目列表
- 'url_type': 1, # 标识url为绝对路径
- 'url': '/kind_admin/crm/userprofile/', # url路径(相对路径就是路径别名)
- 'method': 'GET',
- 'args': [], # 接收参数
- # 'args': ['enroll_id','step']
- },
- 'crm.can_add_userprofile_get': { #可以看到UserProfile表的添加页面
- 'url_type': 0, #0代表使用相对路径名
- 'url': 'table_obj_add', # url路径(相对路径就是路径别名)
- 'method': 'GET',
- 'args': [], # 接收参数
- },
- 'crm.can_add_userprofile_post': { #可以真正添加UserProfile表条目
- 'url_type': 0,
- 'url': 'table_obj_add',
- 'method': 'POST',
- 'args': [],
- },
- 'crm.can_change_userprofile_get': {
- 'url_type': 0,
- 'url': 'table_obj_change',
- 'method': 'GET',
- 'args': [],
- },
- 'crm.can_change_userprofile_post': {
- 'url_type': 0,
- 'url': 'table_obj_change',
- 'method': 'POST',
- 'args': [],
- },
- 'crm.can_delete_userprofile_get': {
- 'url_type': 0,
- 'url': 'table_obj_delete',
- 'method': 'GET',
- 'args': [],
- },
- 'crm.can_delete_userprofile_post': {
- 'url_type': 0,
- 'url': 'table_obj_delete',
- 'method': 'POST',
- 'args': [],
- },
- }
kind_admin\permissions\permission_list.py 文件中定义权限字典
- class UserProfile(AbstractBaseUser,PermissionsMixin):
- class Meta:
- verbose_name_plural = '用户表'
- # 这里是定义权限管理组件
- permissions = (
- ('can_access_userprofile_list','可以访问用户表'),
- ('can_add_userprofile_get','可以访问添加用户表界面'),
- ('can_add_userprofile_post','可以添加用户表记录'),
- ('can_change_userprofile_get','可以访问修改用户表记录'),
- ('can_change_userprofile_post','可以正真修改用户表记录'),
- ('can_delete_userprofile_get', '可以访问删除用户表记录'),
- ('can_delete_userprofile_post','可以正真删除用户表记录'),
- )
在crm/models.py 表的Meta中定义权限管理组件
- from django.shortcuts import HttpResponse,render,redirect
- from kind_admin.permissions import permission_list #定义权限
- from django.core.urlresolvers import resolve #将绝对url转成相对url
- def perm_check(*args,**kwargs):
- '''被装饰器中调用的函数,用来做权限检测的逻辑'''
- request = args[0]
- if request.user.is_authenticated(): #判断用户是否登录
- for permission_name, v in permission_list.perm_dic.items():
- url_matched = False
- if v['url_type'] == 1: # v['url_type'] == 1 代表是绝对路径
- if v['url'] == request.path: #绝对url匹配上
- url_matched = True
- else: # 否则匹配的就是相对路径
- #把request.path中的绝对url请求转成相对url名字
- resolve_url_obj = resolve(request.path)
- if resolve_url_obj.url_name == v['url']: #相对url别名匹配上了
- url_matched = True
- if url_matched: #如果url路径匹配上了才会走这步
- if v['method'] == request.method: #请求方法也匹配上了(如:POST/GET)
- arg_matched = True
- for request_arg in v['args']: #当某些url中要求必须有某些参数,判断这些必须参数没有为空的
- request_method_func = getattr(request,v['method']) #获取: request.POST 或 request.GET方法
- if not request_method_func.get(request_arg): #只要有一个参数没有数据就返回FALSE
- arg_matched = False
- if arg_matched: #走到这里,仅仅代表这个请求和这条权限的定义规则 匹配上了
- if request.user.has_perm(permission_name): #判断当前用户是否有这个权限
- #能走到这里代表:有权限
- return True
- else: #用户未登录返回到登录界面
- return redirect("/account/login/")
- def check_permisssion(func):
- '''检测权限的装饰器'''
- def inner(*args,**kwargs):
- # print("---permissson:",*args,**kwargs)
- perm_check(*args,**kwargs)
- print('perm_check(*args,**kwargs)',perm_check(*args,**kwargs))
- if perm_check(*args,**kwargs) is True: #如果返回True代表有权限
- return func(*args,**kwargs)
- else:
- return HttpResponse('没权限')
- return func(*args,**kwargs)
- return inner
kind_admin\permissions\permission.py文件中定义装饰器
- # 导入我们自己写的 通用权限管理组件
- from kind_admin.permissions import permission
- @permission.check_permisssion
- def display_table_obj(request,app_name,table_name):
- pass
- @permission.check_permisssion
- def table_obj_add(request,app_name,table_name):
- pass
- @permission.check_permisssion
- def table_obj_change(request,app_name,table_name,obj_id):
- pass
- @permission.check_permisssion
- def table_obj_delete(request,app_name,table_name,obj_id):
- pass
kind_admin/views.py中的视图函数使员工装饰器
- C:\Users\tom\Desktop\rewriteDjangoAdmin>python manage.py shell
- >>> from crm import models
- >>> tom = models.UserProfile.objects.get(name='tom')
- >>> tom.has_perm('crm.can_access_userprofile_list')
- True
注:has_perm判断当前用户是否有某权限
01: 重写Django admin的更多相关文章
- 06: Django Admin
目录:Django其他篇 01:Django基础篇 02:Django进阶篇 03:Django数据库操作--->Model 04: Form 验证用户数据 & 生成html 05:Mo ...
- Django admin 显示图片
我有一个表用来储存轮播图片,有一个 `picture` 字段储存的是图片的url,图片的 url 通过上传文件到 cdn 获得.目前这个表的编辑是通过自定义一个 `ModelForm`,然后重写 Dj ...
- Django Admin save 重写 保存
在 django admin管理控制台中,用户按下"Save and add another",表单的字段值仍然填充最后插入的值 并且保存 在ModelAdmin中添加选项save ...
- Django项目:堡垒机(Linux服务器主机管理系统)--01--01堡垒机重写DJANGO账户表
python相关软件安装流程图解————————python安装——————python-3.7.1-amd64 https://www.cnblogs.com/ujq3/p/10098166.htm ...
- Django admin 权威指南(一)
版本: Django 1.10 此部分由官方文档<6.5.1 The Django admin site>翻译而来. 6.5.1.1 概览 默认情况下,使用startproject的时候, ...
- SQLAlchemy连接数据库并在django admin显示
SQLAlchemy 0.7 postgersql 9.0 SQLAlchemy连接数据库有两种方法,一种是classic,一种是modern 1,modern方法 from sqlalch ...
- django admin 设置(转载https://www.cnblogs.com/wumingxiaoyao/p/6928297.html)
Django admin 一些有用的设置 Django自带的后台管理是Django明显特色之一,可以让我们快速便捷管理数据.后台管理可以在各个app的admin.py文件中进行控制.以下是我最近摸 ...
- DJANGO ADMIN 一些有用的设置(转)
DJANGO ADMIN 一些有用的设置 Django自带的后台管理是Django明显特色之一,可以让我们快速便捷管理数据.后台管理可以在各个app的admin.py文件中进行控制.以下是我最近摸 ...
- 定制django admin页面的跳转
在django admin的 change_view, add_view和delete_view页面,如果想让页面完成操作后跳转到我们想去的url,该怎么做 默认django admin会跳转到ch ...
随机推荐
- android speakerphone/
http://www.cnblogs.com/innost/archive/2011/01/22/1942149.html http://blog.sina.com.cn/s/blog_5418969 ...
- java正则表达式使用
用正则表达式进行字符串校验,截取十分方便,项目开发中灵活的使用正则表达式能让你的代码简洁优雅并且不易出错.在实际的应用中,掌握以下几点知识就可以应付绝大多数的开发场景了. 1. 字符串开始结束匹配:^ ...
- 自定义maven插件
之前虽然一直知道maven插件是可以自定义的,不过一致没有用过.最近接触到了swagger项目中的codegen自动生成代码的功能,并且在codegen源码中,也是存在maven插件功能的,所以自己就 ...
- Java Hash Collision之数据生产
上一篇文章一种高级的DoS攻击-Hash碰撞攻击我通过伪造Hash Collision数据实现了对Java的DoS攻击,下面说说如何生产大量的攻击数据. HashTable是一种非常常用的数据结构.它 ...
- Memcached集群架构方面的问题(转)
add by zhj: 这是一个系列中的第二篇,该系列有四篇,英文原文没找到,译文见:http://blog.csdn.net/jarfield/article/details/4336035 ,附上 ...
- 【Python】Pycharm2018激活方式【亲测好用】
2.激活码激活 优点:Window.Mac.Ubantu都稳定有效,关键是这种激活方式不会产生其他影响 缺点:需要修改hosts文件 修改hosts文件将0.0.0.0 account.jetbrai ...
- csv文件的读写
# -*- coding: utf-8 -*- """ Spyder Editor This is a temporary script file. "&quo ...
- Spark中的Spark Shuffle详解
Shuffle简介 Shuffle描述着数据从map task输出到reduce task输入的这段过程.shuffle是连接Map和Reduce之间的桥梁,Map的输出要用到Reduce中必须经过s ...
- Apache 配置多个HTTPS站点
作中经常会遇到多个站点实现https访问,并指向同一个网页,本文将详解如何在Centos 环境下配置Apache多站点实现HTTPS访问. 准备工作 OS:CentOS release 6.8 (Fi ...
- (Power Strings)sdutoj2475
#include <stdio.h>#include <string.h>#include <stdlib.h>char a[1000001];int next[1 ...