list_display:
指定数据展示的字段
注意多对多字段不能放在list_display列表里进行展示
list_display_links
指定跳转字段(跳转到当前字段所在数据的编辑页)
search_fields
搜索title里面1或者price包含1的数据
1.在页面上渲染出搜索框
2.支持条件与条件之间的"或"查询
list_filter
1.它不是用来筛选普通字段的,它是用来帮你筛选外键字段的
2.支持组合查询
actions
批量处理数据
# 先定义一个批量处理函数 def patch_init(self,request,queryset): queryset.update(price=666) # 给这个函数命名 不写就是首字母大写的函数名 patch_init.short_description = '价格批量初始化' # 配置到配置类中 actions = [patch_init]
Django启动会加载配置文件
from django.utils.module_loading import autodiscover_modules def autodiscover(): autodiscover_modules('admin', register_to=site) #django启动的时候就会自动执行每一个应用下的admin.py文件
admin site源码
class AdminSite(object): def __init__(self, name='admin'): self._registry = {} # model_class class -> admin_class instance def register(self, model, admin_class=None, **options): if not admin_class: admin_class = ModelAdmin # 所有模型表公共的配置 # (******) {'模型表变量名':'模型表配置类对象'} self._registry[model] = admin_class(model) site = AdminSite()
self._registry 一定一定要搞清楚的是字典里面的键值对到底是什么!!! <class 'django.contrib.auth.models.Group'> auth.GroupAdmin <class 'django.contrib.auth.models.User'> auth.UserAdmin <class 'app01.models.Book'> app01.BookConfig <class 'app01.models.Author'> app01.ModelAdmin <class 'app01.models.Publish'> app01.ModelAdmin <class 'app01.models.AuthorDetail'> app01.ModelAdmin <class 'django.contrib.auth.models.Group'> auth.GroupAdmin <class 'django.contrib.auth.models.User'> auth.UserAdmin <class 'app01.models.Book'> app01.BookConfig <class 'app01.models.Author'> app01.ModelAdmin
路由分发的本质
url(r'^test/',([],None,None))
一级分发
url(r'^test/',([ url(r'^test_1/',test_1), url(r'^test_2/',test_2)],None,None))
二级分发
url(r'^test/', ([ url(r'^test_1/', ([ url(r'^test_1_1/', test_1_1), url(r'^test_1_2/', test_1_2), url(r'^test_1_3/', test_1_3), ], None, None)), url(r'^test_2/', test_2) ], None, None))
url设计
http://127.0.0.1:8000/admin/app01/book/ 书籍的查看 http://127.0.0.1:8000/admin/app01/book/1/change/ 书籍的编辑 http://127.0.0.1:8000/admin/app01/book/1/delete/ 书籍的删除 http://127.0.0.1:8000/admin/app01/book/add/ 书籍的添加
#获取模型表所在应用名 model_class就是你自定义的模型表变量名 app_label = model_class._meta.app_label #获取模型表的字符串小写名 model_name = model_class._meta.model_name
from app01 import models models.Book._meta.model_name #'book' moeels.Book._meta.app_label #'app01'
一个项目可以有多个应用,一个应用可以被多个项目使用!
stark组件启动
1.settings配置文件中注册app的时候,会加载应用下的apps.py的类
from django.utils.module_loading import autodiscover_modules class StarkConfig(AppConfig): name = 'stark' def ready(self): autodiscover_modules('stark') # 固定写法,即可实现项目已启动自动访问每一个应用下的stark.py文件
stark组件注册
from app01 import models models.Book._meta.get_field('title')#获取模型表中的某个字段对象 #<django.db.models.fields.CharField: title> #获取模型表中的某个字段对象的属性 models.Book._meta.get_field('title').verbose_name #'名称' models.Book._meta.get_field('price').verbose_name #'price'
反向解析
别名格式:'应用名/模型表名/功能名'
def get_urls(self): tmp = [ url(r'^$', self.list_view, name='%s_%s_list' % (self.app_label, self.model_name)), url(r'^add/', self.add_view, name='%s_%s_add' % (self.app_label, self.model_name)), url(r'^edit/(\d+)/', self.edit_view, name='%s_%s_edit' % (self.app_label, self.model_name)), url(r'^delete/(\d+)/', self.delete_view, name='%s_%s_delete' % (self.app_label, self.model_name)), ] return tmp
由于所对应的表不同前端渲染页面时需要区分不同的展示效果即每个路由不同
这里的self指的是每次访问的数据表的不同使这个self动态变化
封装了一个反向解析的通用方法
def get_reverse_url(self, type, object=None): if object: _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type), args=(object.pk,)) else: _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type)) return _url
判断这个object参数是否有值来返回不同的结果
form与modelform
def get_model_form_class(self): if self.model_form_class: return self.model_form_class from django.forms import ModelForm class ModelFormClass(ModelForm): class Meta: model = self.model fields = '__all__' widgets = self.get_class() return ModelFormClass
def get_class(self): for i in self.model._meta.fields: if i.__class__.__name__ == 'DateField': a = getattr(i, "__str__")().split('.')[-1] from django.forms import widgets as wid widgets = { a: wid.DateInput(attrs={'class': 'form-control', 'type': 'date'}), } return widgets
多写了一个当你的模型表中某个字段名是日期类型,让它前端渲染页面的时候使input框中的type是date
注册了的模型表的增删改查
需要注意的是使用modelform进行数据新增个修改操作的时候,用的都是save()方法
通过instance参数来区分是修改还是新增操作
class Pagination(object): def __init__(self, current_page, all_count, params, per_page_num=2, pager_count=5): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param pager_count: 最多显示的页码个数 用法: queryset = model.objects.all() page_obj = Pagination(current_page,all_count) page_data = queryset[page_obj.start:page_obj.end] 获取数据用page_data而不再使用原始的queryset 获取前端分页样式用page_obj.page_html """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count self.pager_count_half = int((pager_count - 1) / 2) import copy self.params = copy.deepcopy(params) @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示11/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_end = self.all_pager + 1 pager_start = self.all_pager - self.pager_count + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] # 添加前面的nav和ul标签 page_html_list.append(''' <nav aria-label='Page navigation>' <ul class='pagination'> ''') first_page = '<li><a href="?page=%s">首页</a></li>' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' else: prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) for i in range(pager_start, pager_end): self.params['page'] = i if i == self.current_page: temp = '<li class="active"><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) else: temp = '<li><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled"><a href="#">下一页</a></li>' else: next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) page_html_list.append(last_page) # 尾部添加标签 page_html_list.append(''' </nav> </ul> ''') return ''.join(page_html_list) #多传入了一个params参数进去用到copy的深度拷贝(deepcopy)
stark组件核心代码
from django.conf.urls import url from django.shortcuts import HttpResponse, reverse, render, redirect from django.utils.safestring import mark_safe from app01.utils.mypag import Pagination from django.db.models import Q # 定一个展示类 class ShowList(object): def __init__(self, config_object, queryset, request): self.config_object = config_object self.queryset = queryset self.request = request current_page = self.request.GET.get('page', 1) self.page_object = Pagination(current_page=current_page, all_count=self.queryset.count(), params=self.request.GET) self.page_queryset = self.queryset[self.page_object.start:self.page_object.end] # 获取头部展示 def get_header(self): header_list = [] for header in self.config_object.get_new_list_display(): if isinstance(header, str): if header == '__str__': value = self.config_object.model._meta.model_name.upper() else: value = self.config_object.model._meta.get_field(header).verbose_name else: value = header(self.config_object, is_header=True) header_list.append(value) return header_list # 获取身体展示 def get_body(self): body_list = [] for data in self.page_queryset: tmp = [] for field_or_func in self.config_object.get_new_list_display(): if isinstance(field_or_func, str): value = getattr(data, field_or_func) if field_or_func in self.config_object.list_display_links: _url = self.config_object.get_reverse_url('edit', data) value = mark_safe('<a href ="%s">%s</a>' % (_url, value)) else: value = field_or_func(self.config_object, object=data) tmp.append(value) body_list.append(tmp) return body_list # 获取action def get_action(self): tmp = [] for action in self.config_object.actions: tmp.append({ 'name': action.__name__, 'desc': action.desc }) return tmp # 获取filter def get_filter(self): tmp_dict = {} # 循环拿到list_filter里面的外键字段字符串 for filter_field in self.config_object.list_filter: tmp = [] # 获取关键字所对应的关联表 rel_model = self.config_object.model._meta.get_field(filter_field).rel.to # 获取所有所有关联表的数据 rel_queryset = rel_model.objects.all() filter_value = self.request.GET.get(filter_field) import copy params = copy.deepcopy(self.request.GET) if filter_field in params: params.pop(filter_field) value = "<a href='?%s'>All</a>" % params.urlencode() else: value = "<a href='?'>All</a>" tmp.append(mark_safe(value)) params = copy.deepcopy(self.request.GET) for data in rel_queryset: params[filter_field] = data.pk if filter_value == str(data.pk): value = "<a href='?%s' class='active'>%s</a>" % (params.urlencode(), str(data)) else: value = "<a href='?%s'>%s</a>" % (params.urlencode(), str(data)) tmp.append(mark_safe(value)) tmp_dict[filter_field] = tmp return tmp_dict class ModelStark(object): list_display = ['__str__', ] list_display_links = [] model_form_class = None search_fields = [] actions = [] list_filter = [] # 获取如果是date字段类型默认使type变为date def get_date_class(self): for i in self.model._meta.fields: if i.__class__.__name__ == 'DateField': date_class = getattr(i, "__str__")().split('.')[-1] from django.forms import widgets as wid widgets = { date_class: wid.DateInput(attrs={'class': 'form-control', 'type': 'date'}), } return widgets # 设置默认input框 def get_model_form_class(self): if self.model_form_class: return self.model_form_class from django.forms import ModelForm class ModelFormClass(ModelForm): class Meta: model = self.model fields = '__all__' widgets = self.get_date_class() return ModelFormClass def __init__(self, model): self.model = model self.app_label = self.model._meta.app_label self.model_name = self.model._meta.model_name # 反向解析 def get_reverse_url(self, type, object=None): if object: _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type), args=(object.pk,)) else: _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type)) return _url # 主页展示编辑栏 def edit_col(self, is_header=False, object=None): if is_header: return '编辑' _url = self.get_reverse_url('edit', object) return mark_safe('<a href ="%s">编辑</a>' % _url) # 主页展示删除栏 def delete_col(self, is_header=False, object=None): if is_header: return '删除' _url = self.get_reverse_url('delete', object) return mark_safe('<a href ="%s">删除</a>' % _url) # 主页展示checkbox栏 def check_col(self, is_header=False, object=None): if is_header: return '选择' return mark_safe('<input type="checkbox" name="select_action" value="%s">' % object.pk) # 获取新的list_display def get_new_list_display(self): tmp = [] tmp.append(ModelStark.check_col) tmp.extend(self.list_display) if not self.list_display_links: tmp.append(ModelStark.edit_col) tmp.append(ModelStark.delete_col) return tmp # 获取搜索 def get_search(self, request, queryset): self.key_word = '' key_word = request.GET.get('search') if key_word: self.key_word = key_word q = Q() q.connector = 'or' for search_field in self.search_fields: q.children.append(('%s__icontains' % search_field, key_word)) queryset = queryset.filter(q) return queryset # 获取过滤 def get_filter(self, request, queryset): q = Q() for filter_field in self.list_filter: value = request.GET.get(filter_field) if value: q.children.append((filter_field, value)) queryset = queryset.filter(q) return queryset # 展示所有的信息 def list_view(self, request): # action操作 if request.method == 'POST': # 交给用户自定义函数的字符串名 action_name = request.POST.get('action') if action_name: # 获取用户想要批量处理的数据主键值 pk_list = request.POST.getlist('select_action') # 查出上述id值对应的queryset数据 action_queryset = self.model.objects.filter(pk__in=pk_list) real_action = getattr(self, action_name) real_action(request, action_queryset) # 获取对应模型表的所有数据 queryset = self.model.objects.all() # search功能 queryset = self.get_search(request, queryset) # filter功能 queryset = self.get_filter(request, queryset) show_object = ShowList(self, queryset, request) url = self.get_reverse_url('add') return render(request, 'stark/list_view.html', locals()) # 添加页面 def add_view(self, request): # 获取modelform类变量名 model_form_class = self.get_model_form_class() # 实例化modelform对象 model_form_object = model_form_class() if request.method == "POST": # 获取加号对应的标签id pop_back_id = request.GET.get('pop_back_id') model_form_object = model_form_class(request.POST) if model_form_object.is_valid(): # 保存返回结果为当前保存对象 obj = model_form_object.save() # 判断当前添加页面是否是加号触发 if pop_back_id: pk = obj.pk text = str(obj) return render(request, 'stark/pop.html', locals()) return redirect(self.get_reverse_url('list')) from django.forms.models import ModelChoiceField for field_object in model_form_object: if isinstance(field_object.field, ModelChoiceField): field_object.is_pop = True real_model = self.model._meta.get_field(field_object.name).rel.to real_app_label = real_model._meta.app_label real_model_name = real_model._meta.model_name url = reverse('%s_%s_add' % (real_app_label, real_model_name)) url = url + '?pop_back_id=%s' % field_object.auto_id field_object.url = url return render(request, 'stark/add_view.html', locals()) # 修改页面 def edit_view(self, request, id): edit_obj = self.model.objects.filter(pk=id).first() model_form_class = self.get_model_form_class() model_form_object = model_form_class(instance=edit_obj) if request.method == 'POST': pop_back_id = request.GET.get('pop_back_id') model_form_object = model_form_class(request.POST, instance=edit_obj) if model_form_object.is_valid(): obj = model_form_object.save() # 判断当前添加页面是否是加号触发 if pop_back_id: pk = obj.pk text = str(obj) return render(request, 'stark/pop.html', locals()) model_form_object.save() return redirect(self.get_reverse_url('list')) from django.forms.models import ModelChoiceField for field_object in model_form_object: if isinstance(field_object.field, ModelChoiceField): field_object.is_pop = True real_model = self.model._meta.get_field(field_object.name).rel.to real_app_label = real_model._meta.app_label real_model_name = real_model._meta.model_name url = reverse('%s_%s_add' % (real_app_label, real_model_name)) url = url + '?pop_back_id=%s' % field_object.auto_id field_object.url = url return render(request, 'stark/edit_view.html', locals()) # 删除 def delete_view(self, request, id): self.model.objects.filter(pk=id).delete() return redirect(self.get_reverse_url('list')) # 获取路由 def get_urls(self): tmp = [ url(r'^$', self.list_view, name='%s_%s_list' % (self.app_label, self.model_name)), url(r'^add/', self.add_view, name='%s_%s_add' % (self.app_label, self.model_name)), url(r'^edit/(\d+)/', self.edit_view, name='%s_%s_edit' % (self.app_label, self.model_name)), url(r'^delete/(\d+)/', self.delete_view, name='%s_%s_delete' % (self.app_label, self.model_name)), ] return tmp class StarkSite(): def __init__(self, name='admin'): self._registry = {} def register(self, model, admin_class=None, **options): if not admin_class: admin_class = ModelStark self._registry[model] = admin_class(model) def get_urls(self): tmp = [] for model_class, config_obj in self._registry.items(): app_label = model_class._meta.app_label model_name = model_class._meta.model_name tmp.append( url(r'^%s/%s/' % (app_label, model_name), (config_obj.get_urls(), None, None)) ) return tmp @property def urls(self): return self.get_urls(), None, None # 单利模式 site = StarkSite()
html代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> <style> table { margin-top: 10px; } .active { color: red; } .plus-father { position: relative; } .plus { color: #369; font-size: 24px; position: absolute; top: 19px; right: -28px; } </style> {% block css %} {% endblock %} </head> <body> <div class="container"> <div class="row"> {% block content %} {% endblock %} </div> </div> </body> </html>
{% extends 'stark/base.html' %} {% block css %} <link rel="stylesheet" href="/static/common.css"> {% endblock %} {% block content %} <h3 class="text-center">添加数据</h3> <form action="" method="post"> {% csrf_token %} {% for foo in model_form_object %} <div class="plus-father"> <p>{{ foo.label }}{{ foo }}</p> {% if foo.is_pop %} <span class="plus" onclick="WindowOpen('{{ foo.url }}')">+</span> {% endif %} </div> {% endfor %} <input type="submit" class="btn pull-right btn-primary"> </form> <script> function WindowOpen(url) { window.open(url, '', 'width=800px,height=400px') } function addOption(pop_back_id, pk, text) { let opt = document.createElement('option'); opt.value = pk; opt.innerHTML = text; opt.selected = 'selected'; let sel = document.getElementById(pop_back_id); sel.appendChild(opt); } </script> {% endblock %}
{% extends 'stark/base.html' %} {% block css %} <link rel="stylesheet" href="/static/common.css"> {% endblock %} {% block content %} <h3 class="text-center">编辑数据</h3> <form action="" method="post"> {% csrf_token %} {% for foo in model_form_object %} <div class="plus-father"> <p>{{ foo.label }}{{ foo }}</p> {% if foo.is_pop %} <span class="plus" onclick="WindowOpen('{{ foo.url }}')">+</span> {% endif %} </div> {% endfor %} <input type="submit" class="btn pull-right btn-danger"> </form> <script> function WindowOpen(url) { window.open(url, '', 'width=800px,height=400px') } function addOption(pop_back_id, pk, text) { let opt = document.createElement('option'); opt.value = pk; opt.innerHTML = text; opt.selected = 'selected'; let sel = document.getElementById(pop_back_id); sel.appendChild(opt); } </script> {% endblock %}
{% extends 'stark/base.html' %} {% block content %} <div class="col-md-9"> <h3 class="text-center" style="margin-bottom: 30px">数据展示</h3> <a href="{{ url }}" class="btn btn-primary pull-right" style="margin-bottom: 10px">添加数据</a> {# search功能开始#} {% if show_object.config_object.search_fields %} <form class="form-inline"> <div class="form-group"> <div class="input-group"> <input type="text" class="form-control" id="exampleInputAmount" placeholder="关键字" name="search" value="{{ show_object.config_object.key_word }}"> </div> </div> <button type="submit" class="btn btn-success">Search</button> </form> {% endif %} {# search功能结束#} {# action功能开始#} <form action="" class="form-inline" method="post" style="margin-top: 10px"> {% csrf_token %} <select name="action" class="form-control"> <option value="">----------------</option> {% for foo in show_object.get_action %} <option value="{{ foo.name }}">{{ foo.desc }}</option> {% endfor %} </select> <input type="submit" class="btn btn-danger" value="GO"> {# action功能结束#} {# 表头表单功能开始#} <table class="table-striped table table-bordered table-hover"> <thead class="text-center"> <tr> {% for head in show_object.get_header %} <th>{{ head }}</th> {% endfor %} </tr> </thead> <tbody class="text-center"> {% for body in show_object.get_body %} <tr> {% for foo in body %} <td>{{ foo }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> {# 表头表单功能结束#} </form> <div class="text-center"> {{ show_object.page_object.page_html|safe }} </div> </div> <div class="col-md-3"> {% if show_object.config_object.list_filter %} <div style="margin-top: 160px"> <div class="alert-info text-center">FILTER</div> {% for k,v in show_object.get_filter.items %} <div class="panel panel-default"> <div class="panel-heading">By {{ k }}</div> <div class="panel-body"> {% for foo in v %} <p>{{ foo }}</p> {% endfor %} </div> </div> {% endfor %} </div> {% endif %} </div> {% endblock %}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> </head> <body> <script> window.opener.addOption('{{ pop_back_id }}', '{{ pk }}', '{{ text }}') window.close() </script> </body> </html>
input[name], select { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; }