【Django】:重构Admin

自定义KingAdmin

  通过admin样式自己做KingAdmin

提前需知道的model操作

# 获取app名
>>> models.Customer._meta.app_label
'repository'

# 获取数据表名
>>> models.Customer._meta.verbose_name     # verbose_name
'customer'
>>> models.Customer._meta.verbose_name_plural
'客户表'  
>>> models.Customer._meta.model_name      #表名小写
'customer'    

# 获取数据表字段、是否为choices类型
>>> models.Customer._meta.get_field('status')
<django.db.models.fields.SmallIntegerField: status>    #字段
>>> models.Customer._meta.get_field('status').choices   #choices字段
((0, '已报名'), (1, '未报名'), (2, '已退学'), (3, '其他'))      
>>> models.Customer._meta.get_field('id').choices     # 普通字段
[]
>>> models.Customer._meta.get_field('consultant').choices  #外键字段
[]

 

1、在Django项目中创建kingadmin

 

2、app_config.py获取已注册以及存在kingadmin.py的App

from django import conf

for app in conf.settings.INSTALLED_APPS:
    try:
        __import__('%s.kingadmin'%app)
    except ImportError as e:
        print('%s has no model kingadmin'%app)

 

3、views.py加载自定义的app_config.py文件

from django.shortcuts import render

from kingadmin import app_config  # 加载app_config
from kingadmin.base_admin import site

def app_index(request):

    print(id(site),site.registered_sites)
    return render(request, 'kingadmin/app_index.html',{'site':site})

def table_data_list(request,app_name,model_name):
    admin_obj = site.registered_sites[app_name][model_name]
    admin_obj.querysets =  admin_obj.model.objects.all()

    return render(request,"kingadmin/table_data_list.html",locals())    # locals 传入局部变量

 

4、base_admin.py定义注册的model(核心)

class AdminRegisterException(Exception):
    def __init__(self,msg):
        self.message = msg


class BaseAdmin(object):
    list_display = ()
    list_filter = ()
    search_fields = ()
    list_editable = ()


class Adminsite(object):
    def __init__(self):
        self.registered_sites = {}

    def register(self,model,admin_class=None):
        '''
        :param model: <class 'crm.models.Course'>
        :param admin_class:
        :return:
        '''
        app_name = model._meta.app_label       # python manage.py shell
        model_name = model._meta.model_name     # dir(<class 'crm.models.Course'>) 查看具有的方法
        if not admin_class:
            admin_class = BaseAdmin

        if app_name not in self.registered_sites:
            self.registered_sites[app_name]={}

        if model_name in self.registered_sites[app_name]:
            raise AdminRegisterException("app [%s] model [%s] has already registered!" % (app_name, model_name))

        # admin_obj = admin_class()
        admin_obj = admin_class()
        admin_obj.model = model
        self.registered_sites[app_name][model_name] = admin_obj

site = Adminsite()

# site = {
#     'crm':{
#         'customers':CustomerAdmin,
#         'customerfollowup':CustomerFollowUPAdmin,
#     }
# }

 

5、crm目录下创建kingadmin.py进行models注册

from kingadmin.base_admin import site,BaseAdmin
from crm import  models

class CustomerAdmin(BaseAdmin):
    list_display = ('id','name','qq','consultant','source','consult_content','status','data')
    list_filter = ('source','status','consultant')
    search_fields = ('qq','name')
    list_editable = ('status',)

site.register(models.Customer,CustomerAdmin)
site.register(models.FollowUpRecord)
site.register(models.Enrollment)
from django.db import models
from django.contrib.auth.models import  User
# Create your models here.

class Customer(models.Model):
    '''潜在客户信息'''
    name = models.CharField(max_length=32,blank=True,null=True)
    qq = models.CharField(max_length=64,unique=True)
    wechat = models.CharField(max_length=64,blank=True,null=True)
    age = models.PositiveIntegerField(blank=True,null=True)
    gender = models.PositiveIntegerField(choices=((0,'Female'),(1,'Male')),blank=True,null=True)
    phone = models.PositiveIntegerField(blank=True,null=True)  #正整数
    source_choices = (
        (0,'Baidu商桥'),
        (1,'51CTO'),
        (2,'QQ群'),
        (3,'知乎'),
        (4,'SOGO'),
        (5,'转介绍'),
        (6,'其他'),
    )
    source = models.SmallIntegerField(choices=source_choices) # get_source_display 显示具体内容
    referral_from = models.ForeignKey('Customer',related_name='my_referrals',blank=True,null=True) # 自关联,字段可写slef,必须写related_name
    consult_courses = models.ManyToManyField(to='Course')
    status_choices = (
        (0,'已报名'),(1,'未报名'),(2,'已退学'),(3,'其他')
    )
    status = models.SmallIntegerField(choices=status_choices)
    consultant = models.ForeignKey(to="UserProfile",verbose_name='课程顾问')
    consult_content = models.TextField(max_length=1024)
    data = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = '客户表'

    # def __str__(self):
    #     return self.name



class Enrollment(models.Model):
    '''注册用户'''
    customer = models.ForeignKey('Customer')
    class_grade = models.ForeignKey('ClassList')
    enrollment_date = models.DateField()

    class Meta:
        unique_together = ('customer', 'class_grade')   #联合唯一
        verbose_name_plural = '注册用户表'

    # def __str__(self):
    #     return '%s'%self.customer


class FollowUpRecord(models.Model):
    '''销售跟进记录'''
    customer = models.ForeignKey('Customer')
    content = models.TextField(max_length=1024)
    status_choices = (
        (0,'绝无报名计划'),
        (1,'一个月内报名'),
        (2,'两周内报名'),
        (3,'已报其他机构'),
    )
    status = models.SmallIntegerField(choices=status_choices)
    consultant = models.ForeignKey(to="UserProfile", verbose_name='课程顾问')
    data = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = '跟进记录表'

    # def __str__(self):
    #     return self.customer


class Course(models.Model):
    '''课程Python,Go'''
    name = models.CharField(unique=True,max_length=32)
    price = models.PositiveIntegerField(default=19800)
    outline = models.TextField(max_length=1024)

    class Meta:
        verbose_name_plural = '课程表'

    def __str__(self):
        return self.name

class ClassList(models.Model):
    '''班级s14,g1'''
    course = models.ForeignKey("Course")
    semester = models.PositiveIntegerField(verbose_name='学期')
    class_type_choices = ((0,'脱产'),(1,'周末'),(2,'网络'))
    branch = models.ForeignKey("Branch")
    class_type = models.PositiveIntegerField(choices=class_type_choices)
    teachers = models.ManyToManyField(to='UserProfile')
    start_date = models.DateField()
    end_date = models.DateField()

    class Meta:
        verbose_name_plural = '班级表'

    def __str__(self):
        return '%s'%self.course


class CourseRecord(models.Model):
    '''每节课上课记录'''
    class_grade = models.ForeignKey('ClassList')
    day_num = models.PositiveIntegerField(verbose_name='节次')
    teacher = models.ForeignKey('UserProfile')
    CourseContent = models.TextField(verbose_name="课程内容", max_length=1024)
    has_homework = models.BooleanField(default=True)        #布尔类型
    homework_title = models.CharField(max_length=128, blank=True, null=True)
    homework_requirement = models.TextField(verbose_name="作业需求", max_length=1024, blank=True, null=True)

    class Meta:
        unique_together = ("class_grade", "day_num")
        verbose_name_plural = '课节次表'

    def __str__(self):
        return " daynum:%s" % (self.day_number)


class StudyRecord(models.Model):
    '''每个学生上的每节课的成绩记录'''
    course_record = models.ForeignKey("CourseRecord")
    student = models.ForeignKey("Enrollment")
    score_choices = ((100, "A+"),
                     (90, "A"),
                     (85, "B+"),
                     (80, "B"),
                     (75, "B-"),
                     (70, "C+"),
                     (65, "C"),
                     (40, "C-"),
                     (-20, "D"),
                     (-50, "COPY"),
                     (0, "N/A"),
                     )
    score = models.SmallIntegerField(choices=score_choices)
    show_status_choices = (
        (0, "缺勤"), (1, "已签到"), (2, "迟到")
    )
    show_status = models.SmallIntegerField(choices=show_status_choices)
    grade_comment = models.TextField(max_length=1024, blank=True, null=True)

    class Meta:
        unique_together = ("course_record", "student")
        verbose_name_plural = '学员成绩记录表'

    def __str__(self):
        return "%s  daynum:%s" % (self.course_record, self.student)


class UserProfile(models.Model):
    '''员工用户表'''
    user = models.OneToOneField(User)
    name = models.CharField(max_length=32)

    roles = models.ManyToManyField("Role")

    class Meta:
        verbose_name_plural = '用户表'

    def __str__(self):
        return self.name

class Role(models.Model):
    """角色表"""
    name = models.CharField(unique=True,max_length=32)
    menus = models.ManyToManyField("Menu")

    class Meta:
        verbose_name_plural = '角色表'

    def __str__(self):
        return self.name


class Branch(models.Model):
    """分校"""
    name = models.CharField(unique=True,max_length=128)

    class Meta:
        verbose_name_plural = '分校表'

    def __str__(self):
        return self.name


class Menu(models.Model):
    """动态菜单"""
    name = models.CharField(unique=True,max_length=32)
    url_type = models.SmallIntegerField(choices=((0,'relative_name'),(1,'absolute_url')))
    url_name = models.CharField(unique=True,max_length=128)

    class Meta:
        verbose_name_plural = '菜单表'

    def __str__(self):
        return self.name
model.py

 

6、kingadmin目录下创建templates/kingadmin页面目录

设置settings文件:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'),
                 os.path.join(BASE_DIR, 'kingadmin','templates'),]  #加入环境变量
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
<!DOCTYPE html>
<!-- saved from url=(0041)http://v3.bootcss.com/examples/dashboard/ -->
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="http://v3.bootcss.com/favicon.ico">

    <title>Dashboard Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">

    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <link href="/static/css/e10-viewport-bug-workaround.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="/static/css/dashboard.css" rel="stylesheet">

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="/static/js/ie-emulation-modes-warning.js"></script>

    {% block header-recources %}{% endblock %}

  </head>

  <body>
    {% block body %}body ....{% endblock %}

    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
    <script src="/static/js/holder.min.js"></script>

    <script>

        $(document).ready(function () {

            $(".nav-sidebar a[href='{{ request.path }}']").parent().addClass("active");

        });//end doc ready


    </script>
</body></html>
base.html
{% extends "kingadmin/base.html" %}

{% block body %}

    {% block nav-bar %}
    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="http://v3.bootcss.com/examples/dashboard/#">Kinadmin</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav navbar-right">
            <li><a href="http://v3.bootcss.com/examples/dashboard/#">Dashboard</a></li>
            <li><a href="http://v3.bootcss.com/examples/dashboard/#">Settings</a></li>
            <li><a href="http://v3.bootcss.com/examples/dashboard/#">Profile</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.user.userprofile.name }}<span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="/logout/">logout</a></li>
                <li><a href="#">Another action</a></li>
                <li><a href="#">Something else here</a></li>

              </ul>
            </li>




          </ul>
          <form class="navbar-form navbar-right">
            <input type="text" class="form-control" placeholder="Search...">
          </form>
        </div>
      </div>
    </nav>
    {% endblock %}



    <div class="container-fluid">
      <div class="row">
         {% block side-bar %}
        <div class="col-sm-3 col-md-2 sidebar">
          <ul class="nav nav-sidebar">
            {% block side-bar-menus %}
             {% for role in request.user.userprofile.roles.all %}
{#            <li class="active"><a href="http://v3.bootcss.com/examples/dashboard/#">Overview <span class="sr-only">(current)</span></a></li>#}
                <hr>
                {% for menu in role.menus.all %}
                  <li>
                      <a href="{% if menu.url_type == 0 %}{% url menu.url_name %}{% else %} {{ menu.url_name }}{% endif %}"  >
                      {{ menu.name }}
                      </a>
                  </li>
                {% endfor %}
            {% endfor %}

            {% endblock %}
           </ul>

        </div>
         {% endblock %}
        {% block right-container %}
        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
          {% block right-container-content %}
          <h1 class="page-header">Dashboard</h1>
                {{ request.user.userprofile.name }}
          <div class="row placeholders">
            <div class="col-xs-6 col-sm-3 placeholder">
              <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200" height="200" class="img-responsive" alt="Generic placeholder thumbnail">
              <h4>Label</h4>
              <span class="text-muted">Something else</span>
            </div>
            <div class="col-xs-6 col-sm-3 placeholder">
              <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200" height="200" class="img-responsive" alt="Generic placeholder thumbnail">
              <h4>Label</h4>
              <span class="text-muted">Something else</span>
            </div>
            <div class="col-xs-6 col-sm-3 placeholder">
              <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200" height="200" class="img-responsive" alt="Generic placeholder thumbnail">
              <h4>Label</h4>
              <span class="text-muted">Something else</span>
            </div>
            <div class="col-xs-6 col-sm-3 placeholder">
              <img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200" height="200" class="img-responsive" alt="Generic placeholder thumbnail">
              <h4>Label</h4>
              <span class="text-muted">Something else</span>
            </div>
          </div>

          <h2 class="sub-header">Section title</h2>
          {% endblock %}
        </div>
        {% endblock  %}
      </div>
    </div>

{% endblock %}
index.html

首页app_index.html:

{% extends 'kingadmin/index.html' %}
{% load kingadmin_tags %}

{% block right-container-content %}
    {% for app,app_tables in site.registered_sites.items %}
    <table class="table table-hover">
      <thead>
        <tr>
            <h3>{{ app }}</h3>
        </tr>
      </thead>
      <tbody>
        {% for model_name,admin_class in app_tables.items %}
        <tr>
          <th ><a href="/kingadmin/{% get_app_name admin_class.model %}/{% get_model_name  admin_class.model %}/">{% get_model_verbose_name admin_class.model %}</a>  </th>
        </tr>
        {% endfor %}
      </tbody>
    </table>
    {% endfor %}
{% endblock %}

详情表table_data_list.html:  

{% extends 'kingadmin/index.html' %}
{% load kingadmin_tags %}

{% block right-container-content %}

    <h4>{% get_model_verbose_name admin_obj.model  %}</h4>

    <table class="table table-hover">
      <thead>
        <tr>
            {% for column in admin_obj.list_display %}
                <th>{{ column }}</th>
            {% endfor %}
        </tr>
      </thead>
      <tbody>

        {% for obj in admin_obj.querysets %}
            <tr>
                {% build_table_row  admin_obj obj  %}

            </tr>
        {% endfor %}
      </tbody>
    </table>

{% endblock %}

 

7、创建templatetags目录下kingadmin_tags.py文件 自定义模板方法

from django import template
from django.utils.safestring import mark_safe

register = template.Library()

@register.simple_tag
def get_model_verbose_name(model_obj):

    model_name = model_obj._meta.verbose_name_plural

    if not model_name:
        model_name = model_obj._meta.model_name

    return model_name

@register.simple_tag
def get_model_name(model_obj):

    return model_obj._meta.model_name

@register.simple_tag
def get_app_name(model_obj):

    return model_obj._meta.app_label

@register.simple_tag
def build_table_row(admin_obj,obj):

    row_ele = ""

    for column in admin_obj.list_display:
        column_obj = obj._meta.get_field(column)
        if column_obj.choices:
            get_column_data = getattr(obj,"get_%s_display" % column)
            column_data = get_column_data()
        else:
            column_data = getattr(obj, column)

        td_ele = '''<td>%s</td>''' % column_data
        row_ele += td_ele

    return mark_safe(row_ele)

 

初步效果:  

 

 

模仿Admin做检索

1、修改views.py

from django.shortcuts import render

from kingadmin import app_config  # 加载app_config
from kingadmin.base_admin import site

def app_index(request):

    print(id(site),site.registered_sites)
    return render(request, 'kingadmin/app_index.html',{'site':site})

def filter_querysets(request,model):    #过滤
    condtions = {}
    for k,v in request.GET.items():
        if v :
            condtions[k] = v
    querysets =model.objects.filter(**condtions)

    return querysets,condtions

def table_data_list(request,app_name,model_name):   #重新改造函数,加上过滤条件
    # print(request.GET)
    admin_obj = site.registered_sites[app_name][model_name]
    model = admin_obj.model
    admin_obj.querysets,admin_obj.condtions =   filter_querysets(request,model)

    print(admin_obj.condtions)
    return render(request,"kingadmin/table_data_list.html",locals())    # locals 传入局部变量

 

2、新增自定义simple_tag

@register.simple_tag
def get_filter_field(filter_column,admin_obj):
    field_obj = admin_obj.model._meta.get_field(filter_column)

    selected  = None
    condtions = admin_obj.condtions
    if filter_column in condtions:
        selected = condtions[filter_column]

    select_ele = """<select name= "%s">""" % filter_column
    for choice in field_obj.get_choices():
        if selected and selected == str(choice[0]):
            option_ele = """<option value="%s" selected> %s </option> """ % choice
        else:
            option_ele = """<option value="%s"> %s </option> """%choice
        select_ele +=option_ele
    select_ele += """</select>"""

    return mark_safe(select_ele)

 

3、修改table_data_list.html

{% extends 'kingadmin/index.html' %}
{% load kingadmin_tags %}

{% block right-container-content %}

    <h4>{% get_model_verbose_name admin_obj.model  %}</h4>
{#    {{ admin_obj.list_filter }}#}
    {% if admin_obj.list_filter %}
        <div class="row">
            <form>
                {% for filter_column in admin_obj.list_filter %}
                    <div class="col-lg-3">
                    {{ filter_column }}:{% get_filter_field filter_column admin_obj %}
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-success" value="过滤">
            </form>
        </div>
    {% endif %}
    <table class="table table-hover">
      <thead>
        <tr>
            {% for column in admin_obj.list_display %}
                <th>{{ column }}</th>
            {% endfor %}
        </tr>
      </thead>
      <tbody>

        {% for obj in admin_obj.querysets %}
            <tr>
                {% build_table_row  admin_obj obj  %}

            </tr>
        {% endfor %}
      </tbody>
    </table>


{% endblock %}
table_data_list.html

 

显示效果:

 

 

利用Django自带分页

1、Paginantor官方源码示例-》跳转

>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)

>>> p.count
4
>>> p.num_pages
2
>>> type(p.page_range)  # `<type 'rangeiterator'>` in Python 2.
<class 'range_iterator'>
>>> p.page_range
range(1, 3)

>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']

>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index() # The 1-based index of the first item on this page
3
>>> page2.end_index() # The 1-based index of the last item on this page
4

>>> p.page(0)
Traceback (most recent call last):
...
EmptyPage: That page number is less than 1
>>> p.page(3)
Traceback (most recent call last):
...
EmptyPage: That page contains no results
基本使用方法
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

def listing(request):
    contact_list = Contacts.objects.all()
    paginator = Paginator(contact_list, 25) # Show 25 contacts per page

    page = request.GET.get('page')
    try:
        contacts = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        contacts = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        contacts = paginator.page(paginator.num_pages)

    return render(request, 'list.html', {'contacts': contacts})
后端处理
{% for contact in contacts %}
    {# Each "contact" is a Contact model object. #}
    {{ contact.full_name|upper }}<br />
    ...
{% endfor %}

<div class="pagination">
    <span class="step-links">
        {% if contacts.has_previous %}
            <a href="?page={{ contacts.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
        </span>

        {% if contacts.has_next %}
            <a href="?page={{ contacts.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>
前端展示

 

2、添加分页全局变量kingadmin.py

class CustomerAdmin(BaseAdmin):
    list_display = ('id','name','qq','consultant','source','consult_content','status','data')
    list_filter = ('source','status','consultant')
    search_fields = ('qq','name')
    list_editable = ('status',)
    list_per_page = 2

 

3、处理函数views.py

def filter_querysets(request,model):    #过滤
    condtions = {}
    for k,v in request.GET.items():
        if v and k !='page':
            condtions[k] = v
    querysets =model.objects.filter(**condtions)

    return querysets,condtions

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

def listing(request,admin_obj):

    paginator = Paginator(admin_obj.querysets, admin_obj.list_per_page) # Show 25 contacts per page
    page = request.GET.get('page')
    try:
        contacts = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        contacts = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        contacts = paginator.page(paginator.num_pages)

    return contacts

def table_data_list(request,app_name,model_name):   #重新改造函数,加上过滤条件
    # print(request.GET)
    admin_obj = site.registered_sites[app_name][model_name]
    model = admin_obj.model
    admin_obj.querysets,admin_obj.condtions =   filter_querysets(request,model)
    print('before',admin_obj.querysets)
    admin_obj.querysets = listing(request,admin_obj)
    print('after',admin_obj.querysets)

    return render(request,"kingadmin/table_data_list.html",locals())    # locals 传入局部变量

  

4、kingadmin_tags.py

@register.simple_tag()
def generate_filter_url(admin_obj):
    url = ""
    for k,v in admin_obj.condtions.items():
        url += "&%s=%s"%(k,v)

    return url

 

5、table_data_list.html

tfoot>
        <nav aria-label="...">
          <ul class="pagination">
            <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">«</span></a></li>
              {% for page in admin_obj.querysets.paginator.page_range %}
                  {% if page ==  admin_obj.querysets.number  %}
                        <li class="active">
                  {% else %}
                        <li >
                  {% endif %}
                            <a href="?page={{ page }}{% generate_filter_url admin_obj %}">{{ page }} <span class="sr-only">(current)</span></a>
                        </li>
              {% endfor %}
          </ul>
        </nav>
    </tfoot>

 

显示效果:

 

 

过滤、排序、分页组合

1、在原views.py上新增:

# 添加排序
def get_orderby(request,querysets):         #排序
    orderby_fieid = request.GET.get('o')
    if orderby_fieid:
        querys_res = querysets.order_by(orderby_fieid)
    else:
        querys_res = querysets

    return querys_res

# 过滤修改
def filter_querysets(request,model):    #过滤
    condtions = {}
    except_list = ["page",'o']
    for k,v in request.GET.items():
        if k in except_list:continue
        if v:
            condtions[k] = v
    querysets =model.objects.filter(**condtions)

    return querysets,condtions

# 分页添加
def listing(request,admin_obj):
    querys_res = get_orderby(request, admin_obj.querysets)

  

2、自定义simple_tag

@register.simple_tag()
def generate_filter_url(admin_obj,request=None):
    url = ""
    if request:
        if request.GET.get('o'):
            url += "&o=%s" % request.GET.get('o')
    for k,v in admin_obj.condtions.items():
        url += "&%s=%s"%(k,v)

    return url

@register.simple_tag
def get_orderby_key(request,column):
    current_order_by_key = request.GET.get('o')
    if current_order_by_key:
        if current_order_by_key == column:
           if column.startswith('-'):
               column.strip("-")
           else:
               column = "-%s"%column
        return column
    else:
        return column

@register.simple_tag
def display_order_by_icon(request,column):
    current_order_by_key = request.GET.get('o')
    if current_order_by_key:
        if current_order_by_key.strip('-') == column:
            if current_order_by_key.startswith('-'):
                icons = '''<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>'''
            else:
                icons = '''<span class="glyphicon glyphicon-arrow-down" aria-hidden="true"></span>'''

            return mark_safe(icons)

    return ' '

  

3、html前端页面

// 过滤时添加排序
<form>
    {% for filter_column in admin_obj.list_filter %}
        <div class="col-lg-3">
        {{ filter_column }}:{% get_filter_field filter_column admin_obj %}
        </div>
    {% endfor %}
    <input type="submit" class="btn btn-success" value="过滤">
    <input type="hidden" name="o" value="{{ request.GET.o }}"> //过滤时加上排序
</form>

// 排序时加上过滤
{% for column in admin_obj.list_display %}
    <th>
        // 排序时加上过滤
        <a href="?o={% get_orderby_key request column %}{% generate_filter_url admin_obj %}"> {{ column }}</a>
        {% display_order_by_icon request column %}
    </th>
{% endfor %}

// 分页时加上过滤和排序
{% for page in admin_obj.querysets.paginator.page_range %}
  {% if page ==  admin_obj.querysets.number  %}
        <li class="active">
  {% else %}
        <li >
  {% endif %}
            // 分页时加上排序和过滤
            <a href="?page={{ page }}{% generate_filter_url admin_obj request%}">{{ page }} <span class="sr-only">(current)</span></a>
        </li>
{% endfor %}

点击过滤或排序时页面恢复到第一页

 

搜索

1、views.py处理文件

# 添加搜索函数 注意顺序

def get_search(request,admin_obj):
    search_content = request.GET.get('q')
    condition = Q()
    condition.connector = 'OR'
    if search_content:
        for column in  admin_obj.search_fields:
            condition.children.append(("%s__contains"%column,search_content))

        querysets = admin_obj.querysets.filter(condition)
    else:
        querysets = admin_obj.querysets
    return querysets

def table_data_list(request,app_name,model_name):   #重新改造函数,加上过滤条件

    admin_obj = site.registered_sites[app_name][model_name]
    model = admin_obj.model

    admin_obj.querysets,admin_obj.condtions =   filter_querysets(request,model) #加过滤条件

    admin_obj.querysets = get_search(request,admin_obj) # 搜索

    admin_obj.querysets = listing(request,admin_obj)        #分页

    return render(request,"kingadmin/table_data_list.html",locals())    # locals 传入局部变量

 

2、自定义simple_tag

 

@register.simple_tag()
def generate_filter_search(request):
    search_content = request.GET.get('q')
    url = ""
    if search_content:
        url = "&q=%s"%search_content

    return url

 

3、前端html页面

 过滤时的from
<form>
    {% for filter_column in admin_obj.list_filter %}
        <div class="col-lg-3">
        {{ filter_column }}:{% get_filter_field filter_column admin_obj %}
        </div>
    {% endfor %}
    <input type="submit" class="btn btn-success" value="过滤">
{#            //过滤时加上排序#}
    <input type="hidden" name="o" value="{{ request.GET.o }}">
    <br/>
    <input type="text" name="q" value="{{ request.GET.q }}">
</form>

排序
{% for column in admin_obj.list_display %}
    <th>
{#                    // 排序时加上过滤#}
        <a href="?o={% get_orderby_key request column %}{% generate_filter_search request %}{% generate_filter_url admin_obj %}"> {{ column }}</a>
        {% display_order_by_icon request column %}
    </th>
{% endfor %}

分页
<a href="?page={{ page }}{% generate_filter_search request %}{% generate_filter_url admin_obj request%}">{{ page }} <span class="sr-only">(current)</span></a>
                      

 

页面显示:

 

 

数据修改

1、kingadmin下创建forms.py

from django import forms

def CreateModelForm(admin_obj):     # 动态生成modelfrom
    class Meta:
        model = admin_obj.model
        fields = "__all__"

    dynamic_model_form = type("DynamicModelForm",(forms.ModelForm,),{'Meta':Meta})

    return dynamic_model_form

 

2、views.py处理函数

from kingadmin import forms
def table_change(request,app_name,model_name,id):
    admin_obj = site.registered_sites[app_name][model_name]
    obj = admin_obj.model.objects.filter(id=id).first()

    model_form = forms.CreateModelForm(admin_obj)
    obj_form = model_form(instance=obj)

    return render(request,'kingadmin/table_change.html',locals())

 

3、html文件

{% extends "kingadmin/index.html" %}


{% load kingadmin_tags %}

{% block right-container-content %}
    {{ obj_form }}
{% endblock %}
table_change.html

 

显示视图:

 

 

修改完善

1、html文件

{% extends "kingadmin/index.html" %}


{% load kingadmin_tags %}

{% block right-container-content %}
    <div class="row" style="margin-bottom: 20px" >
        <ol class="breadcrumb">
          <li><a href="/kingadmin/">Home</a></li>
          <li><a href="/kingadmin/{% get_app_name admin_obj.model %}/">{% get_app_name admin_obj.model %}</a></li>
          <li ><a href="/kingadmin/{% get_app_name admin_obj.model %}/{% get_model_name admin_obj.model %}/">{% get_model_verbose_name admin_obj.model%} </a></li>
          <li class="active">{{ obj_form.instance }}</li>
        </ol>

        <h4>Change {% get_model_verbose_name admin_obj.model  %}</h4>
    </div>

    <form class="form-horizontal" method="post" onsubmit="return BeforeFormSubmit(this);">{% csrf_token %}
       {% for field in obj_form %}
                  <div class="form-group">
                    <label  class="col-sm-2 " style="font-weight: normal">
                        {% if field.field.required %}
                            <b>{{ field.label }}</b>
                        {% else %}
                            {{ field.label }}
                        {% endif %}
                    </label>
                       <div class="col-sm-10">
                        <span style="color: red;">{{ field.errors }}</span>
                            {{ field }}
                    </div>
                  </div>
       {% endfor %}
        <input type="submit" value="Save" class="pull-right btn btn-info" >
    </form>
{% endblock %}
table_data_list.html

 

2、froms.py

from django import forms

def CreateModelForm(admin_obj):     # 动态生成modelfrom
    class Meta:
        model = admin_obj.model
        fields = "__all__"

    def __new__(cls, *args, **kwargs):
        # print("base fields",cls.base_fields)
        for field_name, field_obj in cls.base_fields.items():
            print(field_name,dir(field_obj))
            field_obj.widget.attrs['class'] = 'form-control'
            # field_obj.widget.attrs['maxlength'] = getattr(field_obj,'max_length' ) if hasattr(field_obj,'max_length') \
            #     else ""
            # if field_name in admin_obj.readonly_fields:
            #     field_obj.widget.attrs['disabled'] = True

        return forms.ModelForm.__new__(cls)

    dynamic_model_form = type("DynamicModelForm",(forms.ModelForm,),{'Meta':Meta})
    setattr(dynamic_model_form,"__new__",__new__)
    return dynamic_model_form

 

3、views.py处理文件

from kingadmin import forms
def table_change(request,app_name,model_name,id):
    admin_obj = site.registered_sites[app_name][model_name]
    obj = admin_obj.model.objects.filter(id=id).first()
    model_form = forms.CreateModelForm(admin_obj)

    if request.method == "GET":
        obj_form = model_form(instance=obj)
    elif request.method == "POST":
        obj_form = model_form(instance=obj, data=request.POST)
        if obj_form.is_valid():
            obj_form.save()

    return render(request, "kingadmin/table_change.html", locals())

  

 

表单添加

1、html文件

{% extends "kingadmin/index.html" %}


{% load kingadmin_tags %}

{% block right-container-content %}
    <div class="row" style="margin-bottom: 20px" >
        <ol class="breadcrumb">
          <li><a href="/kingadmin/">Home</a></li>
          <li><a href="/kingadmin/{% get_app_name admin_obj.model %}/">{% get_app_name admin_obj.model %}</a></li>
          <li ><a href="/kingadmin/{% get_app_name admin_obj.model %}/{% get_model_name admin_obj.model %}/">{% get_model_verbose_name admin_obj.model%} </a></li>
          <li class="active">Add {% get_model_name admin_obj.model %}</li>
        </ol>

        <h4>Change {% get_model_verbose_name admin_obj.model  %}</h4>
    </div>

    <form class="form-horizontal" method="post" onsubmit="return BeforeFormSubmit(this);">{% csrf_token %}
       {% for field in obj_form %}
                  <div class="form-group">
                    <label  class="col-sm-2 " style="font-weight: normal">
                        {% if field.field.required %}
                            <b>{{ field.label }}</b>
                        {% else %}
                            {{ field.label }}
                        {% endif %}
                    </label>
                       <div class="col-sm-10">
                        <span style="color: red;">{{ field.errors }}</span>
                            {{ field }}
                    </div>
                  </div>
       {% endfor %}
        <input type="submit" value="Save" class="pull-right btn btn-info" >
    </form>
{% endblock %}
table_add.html

 

2、froms.py文件

from django import forms

def CreateModelForm(admin_obj):     # 动态生成modelfrom
    class Meta:
        model = admin_obj.model
        fields = "__all__"

    def __new__(cls, *args, **kwargs):
        # print("base fields",cls.base_fields)
        for field_name, field_obj in cls.base_fields.items():
            # print(field_name,dir(field_obj))
            field_obj.widget.attrs['class'] = 'form-control'
            # field_obj.widget.attrs['maxlength'] = getattr(field_obj,'max_length' ) if hasattr(field_obj,'max_length') \
            #     else ""
            if field_name in admin_obj.readonly_fields:
                field_obj.widget.attrs['disabled'] = True

        return forms.ModelForm.__new__(cls)

    dynamic_model_form = type("DynamicModelForm",(forms.ModelForm,),{'Meta':Meta})
    setattr(dynamic_model_form,"__new__",__new__)
    return dynamic_model_form

 

3、views.py处理文件

def table_add(request,app_name,model_name):
    admin_obj = site.registered_sites[app_name][model_name]
    model_form = forms.CreateModelForm(admin_obj)
    if request.method == "GET":
        obj_form = model_form()

    if request.method == "POST":
        obj_form = model_form(data=request.POST)
        if obj_form.is_valid():
            obj_form.save()
        if not obj_form.errors:
            return redirect('/kingadmin/%s/%s'%(app_name,model_name))

    return render(request, "kingadmin/table_add.html", locals())

  

 

增加readonly_fields字段

1、forms.py文件修改

from django import forms

def CreateModelForm(admin_obj):     # 动态生成modelfrom
    class Meta:
        model = admin_obj.model
        fields = "__all__"

    def __new__(cls, *args, **kwargs):
        # print("base fields",cls.base_fields)
        for field_name, field_obj in cls.base_fields.items():
            # print(field_name,dir(field_obj))
            field_obj.widget.attrs['class'] = 'form-control'
            # field_obj.widget.attrs['maxlength'] = getattr(field_obj,'max_length' ) if hasattr(field_obj,'max_length') \
            #     else ""
            if field_name in admin_obj.readonly_fields:
                field_obj.widget.attrs['disabled'] = True

        return forms.ModelForm.__new__(cls)

    def default_clean(self):
        # print("default clean:",self)
        for field in admin_obj.readonly_fields:
            print("readonly", field, self.instance)
            field_val_from_db = getattr(self.instance, field)
            field_val = self.cleaned_data.get(field)
            if field_val_from_db == field_val:
                print("field not change ")
            else:  # 被篡改了
                self.add_error(field, ' "%s" is a readonly field ,value should be "%s" ' % (field, field_val_from_db))

        print("cleaned data:", self.cleaned_data)

    dynamic_model_form = type("DynamicModelForm",(forms.ModelForm,),{'Meta':Meta})
    setattr(dynamic_model_form,"__new__",__new__)
    setattr(dynamic_model_form,"clean",default_clean)
    return dynamic_model_form

 

 

添加action动作

1、kingadmin.py

class CustomerAdmin(BaseAdmin):
    list_display = ('id','name','qq','consultant','source','consult_content','status','data')
    list_filter = ('source','status','consultant')
    search_fields = ('qq','name')
    list_editable = ('status',)
    readonly_fields = ('name',)
    list_per_page = 5
    actions = ["change_status", ]

    def change_status(self, request, querysets):
        print("changeing status", querysets)
        querysets.update(status=1)

    change_status.short_description = "改变报名状态"

class EnrollmentAdmin(BaseAdmin):
    list_display = ('customer','class_grade','enrollment_date')

site.register(models.Customer,CustomerAdmin)
site.register(models.FollowUpRecord)
site.register(models.Enrollment,EnrollmentAdmin)

 

2、views.py处理函数

def table_data_list(request,app_name,model_name):   #重新改造函数,加上过滤条件
    admin_obj = site.registered_sites[app_name][model_name]

    if request.method == "POST":

        action = request.POST.get("action_select")
        selected_ids = request.POST.get("selected_ids")
        selected_ids = json.loads(selected_ids)
        print("action:",selected_ids,action)
        selected_objs = admin_obj.model.objects.filter(id__in=selected_ids)

        action_func = getattr(admin_obj,action)
        action_func(request,selected_objs)

    model = admin_obj.model

    admin_obj.querysets,admin_obj.condtions =   filter_querysets(request,model) #加过滤条件

    admin_obj.querysets = get_search(request,admin_obj) # 搜索

    admin_obj.querysets = listing(request,admin_obj)        #分页

    return render(request,"kingadmin/table_data_list.html",locals())    # locals 传入局部变量

  

3、table_data_list.html

{% extends 'kingadmin/index.html' %}
{% load kingadmin_tags %}

{% block right-container-content %}
     <ol class="breadcrumb">
          <li><a href="/kingadmin/">Home</a></li>
          <li><a href="/kingadmin/{% get_app_name admin_obj.model %}/">{% get_app_name admin_obj.model %}</a></li>
          <li class="active">{% get_model_verbose_name admin_obj.model%} </li>
          <li class="pull-right">
              <a href="add/"><input type="button" class="btn btn-sm btn-success" value="+Add"></a>
          </li>
        </ol>

    <h4>{% get_model_verbose_name admin_obj.model  %}</h4>
{#    {{ admin_obj.list_filter }}#}

    {% if admin_obj.list_filter %}
        <div class="row">
            <form>
                {% for filter_column in admin_obj.list_filter %}
                    <div class="col-lg-3">
                    {{ filter_column }}:{% get_filter_field filter_column admin_obj %}
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-success" value="过滤">
            {#            //过滤时加上排序#}
                <input type="hidden" name="o" value="{{ request.GET.o }}">
                <br/>
                <input type="text" name="q" value="{{ request.GET.q }}">
            </form>


        <form method="post" onsubmit="return ActionValidation(this)">{% csrf_token %}
            <select name="action_select">

                {% get_admin_actions admin_obj %}
            </select>
            <input type="submit" value="执行">
        </form>

        </div>
    {% endif %}

    <table class="table table-hover">
      <thead>
        <tr>
         <th><input type="checkbox" onclick="SelectAll(this)" /></th>
            {% for column in admin_obj.list_display %}
                <th>
            {#                    // 排序时加上过滤#}
                    <a href="?o={% get_orderby_key request column %}{% generate_filter_search request %}{% generate_filter_url admin_obj %}"> {{ column }}</a>
                    {% display_order_by_icon request column %}
                </th>
            {% endfor %}
        </tr>
      </thead>

      <tbody>

        {% for obj in admin_obj.querysets %}
            <tr>
                <td>
                    <input tag="obj_checkbox" type="checkbox" value="{{ obj.id }}" />
                </td>
                {% build_table_row  admin_obj obj  %}
            </tr>
        {% endfor %}
      </tbody>
    </table>
    <tfoot>
        <nav aria-label="...">
          <ul class="pagination">
            <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
              {% for page in admin_obj.querysets.paginator.page_range %}
                  {% if page ==  admin_obj.querysets.number  %}
                        <li class="active">
                  {% else %}
                        <li >
                  {% endif %}
{#                            // 分页时加上排序和过滤#}
                            <a href="?page={{ page }}{% generate_filter_search request %}{% generate_filter_url admin_obj request%}">{{ page }} <span class="sr-only">(current)</span></a>
                        </li>
              {% endfor %}
          </ul>
        </nav>
    </tfoot>

    <script >

        function SelectAll(ele) {
            if ($(ele).prop("checked")){
                $("input[tag='obj_checkbox']").prop("checked",true)
            }else {
                $("input[tag='obj_checkbox']").prop("checked",false)
            }

        };//end SelectAll

        function ActionValidation(form_ele) {
            if ($("select[name='action_select']").val() == "-1"){
                alert("must select action before submit!");
                return false;
            }

            var selected_objs = [];
            $("input[tag='obj_checkbox']").each(function () {
                if ($(this).prop("checked")){
                    selected_objs.push($(this).val());
                }
            });//end each

            console.log(selected_objs)
            if ( selected_objs.length ==0){
                alert("must select at least one object to run the action!");
                return false;
            }

            var selected_objs_ele = "<input name='selected_ids' type='hidden' value=" + JSON.stringify(selected_objs) + " >" ;
            $(form_ele).append(selected_objs_ele);

            return true;

        }

    </script>
{% endblock %}
html

 

4、simple_tag

@register.simple_tag
def get_admin_actions(admin_obj):

    options = "<option class='form-control' value='-1'>-------</option>"
    actions = admin_obj.default_actions + admin_obj.actions

    for action in actions:
        action_func = getattr(admin_obj,action)
        if hasattr(action_func,"short_description"):
            action_name = action_func.short_description
        else:
            action_name = action
        options += """<option value="{action_func_name}">{action_name}</option> """.format(action_func_name=action,
                                                                                           action_name=action_name)

    return mark_safe(options)

 

  

 

posted @ 2017-03-27 18:15  Assassinの  阅读(2902)  评论(0编辑  收藏  举报