基於Django-admin實現stark組件

一、url的分發練習

from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import  HttpResponse
def index(request):
    return HttpResponse("首頁")
    
    
def add(request):                         #add視圖函數
    return HttpResponse("add")
def list_view(request):                  #list_view視圖函數
    return HttpResponse("list_view")
def change(request,id):                  #change視圖函數
    return HttpResponse("change")
def delete(request,id):                  #delete視圖函數
    return HttpResponse("delete")
    
    
def get_urls2():                        #url的二級分發函數
    temp=[
        url("^add/$",add),
        url("^$",list_view),
        url("^(\d+)/change/$",change),
        url("^(\d+)/delete/$",delete),
    ]
    return temp
    
def get_urls():
    temp=[]
    for model,model_class_obj in admin.site._registry.items(): # 模型類:該模型類的配置類對象 {Book:ModelAdmin(Book),Publish:ModelAdmn(Publish),....}
          app_name=model._meta.app_label                      #得到應用名
          model_name=model._meta.model_name
          temp.append(url(r"%s/%s/"%(app_name,model_name),(get_urls2(),None,None)))   #url做分發,後面跟元組,元組第一項爲一個列表
    return  temp
    
from stark.service.sites import site
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r"^stark",(get_urls(),None,None))
]


二、實現stark組件


app01應用的models.py文件:

from django.db import models
class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()
    def __str__(self):
        return self.name
class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField()
    def __str__(self):
        return self.name
class Book(models.Model):
    nid = models.AutoField(primary_key=True,verbose_name="編號")
    title = models.CharField( max_length=32,verbose_name="名稱")
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,verbose_name="價格")
    # 與Publish建立一對多的關係,外鍵字段建立在多的一方
    publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
    # 與Author表建立多對多的關係,ManyToManyField可以建在兩個模型中的任意一個,自動創建第三張表
    authors=models.ManyToManyField(to='Author',)
    def __str__(self):
        return self.title
        
        
#執行下面python語句生成相關表(數據遷移)
# python3 manage.py makemigrations
# python3 manage.py migrate


項目下的settings.py文件設置:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    "app02.apps.App02Config",
    "stark.apps.StarkConfig",
]


STATIC_URL = '/static/'
STATICFILES_DIRS=[
    os.path.join(BASE_DIR,"static")
]


項目的urls.py文件:

from django.conf.urls import url
from django.contrib import admin
from stark.service.sites import site
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^stark/', site.urls),               #模擬admin的實現方式,在stark目錄後面進行分發
]


app01應用的stark.py文件對app01的應用表進行註冊:


from stark.service.sites import site,ModelStark
from app01.models import Book
from app01.models import Publish
from app01.models import Author
class BookConfig(ModelStark):
    # def display_authors(self, obj=None,is_header=False):     #顯示多對多字段,單獨定義函數實現方式
    #
    #     if is_header:
    #         return "作者"
    #     s=[]
    #     for author in obj.authors.all():
    #         s.append(author.name)
    #
    #     return " ".join(s)
    list_display = ["nid","title","price","publish","authors",]
    # list_display = ["nid","title","price","publish",display_authors]
site.register(Book,BookConfig)
site.register(Publish)
site.register(Author)


項目下的stark目錄下apps.py定義執行各個應用的stark.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')      # 執行每一個app下的stark.py


項目下的stark目錄下service目錄下sites.py定義對用戶訪問url的分發:

from django.conf.urls import url
from django.shortcuts import HttpResponse, render
from django.utils.safestring import mark_safe
class ModelStark():                    #將增,刪,改,查寫到ModelStark類中,這樣誰調用類中的功能,self.model就是用戶訪問的表
    list_display = ["__str__", ]
    def __init__(self, model, site):
        self.model = model
        self.site = site
    def edit(self, obj=None, is_header=False):
        if is_header:
            return "操作"                                                   #表頭返回 "操作"
        return mark_safe("<a href='%s/change'>編輯</a>" % obj.pk)        #mark_safe會識別返回的html標籤
    def delete(self, obj=None, is_header=False):
        if is_header:
            return "操作"
        return mark_safe("<a href='%s/delete'>刪除</a>" % obj.pk)
    def checkbox(self, obj=None, is_header=False):
        if is_header:
            return "選擇"
        return mark_safe("<input type='checkbox' pk=%s>" % obj.pk)        #在對應位置顯示多選框
    def add(self, request):
        return HttpResponse("add")
    def new_list_display(self):                 #新的顯示樣式,這樣所有表的顯示都有多選框,編輯和刪除操作
        temp = []
        temp.append(ModelStark.checkbox)
        temp.extend(self.list_display)
        temp.append(ModelStark.edit)
        temp.append(ModelStark.delete)
        return temp
    def list_view(self, request):
        print(self.model)
        data_list = self.model.objects.all()
        print("list_display", self.list_display)  # ["nid","title","price",edit]
        # 處理表頭
        header_list = []
        for field in self.new_list_display():
            if isinstance(field, str):                              #判斷field是字符串類型
                if field == "__str__":                            #字段是__str__
                    val = self.model._meta.model_name.upper()        #表頭顯示錶名的大寫字母
                else:
                    field_obj = self.model._meta.get_field(field)
                    val = field_obj.verbose_name                      #得到字段的verbose_name,定義後可以顯示中文
            else:
                val = field(self, is_header=True)                  #field是函數,執行函數
            header_list.append(val)
        # 處理表單數據
        new_data_list = []
        for obj in data_list:
            temp = []
            for field in self.new_list_display():  # ["nid","title","price","authors",edit]    ['__str__']     ["title","price"]
                if isinstance(field, str):
                    try:
                        from django.db.models.fields.related import ManyToManyField
                        field_obj = self.model._meta.get_field(field)          #得到field的字段對象
                        if isinstance(field_obj, ManyToManyField):            #判斷是多對多關係的字段
                            l = []
                            for i in getattr(obj, field).all():             #.all()得到所有的對象,否則none
                                l.append(str(i))
                            val = ",".join(l)
                        else:
                            val = getattr(obj, field)
                            print("val", val)
                    except Exception as e:
                        val = getattr(obj, field)                      #反射,取到字段名
                else:
                    val = field(self, obj)
                temp.append(val)
            new_data_list.append(temp)
        return render(request, "list_view.html", locals())
    def change(self, request, id):
        return HttpResponse("change")
    def delete_view(self, request, id):
        return HttpResponse("delete_view")
    def get_urls2(self):
        temp = [
            url("^add/$", self.add),
            url("^$", self.list_view),
            url("^(\d+)/change/$", self.change),
            url("^(\d+)/delete/$", self.delete_view),
        ]
        return temp
    @property
    def urls2(self):
        return self.get_urls2(), None, None
        
        
class StarkSite():
    def __init__(self, ):
        self._registry = {}
    # 一級分發
    def get_urls(self):
        temp = []
        for model, model_class_obj in self._registry.items():  # {Book:ModelAdmin(Book),Publish:ModelAdmn(Publish),....}
            app_name = model._meta.app_label
            model_name = model._meta.model_name
            temp.append(url(r"%s/%s/" % (app_name, model_name), model_class_obj.urls2))  #接着二級分發
        return temp
    @property
    def urls(self):
        return self.get_urls(), None, None
    def register(self, model, admin_class=None, **options):
        if not admin_class:
            admin_class = ModelStark
        self._registry[model] = admin_class(model, self)
        
        
site = StarkSite()


list_view.html頁面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">
</head>
<body>
<h3>查看數據</h3>
<div>
    <div>
        <div>
            <table class="table table-bordered table-hover table-striped">
                <thead>
                     <tr>
                          {% for foo in header_list %}
                          <th>{{ foo }}</th>
                          {% endfor %}
                     </tr>
                </thead>
                <tbody>
                      {% for new_data in new_data_list %}
                      <tr>
                         {% for foo in new_data %}
                         <td>{{ foo }}</td>
                         {% endfor %}
                      </tr>
                      {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>


三、顯示多對多字段的另一種方法


stark.py註冊表時自定義函數:

class BookConfig(ModelStark):
    def display_authors(self, obj=None,is_header=False):     #顯示多對多字段
        if is_header:
            return "作者"
        s=[]
        for author in obj.authors.all():
            s.append(author.name)
        return " ".join(s)
    list_display = ["nid","title","price","publish",display_authors]


sites.py裏面的ModelStark類定義的list_view

def list_view(self, request):
    print(self.model)
    data_list = self.model.objects.all()
    print("list_display", self.list_display)  #  ["nid","title","price",edit]
    # 處理表頭
    # header_list=["ID","名稱","價格"]
    header_list=[]
    for field in self.new_list_display():
        if isinstance(field,str):
            if field=="__str__":
                val=self.model._meta.model_name.upper()
            else:
                field_obj=self.model._meta.get_field(field)
                val=field_obj.verbose_name
        else:
            val=field(self,is_header=True)
        header_list.append(val)
    # 處理表單數據
    new_data_list = []
    for obj in data_list:
        temp = []
        for field in self.new_list_display():  #   ["nid","title","price","authors",edit]    ['__str__']     ["title","price"]
            if isinstance(field,str):
                val = getattr(obj, field)
            else:
                val=field(self,obj)
            temp.append(val)
        new_data_list.append(temp)

頁面顯示效果圖:

QQ截圖20180615181344.png


四、stark組件自定義功能設置

app01應用下的stark.py文件:

from stark.service.sites import site,ModelStark
from app01.models import Book
from app01.models import Publish
from app01.models import Author
class BookConfig(ModelStark):
    def display_authors(self, obj=None,is_header=False):
        if is_header:
            return "作者"
        s=[]
        for author in obj.authors.all():
            s.append(author.name)
        return " ".join(s)
    list_display = ["nid","title","price","publish","authors",]   #自定義顯示的字段
    
    search_fields=["title","price"]                                 #搜索依據的字段
    
    def patch_init(self,selected_pk):
        print("selected_pk",selected_pk)
        ret=self.model.objects.filter(pk__in=selected_pk).update(price=0)
        print("====>",ret)
    patch_init.desc="批量初始化"
    def patch_delete(self,selected_pk):
        print("selected_pk",selected_pk)
        ret=self.model.objects.filter(pk__in=selected_pk).delete()
        print("====>",ret)
    patch_delete.desc="批量刪除"
    actions=[patch_init,patch_delete]                               #定義批量操作
    
site.register(Book,BookConfig)
site.register(Publish)
site.register(Author)


stark組件下service目錄sites.py文件進行完善:

from django.conf.urls import url
from django.shortcuts import HttpResponse, render, redirect
from django.utils.safestring import mark_safe
from django.urls import reverse
class Show_List(object):
    def __init__(self, config, data_list):
        self.config = config
        self.data_list = data_list
        
    def get_header(self):
        # 處理表頭
        # header_list=["ID","名稱","價格"]
        header_list = []
        for field in self.config.new_list_display():
            if isinstance(field, str):
                if field == "__str__":
                    val = self.config.model._meta.model_name.upper()
                else:
                    field_obj = self.config.model._meta.get_field(field)
                    val = field_obj.verbose_name
            else:
                val = field(self.config, is_header=True)
            header_list.append(val)
        return header_list
        
    def get_body(self):
        # 處理表單數據
        new_data_list = []
        for obj in self.data_list:
            temp = []
            for field in self.config.new_list_display():  # ["nid","title","price","authors",edit]    ['__str__']     ["title","price"]
                if isinstance(field, str):
                    try:
                        from django.db.models.fields.related import ManyToManyField
                        field_obj = self.config.model._meta.get_field(field)
                        if isinstance(field_obj, ManyToManyField):
                            l = []
                            for i in getattr(obj, field).all():
                                l.append(str(i))
                            val = ",".join(l)
                        else:
                            val = getattr(obj, field)
                            print("val", val)
                    except Exception as e:
                        val = getattr(obj, field)
                else:
                    val = field(self.config, obj)
                temp.append(val)
            new_data_list.append(temp)
        return new_data_list
        
    def get_new_actions(self):
        action_list = []
        for i in self.config.actions:  # [patch_init,]
            action_list.append({
                "desc": i.desc,
                "name": i.__name__,
            })
        return action_list
        
class ModelStark():  # 功能的樣式顯示類
    list_display = ["__str__", ]
    search_fields = []
    actions = []
    def __init__(self, model, site):
        self.model = model
        self.site = site
        
    def edit(self, obj=None, is_header=False):
        if is_header:
            return "操作"
        return mark_safe("<a href='%s/change'>編輯</a>" % obj.pk)
        
    def delete(self, obj=None, is_header=False):
        if is_header:
            return "操作"
        return mark_safe("<a href='%s/delete'>刪除</a>" % obj.pk)
        
    def checkbox(self, obj=None, is_header=False):
        if is_header:
            return "選擇"
        return mark_safe("<input type='checkbox' name='selected_pk' value=%s>" % obj.pk)
        
    def get_list_url(self):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label
        _url = reverse("%s_%s_list" % (app_label, model_name))
        return _url
        
    def new_list_display(self):
        temp = []
        temp.append(ModelStark.checkbox)
        temp.extend(self.list_display)
        temp.append(ModelStark.edit)
        temp.append(ModelStark.delete)
        return temp
        
    def get_search_condition(self, request):        #定義搜索查找函數
        from django.db.models import Q
        search_condition = Q()
        val = request.GET.get("q")
        if val:
            search_condition.connector = "or"        #設置搜索依據的字段是或者的關係,即滿足一個字段的查找結果就可以
            for field in self.search_fields:
                search_condition.children.append((field + "__contains", val))  #搜索的字符串進行模糊匹配
        return search_condition
        
    def list_view(self, request):
        if request.method == "POST":
            action = request.POST.get("action")
            selected_pk = request.POST.getlist("selected_pk")
            action = getattr(self, action)
            action(selected_pk)
        search_condition = self.get_search_condition(request)
        data_list = self.model.objects.all().filter(search_condition)
        print("list_display", self.list_display)  # ["nid","title","price",edit]
        sl = Show_List(self, data_list)
        return render(request, "list_view.html", locals())
        
    def get_mdoelForm(self):
        from django.forms import ModelForm
        class DemoModelForm(ModelForm):
            class Meta:
                model = self.model
                fields = "__all__"
        return DemoModelForm
        
    def add(self, request):                         #定義添加頁面的函數
        if request.method == "POST":
            form = self.get_mdoelForm()(request.POST)
            if form.is_valid():
                form.save()
                return redirect(self.get_list_url())
            else:
                return render(request, "add.html", locals())
        form = form = self.get_mdoelForm()()
        return render(request, "add.html", locals())
        
    def change(self, request, id):                   #定義編輯函數
        obj = self.model.objects.filter(pk=id).first()
        if request.method == "POST":
            form = self.get_mdoelForm()(request.POST, instance=obj)
            if form.is_valid():
                form.save()
                return redirect(self.get_list_url())
        form = self.get_mdoelForm()(instance=obj)
        return render(request, "change.html", locals())
        
    def delete_view(self, request, id):
        if request.method == "POST":
            self.model.objects.get(pk=id).delete()
            return redirect(self.get_list_url())
        url = self.get_list_url()
        return render(request, "delete.html", locals())
        
    def get_urls2(self):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label
        temp = [
            url("^add/$", self.add, name="%s_%s_add" % (app_label, model_name)),
            url("^$", self.list_view, name="%s_%s_list" % (app_label, model_name)),
            url("^(\d+)/change/$", self.change, name="%s_%s_change" % (app_label, model_name)),
            url("^(\d+)/delete/$", self.delete_view, name="%s_%s_delete" % (app_label, model_name)),
        ]
        return temp
    @property
    
    def urls2(self):
        return self.get_urls2(), None, None
        
class StarkSite():
    def __init__(self, ):
        self._registry = {}
    # 一級分發
    def get_urls(self):
        temp = []
        for model, model_class_obj in self._registry.items():
            app_name = model._meta.app_label
            model_name = model._meta.model_name
            temp.append(url(r"%s/%s/" % (app_name, model_name), model_class_obj.urls2))
        return temp
    @property
    def urls(self):
        return self.get_urls(), None, None
        
    def register(self, model, admin_class=None, **options):
        if not admin_class:
            admin_class = ModelStark
        self._registry[model] = admin_class(model, self)
        
site = StarkSite()


templates模板

form.html頁面:

<div>
    <div>
        <div class="col-md-8 col-md-offset-1">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div>
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span style="color: red">{{ field.errors.0 }}</span>
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-default">
            </form>
        </div>
    </div>
</div>


add.html添加頁面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">
    <style>
        input[id], 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;
        }
    </style>
</head>
<body>
<h3>添加頁面</h3>
{% include 'form.html' %}
</body>
</html>


change.html修改頁面:


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">
    <style>
        input[id], 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;
        }
    </style>
</head>
<body>
<h3>編輯頁面</h3>
{% include 'form.html' %}
</body>
</html>


delete.html刪除頁面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">
</head>
<body>
<h3>刪除頁面</h3>
<form action="" method="post">
    {% csrf_token %}
    <input type="submit" value="確認刪除">
    <a href="{{ url }}">取消</a>
</form>
</body>
</html>


list_view.html顯示頁面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">
    <style>
        .search {
            margin: 20px;
        }
    </style>
</head>
<body>
<h3>查看數據</h3>
<a href="add/" class="btn btn-primary">添加數據</a>
<div>
    <div>
        <div>
            <form action="" class="search pull-right" method="get">
                <input type="text" name="q" style="width: 300px;display: inline-block">
                <button class="btn btn-danger">search</button>
            </form>
            <form action="" method="post">
                {% csrf_token %}
                <select name="action" id="" style="width: 200px;display: inline-block">
                       {% for action_dict in sl.get_new_actions %}
                           <option value="{{ action_dict.name }}">{{ action_dict.desc }}</option>
                       {% endfor %}
                </select>
                <button type="submit" class="btn btn-success">Go</button>
                <table class="table table-bordered table-hover table-striped">
                    <thead>
                    <tr>
                        {% for foo in sl.get_header %}
                            <th>{{ foo }}</th>
                        {% endfor %}
                    </tr>
                    </thead>
                    <tbody>
                    {% for new_data in sl.get_body %}
                        <tr>
                            {% for foo in new_data %}
                                <td>{{ foo }}</td>
                            {% endfor %}
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </form>
        </div>
    </div>
</div>
</body>
</html>


頁面效果圖:

添加.png

編輯.png

刪除.png

查看.png





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章