Django之stark组件

  现在让我说啥是stark组件,我也说不清楚。反正从今天讲的知识来看,今天完成的就是自己写一个模块,这个模块包含了admin后台管理工具的一些比较好用的功能,我们把它提炼出来,也就是相当于自己写一个admin后台管理工具,但这个工具不叫admin了,叫stark。既然是模仿admin的实现功能,所以整个执行过程和admin是一样的。首先我们得建一个stark包,即一个stark文件夹,在这里,我们也可以新建一个APP叫stark也行。

  第一步,启动所有APP下的stark.py文件

  每当我们创建一个新的APP时,文件夹下都会有一个admin.py的文件,这是django帮你做的,但现在的后台管理工具换成stark了,所以我们首先要在每个APP下新建一个stark.py的文件。怎么才能让django启动的时候,也把每个APP下的stark.py文件执行呢????我们知道哈,admin是在它的__init__.py文件写了

def autodiscover():
    autodiscover_modules('admin', register_to=site)

  django项目启动的时候会执行每个APP下的apps.py文件,我们可以把这句代码写stark文件夹下的apps.py文件里,这样就能保证django项目启动后会执行这一句代码,然后通过这句代码,执行每个APP下的stark.py文件,这样我们就启动了每个APP下的stark.py文件。

  apps.py

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

class StarkConfig(AppConfig):
    name = 'stark'
    def ready(self):
        autodiscover_modules('stark')

  第二步,该写stark的源码了,完成注册功能

  我们在stark文件夹下新建一个service文件夹用来放源码。

  sites.py源码文件

class ModelStark():
    def __init__(self,model):
        self.model=model

class StarkSite():
    def __init__(self):
        self._registry={}
    def register(self,model,stark_class=None):
        stark_class = stark_class or ModelStark
        self._registry[model] = stark_class(model)
site=StarkSite()

  我们可以给app01下的Book表和Publish注册

  app01下的stark.py

from stark.service.sites import site,ModelStark
from app01.models import *
class BookConfig(ModelStark):
    list_dispaly = ['name','price']
site.register(Book,BookConfig)
site.register(Publish)

  这样就完成注册了

  第三步,实现url的二级分发

  urls.py

from django.contrib import admin
from django.urls import path
from stark.service.sites import site

urlpatterns = [
    path('admin/', admin.site.urls),
    path('stark/', site.urls),
]

  然后再去源码里添加内容

  sites.py

class ModelStark():
    def get_urls(self):
    temp=[
            path('',self.list_view),
            path('add/',self.add_view),
            re_path('(\d+)/delete/',self.delete_view),
            re_path('(\d+)/edit/',self.edit_view),
        ]
        return temp
    @property
    def urls(self):
        return self.get_urls(), None, None



class StarkSite():
        def get_urls(self):
        temp=[]
        for model,config_obj in self._registry.items():
            model_name = model._meta.model_name
            app_label = model._meta.app_label
            temp.append(path('%s/%s/'%(app_label,model_name),config_obj.urls))
        return temp

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

  这个二级分发的想法是很巧秒的,一级分发在StarkSite类里面定义的,二级分发是在配置类里面定义的。调用视图函数的对象变成了配置类对象。在注册模型类的时候,我们就说了,我们可以给每个模型类定制一个配置类,然后去继承ModelStark,从而每个模型类可以重新定义自己的变量,也可以使用ModelStark类的变量,这样变的很灵活。到此,stark的主要功能就写完了,接下来我只需对每个视图写上逻辑和自己定制templates模板,就完成了,说的很简单,但其实并不是那么简单。

  第四步,首先写一个查询的视图函数和模板

  我们把视图定义成Modelstark的一个方法,直接用模型类对象就可以调用。我们首先在stark文件下新建一个templates文件夹,django在找模板时,会现在全局找,然后再一个一个APP下找,所以我们不用修改任何设置就行。静态文件也是一个道理,如bootstrap文件和jQuery文件,我们直接在stark文件下新建一个static文件夹,然后放在里面就行,它也是先在全局找,然后再一个一个APP下找。我们为什么要把这些文件放在stark文件下,只是为了我们写的这个stark模块,以后用在任何项目里都可以,只需要把stark文件夹拷走就行了。

  sites.py

class  ModelStark:
  list_dispaly=['__str__'] def _checkbox(self,obj=None): if obj: return mark_safe('<input type="checkbox" name="check" vlaue="%s">'%obj.pk) return '选择' def _delete(self,obj=None): if obj: model_name = self.model._meta.model_name app_label = self.model._meta.app_label return mark_safe('<a href="/stark/%s/%s/%s/delete">删除</a>'%(app_label,model_name,obj.pk)) return '删除' def _edit(self,obj=None): if obj: model_name = self.model._meta.model_name app_label = self.model._meta.app_label return mark_safe('<a href="/stark/%s/%s/%s/edit">编辑</a>' % (app_label, model_name, obj.pk)) return '编辑' def get_new_list_display(self): new_list_display=self.list_dispaly new_list_display.insert(0,self._checkbox) new_list_display.append(self._edit) new_list_display.append(self._delete) return new_list_display def list_view(self,request): queryset=self.model.objects.all() header=[] for field in self.get_new_list_display(): if field=='__str__': header.append(self.model._meta.model_name.upper()) elif callable(field): header.append(field()) else: header.append(self.model._meta.get_field(field).verbose_name) data=[] for obj in queryset: tr=[] for field in self.list_dispaly: if callable(field): val=field(obj) else: val=getattr(obj,field) tr.append(val) data.append(tr) return render(request,'list_view.html',locals())

  list_view.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/bootstrap.css">
    <script src="/static/js/bootstrap.js"></script>
    <script src="/static/jquery-3.3.1.js"></script>
</head>
<body>
    <div class="container">
        <div class="title">所有书籍</div>
        <table class="table table-bordered table-hover table-striped">
            <thead>
            <tr>
                {% for foo in header %}
                    <td>{{ foo }}</td>
                {% endfor %}
            </tr>
            </thead>
            <tbody id="tbody">
                {% for book in data %}
                    <tr>
                        {% for foo in book %}
                            <td>{{ foo }}</td>
                        {% endfor %}
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>

  

 

 

  学习stark第二天,今天完成的功能不多,主要是url的反向解析,利用ModelForm完成了添加和编辑页面的搭建和数据的校验,在这还是不得不说ModelForm真心地好用,开发这个东西的大佬确实用了心的,感谢大佬。最后写了一个删除函数。

  一、实现url的反向解析

  在前面的博客,我们就讲了反向解析的好处,在这就扯它的好处了,直接上代码。

  sites.py

二级分发的代码稍有改动
self.model_name = model._meta.model_name
self.app_label = model._meta.app_label
self.app_model=(self.app_label,self.model_name)
def get_urls(self):
        temp=[
            path('',self.list_view,name='%s_%s'%self.app_model),
            path('add/',self.add_view,name='%s_%s_add'%self.app_model),
            re_path('(\d+)/delete/',self.delete_view,name='%s_%s_delete'%self.app_model),
            re_path('(\d+)/edit/',self.edit_view,name='%s_%s_edit'%self.app_model),
        ]
        return temp
写四个方法,通过反向解析分别拿到四个视图函数对应的路径,
def get_list_url(self):
        return reverse('%s_%s'%self.app_model)
def get_add_url(self):
        return reverse('%s_%s_add'%self.app_model)
def get_delete_url(self,obj):
        return reverse('%s_%s_delete'%self.app_model,args=(obj.pk,))
def get_edit_url(self,obj):
        return reverse('%s_%s_edit'%self.app_model,args=(obj.pk,))
以后哪里用到相关路径,直接调用方法就行了,如下:
def _delete(self,obj=None):
if obj:
return mark_safe('<a url="%s" class="delete btn btn-primary">删除</a>'%self.get_delete_url(obj))
return '删除'
def _edit(self,obj=None):
if obj:
return mark_safe('<a href="%s">编辑</a>' % self.get_edit_url(obj))
return '编辑'

  二、完成添加和编辑页面和数据校验

  首先得创建一个ModelForm类,为了灵活性,即用户可以自定义这个类,我们模仿了配置类写法。首先我们在默认的配置中写了一个方法,可以拿到我写的默认用的ModelForm类,然后用户可以在自定义的配置类里面重写这个方法,在放下重新定义一个ModelForm类,它就会覆盖默认的ModelForm类,从而用自定义的。

  sites.py

    def get_model_form_class(self):
        class Model_Form(forms.ModelForm):
            class Meta:
                model=self.model
                fields='__all__'
        return  Model_Form

  app01用户的自定义配置类里可以重写这方法:

  stark.py

from stark.service.sites import site,ModelStark
from app01.models import *
from django import forms
from django.forms import widgets as wid
class BookConfig(ModelStark):
    list_dispaly = ['name','price']
    list_display_link = ['name']
    def get_model_form_class(self):
        class Model_Form(forms.ModelForm):
            class Meta:
                model=self.model
                fields='__all__'
                widgets={'pub_date':wid.DateInput(attrs={'type':'date'})}
                error_messages={'name':{'required':'不能为空','max_length':'小于15个字符'},
                                'price':{'required':'不能为空'},
                                'publish': {'required': '不能为空'},
                                'pub_date': {'required': '不能为空'},
                                'author': {'required': '不能为空'}}
        return  Model_Form
site.register(Book,BookConfig)
site.register(Publish)

  sites.py

 def list_view(self,request):
        url=request.path
        add_url=self.get_add_url()
        zw_name=self.zw_name
        queryset=self.model.objects.all()
        header=[]
        for field in self.get_new_list_display():
            if field=='__str__':
                header.append(self.model_name.upper())
            elif callable(field):
                header.append(field())
            else:
                header.append(self.model._meta.get_field(field).verbose_name)
        data=[]
        for obj in queryset:
            tr=[]
            for field in self.get_new_list_display():
                if callable(field):
                    val=field(obj)
                else:
                    val=getattr(obj,field)
                    if field in self.list_display_link:
                        val=mark_safe('<a href="%s">%s</a>'%(self.get_edit_url(obj),val))
                tr.append(val)
            data.append(tr)
        return render(request,'list_view.html',locals())
def add_view(self,request): url=request.path pre_url=request.GET.get('next') zw_name=self.zw_name model_form=self.get_model_form_class() #在这调用的时候,应为APP01用户的自定义配置类下重新写了这方法,所以就调用自己写的,所以得到的ModelForm类就是自己定义的类,这样app01用户就可以控制ModelForm渲染标签的样式,还可以要校验的规则等 if request.method=='GET': form=model_form() return render(request,'add_view.html',locals()) else: form=model_form(request.POST) if form.is_valid(): form.save() return redirect(pre_url) return render(request,'add_view.html',locals())
def delete_view(self,request,num): self.model.objects.filter(pk=num).first().delete() return redirect(self.get_list_url())
def edit_view(self,request,num): obj=self.model.objects.all().filter(pk=num).first() zw_name = self.zw_name model_form=self.get_model_form_class() if request.method=='GET': form=model_form(instance=obj) return render(request,'add_view.html',locals()) form=model_form(request.POST,instance=obj) if form.is_valid(): form.save() return redirect(self.get_list_url()) return render(request,'edit_view.html',locals())

  add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/bootstrap.css">
    <script src="/static/jquery-3.3.1.js"></script>
    <script src="/static/js/bootstrap.js"></script>
    <style>
        .outer{
            margin-top: 200px;
        }
        .title{
            margin-bottom: 50px;
        }
        ul{
            list-style: none;
            margin: 0;
            padding: 0;
        }
        .left{
            position: fixed;
            left: 0;
            top: 20px;
        }
    </style>
</head>
<body>
    <div class="left">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">操作栏</h3>
            </div>
            <div class="panel-body">
                <div>
                    <a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>
                </div>
                <div>
                    <a href="" class="">添加作者</a>
                </div>
            </div>
        </div>
    </div>
    <div class="container outer">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <div style="color: blue;font-size: 50px;text-align: center;font-family: 华文隶书" class="title">添加{{ zw_name }}</div>
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group ">
                            <label for="id_{{ field.name }}">{{ field.label }}</label>
                            {{ field }}<span style="color: red">{{field.errors }}</span>
                        </div>
                    {% endfor %}
                    <button class="submit btn btn-info pull-right">提交</button>
                </form>
            </div>
        </div>
    </div>
    <script>
    $('input,select').addClass('form-control');
    </script>
</body>
</html>

  edit.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/bootstrap.css">
    <script src="/static/jquery-3.3.1.js"></script>
    <script src="/static/js/bootstrap.js"></script>
    <style>
        .outer{
            margin-top: 200px;
        }
        .title{
            margin-bottom: 50px;
        }
        ul{
            list-style: none;
            margin: 0;
            padding: 0;
        }
        .left{
            position: fixed;
            left: 0;
            top: 20px;
        }
    </style>
</head>
<body>
    <div class="left">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">操作栏</h3>
            </div>
            <div class="panel-body">
                <div>
                    <a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>
                </div>
                <div>
                    <a href="" class="">添加作者</a>
                </div>
            </div>
        </div>
    </div>
    <div class="container outer">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <div style="color: blue;font-size: 50px;text-align: center;font-family: 华文隶书" class="title">编辑{{ zw_name }}</div>
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div class="form-group ">
                            <label for="id_{{ field.name }}">{{ field.label }}</label>
                            {{ field }}<span style="color: red">{{field.errors }}</span>
                        </div>
                    {% endfor %}
                    <button class="submit btn btn-info pull-right">提交</button>
                </form>
            </div>
        </div>
    </div>
    <script>
    $('input,select').addClass('form-control');
    </script>
</body>
</html>

  删除是没有页面的,用的是一个confirm确定框做的,写在了查询页面的js里面

  list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/bootstrap.css">
    <script src="/static/js/bootstrap.js"></script>
    <script src="/static/jquery-3.3.1.js"></script>
    <style>
        .title{
            width: 100%;
            height: 50px;
            background-color:#337ab7;
            color: yellow;
            font-size: 40px;
            line-height: 50px;
            margin-bottom: 20px;
        }
    </style>
</head>
<body>
    <div class="title">{{ zw_name }}列表</div>
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading"><a href="{{ add_url }}?next={{ url }}" class="btn btn-info add">添加{{ zw_name }}</a></div>
            <table class="table table-bordered table-hover table-striped">
            <thead>
            <tr>
                {% for foo in header %}
                    <td>{{ foo }}</td>
                {% endfor %}
            </tr>
            </thead>
            <tbody id="tbody">
                {% for book in data %}
                    <tr>
                        {% for foo in book %}
                            <td>{{ foo }}</td>
                        {% endfor %}
                    </tr>
                {% endfor %}
            </tbody>
        </table>
        </div>
    </div>
    <script>
        $('.delete').click(function () {
            var url=$(this).attr('url');
            var status=confirm('确定删除?');
            if (status){
                location.href=url;}
        })
    </script>
</body>
</html>

 

 

  第二天就写到这里,明天继续。。。。。。。

posted @ 2019-01-15 21:25  W的一天  阅读(771)  评论(0编辑  收藏  举报