实现CRM五大参数功能

1. 模型表字段的数据

1.1 模型表对应的应用名与模型表对应 的字符串名的获取

from app01 import models

models.Book._meta.app_label # 获取模型表所对应的的应用名
models.Book._meta.model_name # 获取模型表对应的字符串名

models.Book._meta.get_field('title')   # 获取字段对象
# <django.db.models.fields.CharField: title> 

1.2 字段名称的获取

# verbose_name 可以给字段添加名称
title = models.CharField(max_length=32, verbose_name='书名')
price = models.DecimalField(max_digits=5, decimal_places=2)

# 获取字段对应的verbose_name,
models.Book._meta.get_field('title').verbose_name # '书名'


# 如果字段没有指定verbose_name属性时, 则使用字段的字符串名
models.Book._meta.get_field('title').verbose_name # price

 

2. 新建一个stark组件

2.1 stark 的启动 django一启动就要执行每一个应用下的stark.py文件

2.1.1 配置文件中注册

INSTALLED_APPS = [
    'stark.apps.StarkConfig',
]

2.1.2 在StarkConfig类中,固定写ready方法

StarkConfig位于stark文件夹下的apps模块内

from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules

class StarkConfig(AppConfig):
    name = 'stark'
    
    def ready(self):
        # 项目一启动,就会自动查找每一个应用下的stark.py文件
        return autodiscover_modules('stark')

2.2 模型表的注册

# django admin中模型表注册位置为 应用下的 admin.py文件中
# 自定义的stark,需要将注册的地址改为应用下的 stark.py文件中

 

3. 路由反向解析

3.1 实现一级路由

# 实现一级路由,之所以不在这里实现二级路由分发,是因为这里的self是 site对象,
    # 当book,publish ... 传过来的时候,self知道依旧是site,无法对表的种类进行区分
    def get_urls(self):
        tmp = []
        for model_class, config_obj in self._registry.items():
            # config_obj为配置类对象,跟模型表一一对应,因此,我们在配置类中进行二级路由分发
            app_label = model_class._meta.app_label
            model_name = model_class._meta.model_name
            # tmp.append(url(r'^%s/%s' %(app_label, model_name), self.test)) # 实现一级路由
            tmp.append(url(r'^%s/%s/' % (app_label, model_name), config_obj.urls))  # 实现一级路由
        return tmp

3.2 实现二级路由

class StarkSite(object):
    @property
    def urls(self):
        tmp = [
            url(r'^$', self.list_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'list')),
            url(r'^add/', self.add_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'add')),
            url(r'^edit/(\d+)/', self.edit_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'edit')),
            url(r'^delete/(\d+)/', self.delete_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'delete')),
        ]

        return tmp, None, None

3.3 二级路由与方向解析

class ModelStark(object):

    def __init__(self, model):
        self.model = model
        self.app_label = self.model._meta.app_label    # 模型表所在的应用名
        self.model_name = self.model._meta.model_name  # 模型表对应的字符串名

    def check_col(self, is_header=False, obj=None):
        if is_header:
            return '选择'
        return mark_safe('<input type="checkbox"/>')

    def get_reverse_url(self, type, obj=None):
        if obj:
            _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type), args=(obj.pk,))
        else:
            _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type))
        return _url

    def edit_col(self, is_header=False, obj=None):
        if is_header:
            return '编辑'
        _url = self.get_reverse_url('edit', obj)
        return mark_safe('<a href="%s">编辑</a>' % _url)

    def delete_col(self, is_header=False, obj=None):
        if is_header:
            return '删除'
        _url = _url = self.get_reverse_url('delete', obj)
        return mark_safe('<a href="%s">删除</a>' % _url)

 

4. 核心代码(核心笔记都在代码注释,其他的可以不用看)

4.1 目录结构

 

 4.2 app01包下的核心代码

from django.db import models


# Create your models here.

class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄')
    # 与AuthorDetail建立一对一关系
    authorDetail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    def __str__(self):
        return self.name


class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField(verbose_name='出生日期')
    telephone = models.BigIntegerField(verbose_name='手机号')
    addr = models.CharField(max_length=64, verbose_name='地址')

    def __str__(self):
        return self.addr


class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, verbose_name='出版社名称')
    city = models.CharField(max_length=32, verbose_name='城市')
    email = models.EmailField(verbose_name='邮箱')

    def __str__(self):
        return self.name


class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32, verbose_name='书名')
    publishDate = models.DateField(verbose_name='出版日期')
    price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='价格')
    # 与Publish建立一对多的关系,外键字段建立在多的一方
    publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE, verbose_name='出版社')
    # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
    authors = models.ManyToManyField(to='Author', )

    def __str__(self):
        return self.title
models.py
from app01 import models
from django.utils.safestring import mark_safe

from stark.service.stark import site, ModelStark


class BookConfig(ModelStark):

    list_display = ['title', 'price', 'publishDate', 'publish']
    list_display_links = ['title', 'price']

    from django.forms import ModelForm
    class BookModelForm(ModelForm):
        class Meta:
            model = models.Book
            fields = '__all__'
            from django.forms import widgets as wid
            widgets = {
                'title':wid.TextInput(attrs={'class': 'form-control'}),
                'price':wid.TextInput(attrs={'class': 'form-control'}),
                'publishDate':wid.DateInput(attrs={'class': 'form-control'}),
                'publish':wid.Select(attrs={'class': 'form-control'}),
                'authors':wid.SelectMultiple(attrs={'class': 'form-control'})
            }
    model_form_class = BookModelForm

    search_fields = ['title', 'price']
    def patch_init(self, request, queryset):
        queryset.update(price=666)
    patch_init.desc = '价格批量处理'

    def path_delete(self, request, queryset):
        queryset.delete()
    path_delete.desc = '批量删除'
    actions = [patch_init, path_delete]

    list_filter = ['publish', 'authors']


site.register(models.Book, BookConfig)
site.register(models.Publish)
site.register(models.Author)
site.register(models.AuthorDetail)
stark.py

4.3 stark包下的核心代码

from django.shortcuts import HttpResponse, render, reverse, redirect
from django.conf.urls import url
from django.utils.safestring import mark_safe
from stark.utils.my_page import Pagination
from django.db.models import Q

class ShowList(object):

    def __init__(self, config_obj, queryset, request):
        self.config_obj = config_obj
        self.queryset = queryset
        self.request = request
        current_page = self.request.GET.get('page', 1)
        self.page_obj = Pagination(current_page=current_page, all_count=self.queryset.count(), request=self.request)
        self.page_queryset = self.queryset[self.page_obj.start:self.page_obj.end]

    def get_header(self):
        # 表头展示
        header_list = []
        for field_or_func in self.config_obj.get_new_list_display():
            if isinstance(field_or_func, str):
                # 当用户没有指定list_display 默认展示当前表的大写字符串表名
                if field_or_func == '__str__':
                    val = self.config_obj.model._meta.model_name.upper()
                else:
                    val = self.config_obj.model._meta.get_field(field_or_func).verbose_name
            else:
                val = field_or_func(self.config_obj, is_header=True)
            header_list.append(val)
        return header_list


    def get_body(self):
        body_list = []  # [[obj1.title, obj1.price], [obj2.title, obj2.price],]
        for obj in self.page_queryset:
            tmp = []
            for field_or_func in self.config_obj.get_new_list_display():
                if isinstance(field_or_func, str):
                    val = getattr(obj, field_or_func)  # 从对象中找到某一个字段对应的值
                    if field_or_func in self.config_obj.list_display_links:
                        _url = self.config_obj.get_reverse_url('edit', obj)
                        val = mark_safe('<a href="%s">%s</a>' % (_url, val))

                else:
                    val = field_or_func(self.config_obj, obj=obj)
                tmp.append(val)
            body_list.append(tmp)
        return body_list


    def get_actions(self):
        tmp_list = []   # actions = [patch_init]
        for action in self.config_obj.actions:
            tmp_list.append({
                'name': action.__name__,
                'desc': action.desc
            })
        return tmp_list   # ['name':'', 'desc':'']

    def get_filter(self):
        tmp_dict = {}
        for field in self.config_obj.list_filter:  # ['publish', 'authors']
            tmp_list = []
            rel_model = self.config_obj.model._meta.get_field(field).rel.to
            rel_queryset = rel_model.objects.all()
            filter_value = self.request.GET.get(field) # 获得用户点了什么

            import copy
            params1 = copy.deepcopy(self.request.GET)

            if field in params1:
                params1.pop(field)
                s = mark_safe('<a href="?%s">ALL</a>'% params1.urlencode())
            else:
                s = mark_safe('<a href="">ALL</a>')
            tmp_list.append(s)


            params = copy.deepcopy(self.request.GET)
            for obj in rel_queryset:

                params[field] = obj.pk
                # url = '%s=%s' % (field, obj.pk)
                if filter_value == str(obj.pk):
                    s = mark_safe('<a href="?%s" class="active">%s</a>' % (params.urlencode(), str(obj)))
                else:
                    s = mark_safe('<a href="?%s">%s</a>' % (params.urlencode(), str(obj)))
                tmp_list.append(s)

            # tmp_list.append(rel_queryset)
            tmp_dict[field] = tmp_list # {['publish':[obj1, obj2, obj3]],'authors': [obj1, obj2, obj3]}

        return tmp_dict


class ModelStark(object):
    list_display = ['__str__', ]
    list_display_links = []
    model_form_class = None
    search_fields = []
    actions = []
    list_filter = []

    def __init__(self, model):
        self.model = model
        self.app_label = self.model._meta.app_label    # _meta.app_label 模型表所在的应用名
        self.model_name = self.model._meta.model_name  # _meta.model_name 模型表对应的字符串名
        self.key_word = ''
    # 表格最左侧的选择框, is_header 代表该字段是否是表头信息
    def check_col(self, is_header=False, obj=None):
        # 选择框的特点: 第一行为 选择两个字, 第二行为选择框的 勾选栏 状态
        if is_header:
            return '选择'
        return mark_safe('<input type="checkbox" value="%s" name="selected_action"/>' %obj.pk)

    def get_reverse_url(self, type, obj=None):
        # type: 代表种类,如list,add,edit,delete,obj为对象,如果obj存在,则为编辑或者删除
        if obj:
            _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type), args=(obj.pk,))
        else:
            _url = reverse('%s_%s_%s' % (self.app_label, self.model_name, type))
        return _url

    def edit_col(self, is_header=False, obj=None):
        if is_header:
            return '编辑'
        _url = self.get_reverse_url('edit', obj)
        return mark_safe('<a href="%s">编辑</a>' % _url)

    def delete_col(self, is_header=False, obj=None):
        if is_header:
            return '删除'
        _url = self.get_reverse_url('delete', obj)
        return mark_safe('<a href="%s">删除</a>' % _url)

    # 让每个表中都含有选择、编辑、删除的列
    def get_new_list_display(self):
        tmp = []
        tmp.append(ModelStark.check_col)
        tmp.extend(self.list_display) # 根据查找顺序,此时查找的list_display不是上面定义的list_display,
        # 而是传过来的self表自定义的list_display,如若没有自定义的,则使用上面定义好的
        if not self.list_display_links:
            tmp.append(ModelStark.edit_col)
        tmp.append(ModelStark.delete_col)

        return tmp

    def search(self, request, queryset):
        key_word = request.GET.get('q')  # 获取用户输入的值
        self.key_word = ''
        if key_word:
            self.key_word = key_word
            # search_field = ['title', 'price']
            # queryset = queryset.filter(title__contains=key_word)
                # title__contains是变量名,因此不能循环search_field进行字符串拼接,获得的是字符串而不是变量
            """
            查询条件变量名
            查询条件   and ---> or
            """
            q = Q()  # Q查询允许通过字符串进行查找
            q.connector = 'or'  # Q默认为and查询, 查询时需要改为or
            # 不停地往children中添加查询条件
            for search_field in self.search_fields:
                q.children.append(('%s__icontains' % search_field, key_word))  # 拼接查询条件
            queryset = queryset.filter(q)
        return queryset

    def filter_data(self, request, queryset):
        q = Q()
        for field in self.list_filter:
            if field in request.GET:
                field_value = request.GET.get(field)
                q.children.append((field, field_value))
        queryset = queryset.filter(q)
        return queryset


    def list_view(self, request):
        print(self.model)  # self为模型表对象,谁传过来就是谁,book传过来,self.model就是book
        queryset = self.model.objects.all()
        print(queryset)

        if request.method == 'POST':
            # 获取用户选中的主键字段
            action = request.POST.get('action')     # 函数名字符串形式
            pk_list = request.POST.getlist('selected_action')
            # 根据主键字段查询苏欧欧的数据
            queryset_list = self.model.objects.filter(pk__in=pk_list)
            # 将queryset对象传给函数处理
            real_action = getattr(self, action)  # 拿真正的函数名
            real_action(request, queryset_list)

        # search功能
        queryset = self.search(request, queryset)

        # filter功能  list_filter = ['publish', 'authors']
        queryset = self.filter_data(request, queryset)


        show_obj = ShowList(self, queryset, request)
        url = self.get_reverse_url('add')
        return render(request, 'stark/list_view.html', locals())


    def get_model_form(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__'
        return ModelFormClass


    def add_view(self, request):
        model_form_class= self.get_model_form()
        model_form_obj = model_form_class()
        if request.method == 'POST':
            model_form_obj = model_form_class(request.POST)
            pop_back_id = request.GET.get('pop_back_id')
            if model_form_obj.is_valid():
                obj = model_form_obj.save()

                if pop_back_id: # 如果pop_back_id有值 说明是子页面提交过来的post请求
                    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 form_obj in model_form_obj:
            # print(form_obj.field)  # form_obj.field获得的是该字段的类型
            if isinstance(form_obj.field, ModelChoiceField):
                form_obj.is_pop = True

                rel_model = self.model._meta.get_field(form_obj.name).rel.to   # 获取外键字段对应的表的名字
                rel_app_label = rel_model._meta.app_label
                rel_model_name = rel_model._meta.model_name
                url = reverse('%s_%s_%s' % (rel_app_label, rel_model_name, 'add'))
                print(form_obj.auto_id)  # 当前input框对应的id值
                url = url + '?pop_back_id=%s' % form_obj.auto_id
                form_obj.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()
        model_form_obj = model_form_class(instance=edit_obj)
        if request.method == 'POST':
            model_form_obj = model_form_class(request.POST, instance=edit_obj)
            if model_form_obj.is_valid():
                model_form_obj.save()
                return redirect(self.get_reverse_url('list'))
        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'))

    # 实现二级路由分发 起别名(反向解析))
    @property
    def urls(self):
        tmp = [
            url(r'^$', self.list_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'list')),
            url(r'^add/', self.add_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'add')),
            url(r'^edit/(\d+)/', self.edit_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'edit')),
            url(r'^delete/(\d+)/', self.delete_view, name='%s_%s_%s' % (self.app_label, self.model_name, 'delete')),
        ]

        return tmp, None, None


class StarkSite(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 = ModelStark

        # Instantiate the admin class to save in the registry
        self._registry[model] = admin_class(model)  # admin_class(model)为配置类对象

    # 实现一级路由,之所以不在这里实现二级路由分发,是因为这里的self是 site对象,
    # 当book,publish ... 传过来的时候,self知道依旧是site,无法对表的种类进行区分
    def get_urls(self):
        tmp = []
        for model_class, config_obj in self._registry.items():
            # config_obj为配置类对象,跟模型表一一对应,因此,我们在配置类中进行二级路由分发
            app_label = model_class._meta.app_label
            model_name = model_class._meta.model_name
            # tmp.append(url(r'^%s/%s' %(app_label, model_name), self.test)) # 实现一级路由
            tmp.append(url(r'^%s/%s/' % (app_label, model_name), config_obj.urls))  # 实现一级路由
        return tmp

    @property
    def urls(self):
        return self.get_urls(), None, None


site = StarkSite()
stark.py
from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules

class StarkConfig(AppConfig):
    name = 'stark'

    def ready(self):
        # 项目一启动,就会自动查找每一个应用下的stark.py文件
        return autodiscover_modules('stark')
apps.py

4.4 HTML页面

{% extends 'stark/base.html' %}

{% block css %}
    <link rel="stylesheet" href="/static/css/mycss.css">
{% endblock %}

{% block content %}
    <h2>添加数据</h2>
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post">
                {% csrf_token %}
                {% for form_obj in model_form_obj %}
                    <div class="plus-father">
                    <p>{{ form_obj.label }}{{ form_obj }}
                    <span>{{ form_obj.errors.0 }}</span>
                    </p>
                    {% if form_obj.is_pop %}
                        <span class="plus" onclick="WindowOpen('{{ form_obj.url }}')">+</span>
                    {% endif %}
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-primary pull-right" >
            </form>

        </div>

    <script>
        function WindowOpen(url) {
            window.open(url, '', 'width=800px, height=400px')
        }

        function addOptions(pop_back_id, pk, text) {
            // 动态创建option标签
            var opEle = document.createElement('option');
            //给标签赋值
            opEle.innerText = text;
            opEle.value = pk;
            opEle.selected = 'selected';
            // 查找option所在的父标签
            var seEle = document.getElementById(pop_back_id);
            seEle.appendChild(opEle)
        }


    </script>


{% endblock %}
add_view.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Title</title>
    <script src="/static/jquery-3.4.1.js"></script>
    <link rel="stylesheet" href="/static/bs-3.3.7/css/bootstrap.css">
    <script src="/static/bs-3.3.7/js/bootstrap.min.js"></script>
    {% block css %}
    
    {% endblock %}

    <style>
        .active {
            color: red;
        }
        .plus {
            position: absolute;
            font-size: 24px;
            color: #369;
            top: 19px;
            right: -27px;
        }
        .plus-father {
            position: relative;
        }
    </style>

</head>
<body>


<div class="container">
    <div class="row">
        {% block content %}

        {% endblock %}
    </div>
</div>


</body>
>


</html>
base.html
{% extends 'stark/base.html' %}


{% block css %}
<link rel="stylesheet" href="/static/css/mycss.css">
{% endblock %}

{% block content %}


<h2>编辑数据</h2>
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post">
                {% csrf_token %}
                {% for form_obj in model_form_obj %}
                    <p>{{ form_obj.label }}{{ form_obj }}</p>
                    <span>{{ form_obj.errors.0 }}</span>
                {% endfor %}
                <input type="submit" class="btn btn-primary pull-right">
            </form>

        </div>



{% endblock %}
edit_view.html
{% extends 'stark/base.html' %}






{% block content %}
    <h2>数据展示</h2>
    <div class="col-md-9">
        <a href="{{ url }}" class="btn btn-primary">添加数据</a>

        {# search功能开始 #}
        {% if show_obj.config_obj.search_fields %}
            <form class="form-inline pull-right">
                <div class="form-group">

                    <div class="input-group">

                        <input type="text" class="form-control" id="exampleInputAmount" placeholder="关键字" name="q"
                               value="{{ show_obj.config_obj.key_word }}">
                    </div>
                </div>
                <button type="submit" class="btn btn-primary">Search</button>
            </form>
        {% endif %}
        {# search功能结束 #}


        {# action样式开始 #}
        <form action="" method="post" class="form-inline">
            {% csrf_token %}
            <select name="action" id="" class="form-control">
                <option value="">-----------------------</option>

                {% for foo in show_obj.get_actions %}
                    <option value="{{ foo.name }}">{{ foo.desc }}</option>

                {% endfor %}


            </select>
            <input type="submit" value="GO" class="btn btn-primary">
            <table class="table table-bordered table-striped">

                <thead>
                {% for head in show_obj.get_header %}
                    <th>{{ head }}</th>
                {% endfor %}
                </thead>
                <tbody>
                {% for body in show_obj.get_body %}
                    <tr>
                        {% for foo in  body %}
                            <td>{{ foo }}</td>

                        {% endfor %}

                    </tr>

                {% endfor %}

                </tbody>
            </table>
        </form>

        {# action样式结束 #}


        {{ show_obj.page_obj.page_html|safe }}
    </div>
    <div class="col-md-3">
        {% if show_obj.config_obj.list_filter %}
            <div class="alert-info text-center">FILTER</div>

        {% for k,v in show_obj.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 %}

        {% endif %}



    </div>

{% endblock %}
list_view
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Title</title>
</head>
<body>


<script>
    window.opener.addOptions('{{ pop_back_id }}', '{{ pk }}', '{{ text }}');
    window.close()
</script>

</body>

</html>
pop.html

 

5 pop实现添加数据(需配合视频)

    pop
        window.open(url,'','width=800px')  # 打开一个url页面, 中间为'',不用管, 第三为弹框的大小
        
        子页面可以调用父页面中的方法
            window.opener.fatherFunc(...)
            window.close()
        
        1.哪些标签需要加 加号
            form_obj.field 
                form_obj.is_pop = True
            
        
        2.给加号绑定点击事件
            url是外键字段所对应的模型表的添加url
            app_label = models.Book._meta.app_label
            model_name = models.Book._meta.model_name
            url = reverse('%s_%s_add'%(app_label,model_name))
        
        
            function WindowOpen(url){
                window.open(url,'','width=800px,height=400px')
            }
            
        2.如何在后端添加逻辑中区分是主页面还是子页面发送的post请求
            在打开子页面的url后面加get请求参数
            获取form_obj渲染的标签id值
            form_obj.auto_id
            
            
        
        3.父页面新增添加数据的方法
            function addOption = document.createElement('option')

 

posted on 2019-08-06 22:45  软饭攻城狮  阅读(184)  评论(0编辑  收藏  举报

导航