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的嵌套 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中取,能夠理解爲緩存),每次導入到的都是同一個對象,(內存地址相同)。
# 使用 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)) ]
查看頁面: 表頭 表數據 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了
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)
# 發現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>
# 添加頁面寫完了,下來再咱們繼續完成編輯和刪除頁面的邏輯,視圖中的函數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>
# 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
補充必看: # 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
1.若是在一對多和多對多後面顯示加號 2. +對應跳轉路由 3. 保存記錄的同時,將原頁面中的下拉菜單中天驕新紀錄 知識點: window.opener當前窗口的打開窗口
最後一個也就是下面這幅圖中的+號的實現,點擊加號直接跳到建立頁面,其中的難點就是上面列出的三條,咱們一個一個解決。
# 1.如何在一對多和多對多後面顯示加號(咱們發先這個是在添加頁面的時候才顯示的因此咱們找到add函數),跳到add_index.html頁面發現。全部的字段都是經過form組件渲染出來的,那麼有沒有什麼解決的辦法。先來一個簡易版的直接加。
它有這麼多的屬性,咱們一個一個打印試試,最後發現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(我直接把項目代碼弄上去了,可是外面的目錄沒傳上去)