24.stark組件所有

admin組件

admin參考:https://www.cnblogs.com/yuanchenqi/articles/8323452.htmljavascript

model參考:https://www.cnblogs.com/yuanchenqi/articles/8963244.htmlcss

# admin的參數,以及自定義顯示
from django.contrib import admin
from app01.models import *
class BookConfig(admin.ModelAdmin):
    list_display = ["nid", "title", "price", "publishDate"]  # 定製顯示那些列,不能放多對多
    list_display_links = ["title"]  # 查看連接
    # list_filter = ["title", "publishDate", "authors"]  # 過濾
    list_editable = ["price"]  # 編輯
    search_fields = ["title", "price"]  # 搜索字段
    # date_hierarchy = "publishDate"
    fields = ("title",)   # 限定現實的字段
    exclude = ("",)   # 不顯示哪些字段,和fields相反
    ordering = ("nid",)  # 排序
    def patch_action(self, request, queryset):
        queryset.update(publishDate="2019-11-22")
    patch_action.short_description = "批量初始化"
    actions = [patch_action]
admin.site.register(Book, BookConfig)
admin.site.register(Author)
admin.site.register(AuthorDetail)
admin.site.register(Publish)

url使用:

# url的嵌套
from django.contrib import admin
from django.http import HttpResponse
from django.urls import path
def yuan(request):
    return HttpResponse("ok")
def test01(request):
    return HttpResponse("test01")
from starkapp.service.stark import site
urlpatterns = [
    path('admin/', admin.site.urls),
    url(r"^stark/", site.urls),
    url("^yuan/", ([
        url(r"^test01/", ([
             url(r"^test001/", test01),
            url(r"^test002/", test01),
        ], None, None),
            url(r"^test02/", test01),
            url(r"^test03/", test01),
                   ], None, None))  # 第一個是namespace, 第二個是app_name,
]

單例模式:

# 單例模式 (設計模式全部語言都有)
單例類:就是隻容許類生成一個對象,全部的修改對這一個對象的修改,保證數據的統一性
class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance
class MyClass(Singleton):
    a = 1
one = MyClass()
one.a = 2
two = MyClass()
print(two.a)

python天生就只吃單例模式的,爲何?
寫一個類,實例化一個對象,將其放入一個包中,其餘地方使用的時候直接從這個裏面導入(這個時候會生成一個.pyc,以後的每次使用這個對象都是從這個.pyc中取,能夠理解爲緩存),每次導入到的都是同一個對象,(內存地址相同)。

### admin源碼:
# 使用
path('admin/', admin.site.urls),
admin.site.register(Book, BookConfig)
    def register(self, model_or_iterable, admin_class=None, **options):
        admin_class = admin_class or ModelAdmin
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
 
                self._registry[model] = admin_class(model, self)
1.註冊admin.py
	admin.site.register(Book, BookConfig)
    def register(self, model_or_iterable, admin_class=None, **options):
    	if not admin_class:
            return admin_class=ModelAdmin
       	self._registry[model] = admin_class(model, self)
2.url設計
	如何獲取app名稱和model名稱
	model._meta.app_label, model._meta.model_name
一次分發
	def get_urls():
    temp = []
    for model, admin_class in admin.site._registry.items():
    	# url(r"app01/book/", )
        temp.append(url(r"^%s/%s" %(model._meta.app_label, model._meta.model_name), yuan))
    return temp
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        url(r"^stark/", site.urls),
        url("^yuan/", (get_urls(), None, None))
    ]
二次分發:
def add(request,):
    return HttpResponse("add")
def delete(request,id):
    return HttpResponse("delete")
def change(request,id):
    return HttpResponse("change")
def list_view(request,):
    return HttpResponse("list_view")
def get_urls_2():
    temp = []
    temp.append(url(r"^add/", add))
    temp.append(url(r"^(\d+)/delete/", delete))  # 函數本身定義
    temp.append(url(r"^(\d+)/change/", change))
    temp.append(url(r"^", list_view))

    return temp
def get_urls():
    temp = []
    for model, admin_class in admin.site._registry.items():
        temp.append(url(r"^%s/%s/" %(model._meta.app_label, model._meta.model_name), (get_urls_2(), None, None)))
    return temp
urlpatterns = [
    path('admin/', admin.site.urls),
    url(r"^stark/", site.urls),
    url("^yuan/", (get_urls(), None, None))
]

stark組件:

查看頁面:
	表頭
	表數據
	search
	action

直接給出參考博客網址:http://www.javashuo.com/article/p-tufkhsym-hr.htmlhtml

看不懂的話這裏有視頻:https://www.bilibili.com/video/av58974459?from=search&seid=4711332916584502531java

這篇博客開始部分很差開頭,我把開頭整理一下,關於老男孩的項目有了規律,就是你發現你如今看的項目沒有視頻,那麼多是前幾期教的,直接去B站搜這個項目名稱相關的視頻,就能夠找,我是直接看博客,看到多級過濾的時候看不下去了才發現的,第九期的視頻裏面有不少好的項目能夠看。python

基礎版本:

第一步:建立一個項目,去包裏面最少有兩個app(一個是stark組件的,一個是其餘用來測試的stark組件是否產生工做的),我建立了app01和stark組件的app(starkapp)名字能夠任意起只要你能找到就行
第二步:首先settings中註冊:
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'starkapp.apps.StarkappConfig',
    'app01.apps.App01Config',
]
第三步:
starkapp下面建立一個service的包,記住是包,不是的話可能出現問題。同時建立一個stark.py文件
第四步:
在stark.py中寫咱們的程序
from django.conf.urls import url
from django.http import HttpResponse
from django.shortcuts import render


class ModelStark(object):
    list_display = []

    def __init__(self, model, site):
        self.model = model  # 當前用戶訪問的模型表
        self.site = site

    # 記住這個視圖函數,若是下面的get_url_2中你沒有添加它的話,你根本看不到只記得頁面
    def change_list(self, request):
        ret = self.model.objects.all()
        return render(request, "index.html", locals())

    def add(self, request):
        return HttpResponse("add")

    def delete(self, request, id):
        return HttpResponse("delete")

    def change(self, request, id):
        return HttpResponse("change")

    # def list_view(self, request):
    #     return HttpResponse("list_view")

    def get_urls_2(self):
        temp = []
        model_name = self.model._meta.model_name  # 當前模型表
        app_label = self.model._meta.app_label  # 當前app

        temp.append(url(r"^add/$", self.add, name="%s_%s_add" % (app_label, model_name)))
        temp.append(url(r"^(\d+)/delete/$", self.delete, name="%s_%s_delete" % (app_label, model_name)))
        temp.append(url(r"^(\d+)/change/$", self.change, name="%s_%s_change" % (app_label, model_name)))
        temp.append(url(r"^$", self.change_list, name="%s_%s_list" % (app_label, model_name)))
        return temp

    @property
    def urls_2(self):
        return self.get_urls_2(), None, None  # [], None, None


class StarkSite(object):

    def __init__(self):
        self._registry = {}

    def register(self, model, stark_class=None, **options):
        if not stark_class:
            # 若是註冊的時候沒有自定義配置類,執行
            stark_class = ModelStark   # 配置類

        # 降配置類對象加到_registry字典中,創建模型類
        self._registry[model] = stark_class(model, self)   # _registry={'model':stark_class(model)}

    def get_urls(self):
        """構造一層url"""
        temp = []
        for model, stark_class_obj in self._registry.items():
            # model:一個模型表
            # stark_class_obj:當前模型表相應的配置類對象
            model_name = model._meta.model_name
            app_label = model._meta.app_label
            # 分發增刪改查url
            temp.append(url(r"^%s/%s/" % (app_label, model_name), stark_class_obj.urls_2))
            """
               path('app01/user/',UserConfig(User,site).urls2),
               path('app01/book/',ModelStark(Book,site).urls2),
            """
        return temp

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


site = StarkSite()  # 生成一個單例模式的對象
第五步:
有沒有發現本身的項目中urls沒配stark app的路由,如今就去
from django.conf.urls import url
from django.contrib import admin
from django.urls import path

from starkapp.service.stark import site

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r"^stark/", site.urls),  # 有沒有發現這個site是咱們本身寫的呢個單例模式的對象,沒錯就是它
]
第六步:
咱們要是用stark組件爲咱們的模型,建立增刪改查頁面,咱們是否是應該去本身的項目app01了
```python 要是用這個模塊咱們就須要建立一個stark1.py(這個名字能夠改,看完你就知道咋改了)在本身得項目裏面,內容是這樣的:

from starkapp.service.stark import site, ModelStark
from django.utils.safestring import mark_safe
from django.urls import reverse
from .models import *jquery

自定義配置類

class BookStark(ModelStark):
passgit

list_display = ["nid", "title", "price", edit, delete]

site.register(Book, BookStark) # 初始的你只須要把BookStark類定義了,使用咱們site註冊了就行,裏面的內容能夠直接passgithub

```html
第七步:
前段的頁面也給你:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
</head>
<body>
<h3>數據展現</h3>

<div class="container">
    <div class="row">
        <div class="col-md-8">
            <table class="table table-striped table-hover">
                <heade>
                    <tr>
                        <td>id</td>
                        <td>標題</td>
                        <td>價格</td>
                    </tr>
                </heade>

                {% for obj in ret %}
                    <tr>
                        <td>{{ obj.nid }}</td>
                        <td>{{ obj.title }}</td>
                        <td>{{ obj.price }}</td>
                    </tr>
                {% endfor %}
            </table>
        </div>
    </div>
</div>
</body>
</html>
第八步:
有沒有發現,咱們什麼都寫了,可是咋麼讓django找到我第六步建立的stark1.py呢?
如今回到stark組件app下面,在apps.py裏面寫下面的代碼:
from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules
class StarkappConfig(AppConfig):
    name = 'starkapp'

    # 程序啓動時,掃描app下得指定文件(stark1.py)並執行
    def ready(self):
        autodiscover_modules('stark1')  # 這句的做用就是讓django找到咱們的stark1。py模塊,執行裏面的site.register()方法,進行初始化路由。簡化版的在這裏完成了,已經能夠看到頁面了,我須要下次的寫高級一點的。

中級版本:

能夠將app01.stark1.py改爲
# 自定義配置類
class BookStark(ModelStark):
    def edit(self, obj=None, is_header=False):
        if is_header:
            return "操做"
        return mark_safe("<a href='/stark/app01/book/%s/change'>編輯</a>" % obj.pk)

    def delete(self, obj=None, is_header=False):
        if is_header:
            return "操做"
        return mark_safe("<a href='/stark/app01/book/%s/delete'>刪除</a>" % obj.pk)

    list_display = ["nid", "title", "price", edit, delete]
# 補充
field_obj = Book._meta.get_field("title")   #就是一個charField字段的對象
field_obj.max_length  # 最大長度
starkapp.service.stark.py改爲這樣,你只須要改着一個函數的內容
 def change_list(self, request):
        header_list = []
        for field in self.list_display:
            if callable(field):
                val = field(self, is_header=True)
                header_list.append(val)
            else:
                field_obj = self.model._meta.get_field(field)
                header_list.append(field_obj.verbose_name)
        data_list = self.model.objects.all()
        new_data_list = []
        for obj in data_list:
            temp = []
            for field in self.list_display:
                if callable(field):
                    val = field(self, obj)
                else:
                    val = getattr(obj, field)
                temp.append(val)
            new_data_list.append(temp)
        return render(request, "index.html", locals())
index.html改爲這樣
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
</head>
<body>
<h3>數據展現</h3>

<div class="container">
    <div class="row">
        <div class="col-md-8">
            <table class="table table-striped table-hover">
                <thead>
                    <tr>
                        {% for foo in header_list %}
                        <td>{{ foo }}</td>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody>
                    {% for data in new_data_list %}
                        <tr>
                            {% for item in data %}
                            <td>{{ item }}</td>
                            {% endfor %}
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

​ 而後你的頁面就變成了下面這同樣:接下來你也想到只要在starkapp.service.satrk下的modelstark類中將,那些函數補全你能夠刪除和編輯了。
web

如今我感受前面缺乏選擇框:
# 自定義配置類
class BookStark(ModelStark):
    def edit(self, obj=None, is_header=False):
        if is_header:
            return "操做"
        return mark_safe("<a href='/stark/app01/book/%s/change'>編輯</a>" % obj.pk)

    def delete(self, obj=None, is_header=False):
        if is_header:
            return "操做"
        return mark_safe("<a href='/stark/app01/book/%s/delete'>刪除</a>" % obj.pk)
	
	# 加了這麼一行
    def select(self, obj=None, is_header=None):
        if is_header:
            return mark_safe("<input id='mutPut' type='checkbox'>")
        return mark_safe("<input type='checkbox' value=%s>" % obj.pk)
    list_display = [select, "nid", "title", "price", edit, delete]
而後加上是加上了,可是沒有事件: index頁面引入jquery,加上下面一句就有了
<script>
    $("#mutPut").click(function () {
        if ($(this).prop("checked")){
            $("tbody [type='checkbox']").prop("checked", true)
        }else{
            $("tbody [type='checkbox']").prop("checked", false)
        }
    })
</script>
# 可是如今我感受,編輯刪除以及選擇的函數中的href中的url都是寫死的,其餘的模型使用不了,因此咱們是用反向解析,這樣就能夠適用全部的模型了,同時爲了每一個模型都須要這三個方法,因此將這三個方法移入modelstark中。
 # 展現編輯鏈接
    def edit_link(self, obj=None, is_header=False):
        if is_header:
            return "操做"
        name = "{}_{}_change".format(self.app_label, self.model_name)
        return mark_safe("<a href=%s>編輯</a>" % reverse(name, args=(obj.pk,)))

    # 本來都是在用戶本身的配置類中寫,可是如今寫入modelStark中是由於,是由於每一個表可能都能用到,減小代碼重複
    def delete_link(self, obj=None, is_header=False):
        if is_header:
            return "操做"
        name = "{}_{}_delete".format(self.app_label, self.model_name)
        return mark_safe("<a href=%s>刪除</a>" % reverse(name, args=(obj.pk,)))

    def select_btn(self, obj=None, is_header=None):
        if is_header:
            return mark_safe("<input id='mutPut' type='checkbox'>")
        return mark_safe("<input type='checkbox' value=%s name='_selected_action'>" % obj.pk)
    
    # 組成新的list_display
    @property
    def get_list_display(self):
        new_list_display = []
        new_list_display.extend(self.list_display)
        if not self.list_display_link:
            new_list_display.append(ModelStark.edit_link)
        new_list_display.append(ModelStark.delete_link)
        new_list_display.insert(0, ModelStark.select_btn)

        return new_list_display

    # 相應的下面的路由也須要加上別名,咱們是用app_name和mdoel——name,以及要執行的操做三者放一塊兒組成每一個模型惟一的別名
    def get_urls_2(self):
        temp = []
        model_name = self.model._meta.model_name  # 當前模型表
        app_label = self.model._meta.app_label  # 當前app

        temp.append(url(r"^add/$", self.add, name="%s_%s_add" % (app_label, model_name)))
        temp.append(url(r"^(\d+)/delete/$", self.delete, name="%s_%s_delete" % (app_label, model_name)))
        temp.append(url(r"^(\d+)/change/$", self.change, name="%s_%s_change" % (app_label, model_name)))
        temp.append(url(r"^$", self.change_list, name="%s_%s_list" % (app_label, model_name)))
        return temp
    
    def change_list(self, request):
        中list_display = self.get_list_display()  記得改
# django中的admin有list_display咱們已經實現了,如今咱們來實現list_display_link
BookStrak中加一句
class BookStark(ModelStark):
    list_display = ["nid", "title", "price"]
    list_display_link = ["nid", "title"]

# 以後修改modelStark中的    
@property
def get_list_display(self):
    new_list_display = []
    new_list_display.extend(self.list_display)
    # 若是設置了list_display_link就把編輯刪除
    if not self.list_display_link:
        new_list_display.append(ModelStark.edit_link)
        new_list_display.append(ModelStark.delete_link)
        new_list_display.insert(0, ModelStark.select_btn)

        return new_list_display
    
# 最後將chang_list中的獲取數據的代碼改動一下
data_list = self.data_list
        new_data_list = []
        # 這裏纔是咱們數據庫中的數據
        for obj in data_list:
            temp = []
            for field in self.config.get_list_display:
                if callable(field):
                    val = field(self.config, obj)
                else:
                    val = getattr(obj, field)
                    # 若是有list_display_link,就把他編程a標籤
                    if field in self.config.list_display_link:
                        val = mark_safe("<a href=%s>%s</a>" % (self.config.get_edit_url(obj), val))
                temp.append(val)
            new_data_list.append(temp)

add:

# 發現admin還有添加數據的操做,咱們也須要實現,使用modelForm
# 其餘的組件咱們慢慢實現
- ModelStark中加一個函數
    # 使用modelform組件
    def get_modelfrom_class(self):
        class ModelFormClass(ModelForm):
            class Meta:
                model = self.model
                fields = "__all__"
        if not self.get_modelfrom_class:
            return ModelFormClass
        else:
            return self.model_form_class
- 完善add添加函數的邏輯
    def add(self, request):
        form = self.model_form_class
        if request.method == "GET":
            return render(request, "add_index.html", locals())
        else:
            data = request.POST
            form = form(data=data)
            if form.is_valid():
                form.save()
                return redirect(self.get_list_url())
            else:
                return render(request, "add_index.html", locals())
# add_index.html頁面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
    <script type="application/javascript" src="/static/jquery.js"></script>
    <style>
        .form-group input{
            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;
        }
        .error{
                    color: red;
                }
    </style>
</head>
<body>
<h3>添加數據</h3>
{% include "add_conponent.html" %}
</body>
</html>
# 使用模板循環使用
<div class="container">
    <div class="row">
        <div class="col-md-6">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}:</label>
                        <div>
                            {{ field }}
                            <span class="error pull-right">
                                {{ field.errors.0 }}
                            </span>
                        </div>
                    </div>
                {% endfor %}
            <p><input type="submit" class="btn btn-primary"></p>
            </form>
        </div>
    </div>
</div>
### delete&chanage:
# 添加頁面寫完了,下來再咱們繼續完成編輯和刪除頁面的邏輯,視圖中的函數modelStark中
    def get_delete_url(self, obj):
        delete_url = "{}_{}_delete".format(self.app_label, self.model_name)
        return delete_url

    def get_add_url(self):
        add_url = "{}_{}_add".format(self.app_label, self.model_name)
        return add_url

    def get_list_url(self):
        list_url = "{}_{}_list".format(self.app_label, self.model_name)
        return list_url
    # 上面三個就是爲了獲取反向解析的name名稱
    def delete(self, request, id):
        del_obj = self.model.objects.filter(nid=id).first()
        if request.method == "GET":
            list_url = self.get_list_url()
            # 爲何要確認頁面,是由於重要數據須要二次確認,防止誤刪
            return render(request, "del_index.html", locals())
        else:
            del_obj.delete()
            return redirect(self.get_list_url())

    # 編輯頁面
    def change(self, request, id):
        form = self.get_modelfrom_class()
        obj = self.model.objects.filter(pk=id).first()
        if request.method == "GET":
            form = form(instance=obj)
            return render(request, "change_index.html", locals())
        else:
            form = form(data=request.POST, instance=obj)
            if form.is_valid():
                form.save()
                return redirect(self.get_list_url())
            else:
                return render(request, "change_index.html", locals())
# 編輯的html頁面代碼:
# 由於代碼和添加頁面相似,因此講代碼整合,弄一個子組件
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
    <script type="application/javascript" src="/static/jquery.js"></script>
    <style>
        .form-group input{
            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;
        }
        .error{
            color: red;
        }
    </style>
</head>
<body>
<h3>編輯數據</h3>
{% include "add_conponent.html" %}
</body>
</html>
# add_conponent.html
<div class="container">
    <div class="row">
        <div class="col-md-6">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}:</label>
                        <div>
                            {{ field }}
                            <span class="error pull-right">
                                {{ field.errors.0 }}
                            </span>
                        </div>
                    </div>
                {% endfor %}
            <p><input type="submit" class="btn btn-primary"></p>
            </form>
        </div>
    </div>
</div>
# 刪除頁面比較簡單delete_index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>刪除</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
    <script src="/static/jquery-3.2.1.min.js"></script>
</head>
<body>
<h3>刪除頁面</h3>
<div>
    <p>{{ del_obj }}</p>
</div>
<form action="" method="post">
    {% csrf_token %}
    <input type="submit" value="確認刪除" class="btn btn-danger">
    <a href="{% url list_url %}" class="btn btn-primary">取消</a>
</form>
</body>
</html>

分頁:

# 這是本身寫的一個分頁器類,其中咱們有添加了一些參數
# params self.request.GET信息 主要是爲了保存,請求的參數信息,好比你選了一堆的過濾條件,好比:
# http://localhost:8000/stark/app01/book/page=1&title=django,可是你的分頁器中的路徑是這樣子的 http://localhost:8000/stark/app01/book/page=1,用戶的條件title=django沒有保留,那麼問題來了,用戶發現這樣咱們次都要選擇,你這網站很差用我不玩了,因此爲了提升用戶體驗,咱們須要保留用戶的篩選過濾條件,那麼咱們就須要加一個參數params
class Pagination(object):

    def __init__(self, current_page, all_count, base_url, params, per_page_num=4, pager_count=11):
        """
        封裝分頁相關數據
        :param current_page: 當前頁
        :param all_count:    數據庫中的數據總條數
        :param per_page_num: 每頁顯示的數據條數
        :param base_url: 分頁中顯示的URL前綴
        :param pager_count:  最多顯示的頁碼個數
        :param params:  self.request.GET信息
        """

        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page <1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num
        self.base_url = base_url
        import copy
        params = copy.deepcopy(params)
        params._mutable = True
        self.params = params

        # 總頁碼
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager
        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 若是總頁碼 < 11個:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 總頁碼  > 11
        else:
            # 當前頁若是<=頁面上最多顯示11/2個頁碼
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 當前頁大於5
            else:
                # 頁碼翻到最後
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 這個地方就用到了,每次咱們把保留的參數所有拼接進入url中那麼就沒有問題了							    # self.base_url:http://localhost:8000/stark/app01/book/ 
        # self.params.urlencode(): ?page=1&title=django
        self.params["page"] = 1
        
        first_page = '<li><a href="%s?%s">首頁</a></li>' % (self.base_url, self.params.urlencode(),)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一頁</a></li>'
        else:
            self.params["page"] = self.current_page - 1
            prev_page = '<li><a href="%s?%s">上一頁</a></li>' % (self.base_url, self.params.urlencode(),)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            self.params["page"] = i
            if i == self.current_page:
                temp = '<li class="active"><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
            else:
                temp = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一頁</a></li>'
        else:
            self.params["page"] = self.current_page + 1
            next_page = '<li><a href="%s?%s">下一頁</a></li>' % (self.base_url, self.params.urlencode(),)
        page_html_list.append(next_page)
        self.params["page"] = self.all_pager
        last_page = '<li><a href="%s?%s">尾頁</a></li>' % (self.base_url, self.params.urlencode(),)
        page_html_list.append(last_page)

        return ''.join(page_html_list)

分頁組件直接在starkapp下面建一個util的包放進去就行了,目錄結構是這樣子的。
shell

而後再添加分頁的時候返現modelstark組建中change_list函數下的代碼是在是太多了,並且後面還須要繼續添加,咱們整合一下。用一個類ChangeList來封裝一下,其中的咱們主要是須要獲取表頭和表體的數據,因此將這兩個代碼各寫一個函數。同時咱們將分頁組件引入。
class ChangeList(object):
    def __init__(self, config, request, queryset):
        self.config = config
        self.request = request
        self.queryset = queryset

        from starkapp.util.paginator import Pagination
        current_page = request.GET.get("page")
        all_count = self.queryset.count()
        base_url = self.request.path_info
        params = self.request.GET
        paginator = Pagination(current_page, all_count, base_url, params)
        data_list = self.queryset[paginator.start: paginator.end]
        self.paginator = paginator
        self.data_list = data_list

    def get_header(self):
        header_list = []
        # 這裏的代碼是處理表頭數據的
        for field in self.config.get_list_display:
            if callable(field):
                val = field(self.config, is_header=True)
                header_list.append(val)
            else:
                if field == "__str__":
                    header_list.append(self.config.model._meta.model_name.upper())
                else:
                    field_obj = self.config.model._meta.get_field(field)
                    header_list.append(field_obj.verbose_name)
        return header_list

    def get_body(self):
        data_list = self.data_list
        new_data_list = []
        # 這裏纔是咱們數據庫中的數據
        for obj in data_list:
            temp = []
            for field in self.config.get_list_display:
                if callable(field):
                    val = field(self.config, obj)
                else:
                    val = getattr(obj, field)
                    # 若是有list_display_link,就把他編程a標籤
                    if field in self.config.list_display_link:
                        val = mark_safe("<a href=%s>%s</a>" % (reverse(self.config.get_edit_url(obj), args=(obj.pk,)), val))
                temp.append(val)
            new_data_list.append(temp)
        return new_data_list
# modelstark類中的change_list函數進行簡化, 這下是否是很簡潔了,並且解耦也作好了
# 首頁展現頁面
    def change_list(self, request):
        self.request = request
        queryset = self.model.objects.all()
        # 至於爲何將self傳過去,是由於changeList類中使用到了
        cl = ChangeList(self, request, queryset)
        return render(request, "index.html", locals())
# modelstark中添加
@property
def get_search_condition(self):
    from django.db.models import Q
    search_condition = Q()
    search_condition.connector = "or"  # 設置關係爲或
    if self.search_fields:  # ["title", "price"]
        key_word = self.request.GET.get("q", None)
        # 若是有值才添加,沒有紙就直接返回空的Q()
        self.key_word = key_word
        if key_word:
            for search_field in self.search_fields:
                # 由於條件設置得是or因此這裏才能夠成立,若是是and,所有遍歷加進去查詢可能會出錯
                search_condition.children.append((search_field + "__contains", key_word))
  return search_condition
# change_list改
# 首頁展現頁面
    def change_list(self, request):
        self.request = request
        add_url = self.get_add_url()
        # search模糊查詢
        queryset = self.model.objects.filter(self.get_search_condition)
        # filter模糊查詢
        queryset = queryset.filter(self.get_filter_condition)
        cl = ChangeList(self, request, queryset)
        return render(request, "index.html", locals())
# index.html改
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.4/css/bootstrap.min.css">
    <script type="application/javascript" src="/static/jquery.js"></script>
</head>
<body>
<h3>數據展現</h3>
<div class="container">
    <div class="row">
        <div class="col-md-8">
            <!-添加-->
            <a href="{% url add_url %}"><button class="btn btn-primary">添加</button></a>
            <!-搜索-->
            {% if cl.config.search_fields %}
                <div class="pull-right form-group">
                    <form action="" method="get" class="form-inline">
                        <input type="text" class="form-control" name="q" value="{{ cl.config.key_word }}">
                        <input type="submit" class="btn btn-primary" value="search">
                    </form>
                </div>
            {% endif %}
            <!-主題內容-->
            <form action="" method="post">
                {% csrf_token %}
                <!-批量操做下拉框-->
                <div>
                    <select class="form-control" name="action" id="" style="width: 200px;
                    margin: 5px 0; display: inline-block; vertical-align: -1px">
                        <option value="">------------</option>
                        {% for item in cl.handler_action %}
                            <option value="{{ item.name }}">{{ item.desc }}</option>
                        {% endfor %}
                    </select>
                    <button type="submit" class="btn btn-primary">Go</button>
                </div>

                <!-展現-->
                <table class="table table-striped table-hover">
                    <thead>
                        <tr>
                            {% for foo in cl.get_header %}
                            <td>{{ foo }}</td>
                            {% endfor %}
                        </tr>
                    </thead>
                    <tbody>
                        {% for data in cl.get_body %}
                            <tr>
                                {% for item in data %}
                                <td>{{ item }}</td>
                                {% endfor %}
                            </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </form>
        <!-分頁-->
        <nav aria-label="Page navigation" class="pull-right">
            <ul class="pagination">
                {{ cl.paginator.page_html|safe }}
            </ul>
        </nav>
        </div>
    </div>
</div>
<script>
    $("#mutPut").click(function () {
        if ($(this).prop("checked")){
            $("tbody [type='checkbox']").prop("checked", true)
        }else{
            $("tbody [type='checkbox']").prop("checked", false)
        }
    })
</script>
</body>
</html>

actions:

# modelstark中添加
@property
def get_filter_condition(self):
        from django.db.models import Q
        filter_condition = Q()
        for field, val in self.request.GET.items():
            if field in self.list_filter:
                filter_condition.children.append((field, val))
        return filter_condition
# 修改  加上name屬性
    def select_btn(self, obj=None, is_header=None):
        if is_header:
            return mark_safe("<input id='mutPut' type='checkbox'>")
        return mark_safe("<input type='checkbox' value=%s name='_selected_action'>" % obj.pk)
# 更改change_list函數,前段頁面就是上面的呢個
# 首頁展現頁面
    def change_list(self, request):
        if request.method == "POST":
            func_name = request.POST.get("action")
            # getlist多個值處理
            pk_list = request.POST.getlist("_selected_action")
            queryset = self.model.objects.filter(pk__in=pk_list)
            func = getattr(self, func_name)
            func(queryset=queryset)
        self.request = request
        add_url = self.get_add_url()
        # search模糊查詢
        queryset = self.model.objects.filter(self.get_search_condition)
        # filter模糊查詢
        queryset = queryset.filter(self.get_filter_condition)
        cl = ChangeList(self, request, queryset)
        return render(request, "index.html", locals())
# modelstark類中加上
def get_actions(self):
        temp = []
        temp.extend(self.actions)
        temp.append(ModelStark.patch_delete)
    return temp

# changeList類中加上
class ChangeList(object):
    def __init__(self, config, request, queryset):
        self.config = config
        self.request = request
        self.queryset = queryset

        from starkapp.util.paginator import Pagination
        current_page = request.GET.get("page")
        all_count = self.queryset.count()
        base_url = self.request.path_info
        params = self.request.GET
        paginator = Pagination(current_page, all_count, base_url, params)
        data_list = self.queryset[paginator.start: paginator.end]
        self.paginator = paginator
        self.data_list = data_list
        # actions 批量操做的動做
        self.actions = self.config.get_actions()  # 這裏的獲取actions是modelstark類中的
    def handler_action(self):
        temp = []
        for action in self.actions:
            temp.append({"name": action.__name__, "desc": action.desc if getattr(action, "desc", None) else action.__name__})
        return temp
### filter: 難
補充必看:
# self.config.model._meta.get_field(filter_field_name)給你看一下拿的是什麼吧(下面第一張圖),能夠看到拿到的關聯的對多字段的實例,其中個人django是2.0版本以上的,你能夠經過related_model拿到對應的模型,以後經過模型管理器拿到對應的數據。(2.0版本已一下的試一試rel.to.objects.all(),這個我也沒試,拿不到本身搜一搜吧。)
filter_field_obj = self.config.model._meta.get_field(filter_field_name)
# filter_field = FilterField(filter_field_name, filter_field_obj, self)
print(type(filter_field_obj))
print(filter_field_obj.related_model.objects.all())
from django.db.models.fields.related import ManyToManyField
from django.db.models.fields.related import ForeignKey
# 更愛changeList類
class ChangeList(object):
    def __init__(self, config, request, queryset):
        self.config = config
        self.request = request
        self.queryset = queryset

        from starkapp.util.paginator import Pagination
        current_page = request.GET.get("page")
        all_count = self.queryset.count()
        base_url = self.request.path_info
        params = self.request.GET
        paginator = Pagination(current_page, all_count, base_url, params)
        data_list = self.queryset[paginator.start: paginator.end]
        self.paginator = paginator
        self.data_list = data_list
        # actions 批量操做的動做
        self.actions = self.config.get_actions()
        # filter  過濾的字段
        self.list_filter = self.config.list_filter

    def get_filter_link_tag(self):
        link_list = {}
        data = self.request.GET
        import copy
        params = copy.deepcopy(data)
        for filter_field_name in self.config.list_filter:
            # 爲何放裏面而不是放外面
            data = self.request.GET
            import copy
            params = copy.deepcopy(data)

            current_id = self.request.GET.get(filter_field_name, 0)
            filter_field_obj = self.config.model._meta.get_field(filter_field_name)
            # filter_field = FilterField(filter_field_name, filter_field_obj, self)
            if isinstance(filter_field_obj, ManyToManyField) or isinstance(filter_field_obj, ForeignKey):
                data_list = filter_field_obj.related_model.objects.all()
            else:
                data_list = self.config.model.objects.values_list("pk", filter_field_name)
            temp = []
            # 處理所有標籤
            if params.get(filter_field_name, None):
                del params[filter_field_name]
                temp.append("<a href='?%s'>所有</a>" % (params.urlencode()))
            else:
                temp.append("<a class='active' href='?%s'>所有</a>" % (params.urlencode()))
            # 處理數據標籤
            for obj in data_list:
                if isinstance(filter_field_obj, ManyToManyField) or isinstance(filter_field_obj, ForeignKey):
                    pk, text = obj.pk, str(obj)
                    params[filter_field_name] = pk
                else:
                    pk, text = obj
                    params[filter_field_name] = text
                _url = params.urlencode()
                if current_id == str(pk) or current_id == text:
                    link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)  # %s/%s/
                else:
                    link_tag = "<a href='?%s'>%s</a>" % (_url, text)  # %s/%s/
                temp.append(link_tag)
            link_list[filter_field_name] = temp
        # 在下面我本身封裝的類,能夠本身玩玩,只須要將這個函數中的代碼所有註釋,保留這兩句就能夠了
        # ff = FilterField(self.config, self.request)
        # link_list = ff.get_filter_link()
        return link_list
# 因爲發現上面的呢個函數是在太長,因此我本身試試能不能將它分紅一個類,能夠練習一下,其實咱們寫代碼的時候固然是都寫在一個函數中看起來方便,可是爲了裝逼吧,因此將每一個功能分開,讓每一個函數變得簡單一些,我的感受這種方式其實不方便閱讀代碼。
# 爲每個過濾的字段封裝成總體類
class FilterField(object):
    def __init__(self, config, request):
        self.config = config
        self.request = request

    def get_data(self):
        if isinstance(self.filter_field_obj, ForeignKey) or isinstance(self.filter_field_obj, ManyToManyField):
            return self.filter_field_obj.related_model.objects.all()
        elif self.filter_field_obj.choices:
            return self.filter_field_obj.choices
        else:
            return self.config.model.objects.values_list("pk", self.filter_field_name)

    def get_params(self):
        data = self.request.GET
        import copy
        params = copy.deepcopy(data)
        return params

    def get_filter_link(self):
        link_list = {}
        for filter_field_name in self.config.list_filter:
            self.filter_field_name = filter_field_name
            # 爲何放裏面而不是放外面
            params = self.get_params()
            current_id = self.get_current_id()
            self.get_filter_field_obj()
            temp = self.get_link_list(params, current_id)
            link_list[filter_field_name] = temp
        return link_list

    def get_current_id(self):
        current_id = self.request.GET.get(self.filter_field_name, 0)
        return current_id

    def get_filter_field_obj(self):
        filter_field_obj = self.config.model._meta.get_field(self.filter_field_name)
        self.filter_field_obj = filter_field_obj

    def get_link_list(self, params, current_id):
        data_list = self.get_data()
        temp = []
        temp = self.deal_all_tag(params, temp)
        temp = self.deal_data_tag(params, data_list, current_id, temp)
        return temp

    def deal_data_tag(self, params, data_list, current_id, temp):
        for obj in data_list:
            pk, text, params = self.get_pk_text(params, obj)
            _url = params.urlencode()
            if current_id == str(pk) or current_id == text:
                link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)  # %s/%s/
            else:
                link_tag = "<a href='?%s'>%s</a>" % (_url, text)  # %s/%s/
            temp.append(link_tag)
        return temp

    def get_pk_text(self, params, obj):
        if isinstance(self.filter_field_obj, ManyToManyField) or isinstance(self.filter_field_obj, ForeignKey):
            pk, text = obj.pk, str(obj)
            params[self.filter_field_name] = pk
        else:
            pk, text = obj
            params[self.filter_field_name] = text
        return pk, text, params

    def deal_all_tag(self, params, temp):
        if params.get(self.filter_field_name, None):
            del params[self.filter_field_name]
            temp.append("<a href='?%s'>所有</a>" % (params.urlencode()))
        else:
            temp.append("<a class='active' href='?%s'>所有</a>" % (params.urlencode()))
        return temp

pop頁面:

1.若是在一對多和多對多後面顯示加號
2. +對應跳轉路由
3. 保存記錄的同時,將原頁面中的下拉菜單中天驕新紀錄
知識點:
window.opener當前窗口的打開窗口
最後一個也就是下面這幅圖中的+號的實現,點擊加號直接跳到建立頁面,其中的難點就是上面列出的三條,咱們一個一個解決。
# 1.如何在一對多和多對多後面顯示加號(咱們發先這個是在添加頁面的時候才顯示的因此咱們找到add函數),跳到add_index.html頁面發現。全部的字段都是經過form組件渲染出來的,那麼有沒有什麼解決的辦法。先來一個簡易版的直接加。
```python # 這個時候發現,每一個後面都有咋辦,咱們只想在多對因此及一對多字段添加,這個時候我想到了model,可是發現好想聯繫不上。而後忽然想到使用form組件渲染的,咱們打印一下每一個字段的類型,發現是一個boundField類型。進入看看 ```
它有這麼多的屬性,咱們一個一個打印試試,最後發現field就是咱們的字段。
``` 這個時候操做性就來了,咱們能夠判斷這個字段是否是,一對多的類型(多對多繼承一對多),而後給他加一個屬性。 ```
# modelstark的add函數中
    def add(self, request):
        modelform = self.get_modelfrom_class()
        from django.forms.boundfield import BoundField
        form = modelform()
        for field in form:
            print(type(field.field))
            if isinstance(field.field, ModelChoiceField):
                field.is_pop = True
                related_model_name = field.field.queryset.model._meta.model_name
                related_app_name = field.field.queryset.model._meta.app_label
                _url = reverse("{}_{}_add".format(related_app_name, related_model_name)) + \
                       "?pop_res_id=id_{}".format(field.name)
                field.url = _url
        if request.method == "GET":
            return render(request, "add_index.html", locals())
        else:
            data = request.POST
            form = modelform(data=data)
            if form.is_valid():
                obj = form.save()
                pop_res_id = request.GET.get("pop_res_id")
                if pop_res_id:
                    res = {"pk": obj.pk, "text": str(obj), "pop_res_id": pop_res_id}
                    return render(request, "pop.html", {"res": res})
                else:
                    return redirect(self.get_list_url())
            else:
                return render(request, "add_index.html", locals())
<div class="container">
    <div class="row">
        <div class="col-md-6 col-xs-6 col-md-offset-3">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}:</label>
                        <div style="position: relative">
                            {{ field }}
                            <span class="error pull-right">
                                {{ field.errors.0 }}
                            </span>
                            {% if field.is_pop %}  # 而後這裏加上判斷就能夠了
                                <a onclick="pop('{{ field.url }}')" style="color: blue; font-size: 30px; position: absolute;top: -10px;">+</a>
                            {% endif %}
                        </div>
                    </div>
                {% endfor %}
            <p><input type="submit" class="btn btn-primary"></p>
            </form>
        </div>
    </div>
</div>
2. +對應跳轉路由
加號對應的跳轉路由咱們能夠直接看django自帶的admin路由是什麼樣?
http://localhost:8000/stark/app01/publish/add/?pop_res_id=id_publish可是是動態的因此咱們想第一步中。給每一個外鍵屬性的字段加上一個url
         for field in form:
            print(type(field.field))
            if isinstance(field.field, ModelChoiceField):
                field.is_pop = True
                related_model_name = field.field.queryset.model._meta.model_name
                related_app_name = field.field.queryset.model._meta.app_label
                # 就是這句
                _url = reverse("{}_{}_add".format(related_app_name, related_model_name)) + \
                       "?pop_res_id=id_{}".format(field.name)
                field.url = _url
這個時候還要解決的時候打開一個新頁面
添加到添加頁面的組件中,也就是下面的圖。
<script>
    function pop(url) {
        window.open(url,"","width=600,height=400,top=100,left=200")
    }
</script>
可是又如何作到,在點擊保存以後關閉頁面的呢。看到下面紅框中的判斷沒有,返回了一個pop.html頁面。
3. 保存記錄的同時,將原頁面中的下拉菜單中添加新紀錄
# pop.html頁面就這幾句代碼,加註釋
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body> 
<script>
	# 這句話是將,數據傳入打開它的父window,pop_response是父window的一個函數,本身定義就行了。
    window.opener.pop_response('{{ res.pk }}', '{{ res.text }}', '{{ res.pop_res_id }}');
    # 他纔是關閉窗口,直接在js中將窗口關閉
    window.close()
</script>
</body>
</html>
# add_index.html若是處理呢?看到裏面的函數你就明白了,就是生成一個option標籤,綁定數據和屬性給,插入下拉框中。

而後到這裏就完了。不習慣使用git的可是又怕代碼沒地方保存哎?

地址:https://github.com/maxhope8/stark.git(我直接把項目代碼弄上去了,可是外面的目錄沒傳上去)

相關文章
相關標籤/搜索