10.20stark元件已經完工
阿新 • • 發佈:2018-11-08
2018-10-20 19:37:31
stark元件已經做完!基本上和Django的admin一樣!
放上github連線:https://github.com/TrueNewBee/pythonDemo/blob/master/stark_demo.rar
放上正版的stark元件: https://github.com/TrueNewBee/pythonDemo/blob/master/stark.zip
stark元件已經完成,明天整理Django部落格!
後面的就是crm專案啦!
越努力,與幸運!永遠不要高估自己!
知識點 test.py
子類繼承父類 , 然後和父類的型別一樣 isinstance()
modelForm 把 model.Model 轉換成 form.Form
ChoiceFiled ModelChoiceFiled(ChoiceFiled) ---- select(單選) MultiModelChoiceFiled (ModelChoiceFiled)----select(多選) class Book(model.Model): title = models.CharField(max_length=32) price = models.IntegerField() publish=model.Foreignkey("Publish") authors=model.ManyToMany("Author") from django.forms import ModelForm class BookForm(ModelForm): class Meta: model=Book fields="__all__" from django import forms class BookForm(forms.Form): title=forms.CharField(max_length=32) price=forms.IntegerField() publish = forms.ModelChoiceFiled("Publish") authors = forms.ModelMultipleChoiceField("Author") form=BookForm() for i in form: if isinstance(i,ModelChoiceFiled): pass '''
pop功能:
1 如何在一對多和多對多欄位後渲染 +
2 +對應的跳轉路徑
上述兩步實現邏輯
def add_view(self, request): # 例項化form類物件 ModelFormDemo = self.get_modelform_class() form = ModelFormDemo() for bfield in form: # 這個可以看原始碼,然後類呼叫所需屬性 from django.forms.boundfield import BoundField print(bfield.field) # 欄位物件 print("name",bfield.name) # 欄位名(字串) print(type(bfield.field)) # 欄位型別 # 看原始碼可得 多對多和一對多是ModelChoiceFiled的型別 from django.forms.models import ModelChoiceField if isinstance(bfield.field, ModelChoiceField): # 增加一個屬性,傳給前端做判斷,是否顯示這個 +按鈕 bfield.is_pop = True print("=======>", bfield.field.queryset.model) # 一對多或者多對多欄位的關聯模型表 # 通過下面兩個方法,找到表和app名字 related_model_name = bfield.field.queryset.model._meta.model_name related_app_label = bfield.field.queryset.model._meta.app_label # 拼接url傳給前端 _url = reverse("%s_%s_add" % (related_app_label, related_model_name)) # 建立一個新的屬性url 給前端呼叫 bfield.url = _url+"?pop_res_id=id_%s" % bfield.name
上面是get請求,下面是這個函式post請求的邏輯
# POST請求 if request.method == "POST": form = ModelFormDemo(request.POST) if form.is_valid(): obj = form.save() # 儲存資料,並返回一個obj pop_res_id = request.GET.get("pop_res_id") # 判斷是點選+按鈕帶引數訪問,還是通過add頁面直接訪問的 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()) return render(request, "add_view.html", locals())
3 儲存新增記錄同時,將原頁面的對應的下拉選單中新增該記錄
通過js方法!
from.html
<script> // 開啟一個新的視窗新增資料 function pop(url) { window.open(url,"","width=600,height=400,top=100,left=100") } </script>
pop.html (作用就是一個橋樑,傳給父類頁面相應引數,然後關閉)
<script> // 呼叫父類的方法,並把相應引數傳給父類 window.opener.pop_response('{{ res.pk }}',"{{ res.text }}",'{{ res.pop_res_id }}') // 傳過去後直接關閉頁面,由於響應很快,時間可以忽略 window.close() </script>
add.html(接受子頁面傳入的引數,然後通過jQuery方法把傳入的資料直接新增到select框中預設選中)
<script> function pop_response (pk,text,id) { console.log(pk,text,id); // 接受子頁面傳入的引數,並通過jQuery,新增相應的標籤和值 // 選擇哪一個select標籤 // option的文字值和value值 var $option=$('<option>'); // <option></option> $option.html(text); // <option>南京出版社</option> $option.val(pk); // <option value=111>南京出版社</option> $option.attr("selected","selected") ; // <option value=111>南京出版社</option> $("#"+id).append($option) } </script>
然後附上這個stark元件的主要程式碼!主要看邏輯實現!!想看原始碼就去github下一下!原始碼裡面都註釋好啦!!!!!
stark/stark.py
from django.conf.urls import url from django.shortcuts import render, redirect from django.urls import reverse from django.utils.safestring import mark_safe from django.forms import ModelForm from stark.utils.page import Pagination from django.db.models import Q from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ManyToManyField import copy
"""
Stark元件!
2018-10-20 19:39:08
功能簡介:
1. 使用方法和Django的admin一樣,需要在strak裡面註冊,詳情看app01/stark.py
2. 實現了對不同表的url的各級分發
3. 使用者可以自定義配置表的現實資訊 詳情可以看app01/stark.py
4. 實現了對錶新增資料pop的功能!
5. 最強大就是,你可以拿去直接用,和admin一樣,而且不需要超級使用者!
2333333333333333333333333333333
"""
class ShowList(object): # 這是一個配置類的物件初始化 def __init__(self, config, data_list, request): self.config = config self.data_list = data_list self.request = request # 分頁 data_count = self.data_list.count() current_page = int(self.request.GET.get("page", 1)) base_path = self.request.path self.pagination = Pagination(current_page, data_count, base_path, self.request.GET, per_page_num=3, pager_count=11,) self.page_data = self.data_list[self.pagination.start:self.pagination.end] # actions 獲取actions這個配置類的列表 self.actions = self.config.actions # [patch_init,] # 處理filter欄位連線 def get_filter_linktags(self): """用了兩次for迴圈,在演算法上有點綴餘!不過可以用類或函式封裝只是懶-.-能力欠缺!""" print("list_filter:", self.config.list_filter) link_dic = {} for filter_field in self.config.list_filter: # ["title","publish","authors",] params = copy.deepcopy(self.request.GET) cid = self.request.GET.get(filter_field, 0) print("filter_field", filter_field) # "publish" # 通過_meta.get_field方法,獲取該表名物件 filter_field_obj = self.config.model._meta.get_field(filter_field) print("filter_field_obj", filter_field_obj) print(type(filter_field_obj)) # print("rel...",filter_field_obj.rel.to.objects.all()) # 判斷一下 如果是多對多或一對多型別的 if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField): # 拿到表的所有QuerySet物件 data_list = filter_field_obj.rel.to.objects.all() # 【publish1,publish2...】 else: # 這個則是自定義過濾欄位 data_list = self.config.model.objects.all().values("pk", filter_field) print("data_list", data_list) temp = [] # 處理 全部標籤 if params.get(filter_field): # 如果url如果存在引數 則del del params[filter_field] temp.append("<a href='?%s'>全部</a>" % params.urlencode()) else: # 反之加上class 增加顏色 temp.append("<a class='active' href='#'>全部</a>") # 處理 資料標籤 for obj in data_list: # 迴圈列表中每個QuerySet的物件然後取到相應的值 if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField): pk = obj.pk text = str(obj) params[filter_field] = pk else: # data_list= [{"pk":1,"title":"go"},....] pk = obj.get("pk") text = obj.get(filter_field) params[filter_field] = text _url = params.urlencode() if cid == str(pk) or cid == text: link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text) else: link_tag = "<a href='?%s'>%s</a>" % (_url, text) temp.append(link_tag) link_dic[filter_field] = temp return link_dic # 獲取下拉框 使用者配置的action_list def get_action_list(self): temp = [] for action in self.actions: # [{"name":""patch_init,"desc":"批量初始化"}] temp.append({ "name": action.__name__, "desc": action.short_description }) return temp # 構建表頭 def get_header(self): header_list = [] print("header", self.config.new_list_play()) # [checkbox,"pk","name","age",edit ,deletes] 【checkbox ,"__str__", edit ,deletes】 for field in self.config.new_list_play(): if callable(field): # header_list.append(field.__name__) val = field(self.config, header=True) header_list.append(val) else: if field == "__str__": header_list.append(self.config.model._meta.model_name.upper()) else: # header_list.append(field) val = self.config.model._meta.get_field(field).verbose_name header_list.append(val) return header_list # 構建表單資料 def get_body(self): # 構建表單資料 new_data_list = [] for obj in self.page_data: temp = [] for filed in self.config.new_list_play(): # ["__str__",] ["pk","name","age",edit] if callable(filed): val = filed(self.config, obj) else: # 這裡捕獲一下異常,因為預設的list_play裡面有__str__ 直接找不到該欄位 # 所以直接用getattr方法就行啦! try: field_obj = self.config.model._meta.get_field(filed) if isinstance(field_obj, ManyToManyField): # getattr()僅取到Object, 然後.all() 則可以取到物件 ret = getattr(obj, filed).all() t = [] for obj in ret: t.append(str(obj)) val = ",".join(t) else: val = getattr(obj, filed) if filed in self.config.list_display_links: # "app01/userinfo/(\d+)/change" _url = self.config.get_change_url(obj) val = mark_safe("<a href='%s'>%s</a>" % (_url, val)) except Exception as e: val = getattr(obj, filed) temp.append(val) new_data_list.append(temp) return new_data_list class ModelStark(object): # 預設的list_play[] list_display = ["__str__", ] list_display_links = [] modelform_class = None search_fields = [] actions = [] list_filter = [] def __init__(self, model, site): self.model = model self.site = site # 預設的批量刪除action def patch_delete(self, request, queryset): queryset.delete() patch_delete.short_description = "批量刪除" # 配置表頭: 刪除 編輯,複選框 def edit(self, obj=None, header=False): """編輯""" if header: return "操作" # return mark_safe("<a href='%s/change'>編輯</a>"%obj.pk) _url = self.get_change_url(obj) return mark_safe("<a href='%s'>編輯</a>" % _url) def deletes(self, obj=None, header=False): """刪除""" if header: return "操作" # return mark_safe("<a href='%s/change'>編輯</a>"%obj.pk) _url = self.get_delete_url(obj) return mark_safe("<a href='%s'>刪除</a>" % _url) def checkbox(self, obj=None, header=False): """複選框""" if header: return mark_safe('<input id="choice" type="checkbox">') # value的值不能寫死, return mark_safe('<input class="choice_item" type="checkbox" name="selected_pk" value="%s">' % obj.pk) # 獲取配置類的表頭資訊 def get_modelform_class(self): """獲取表的配置類""" if not self.modelform_class: # 如果表的配置類為空 class ModelFormDemo(ModelForm): class Meta: model = self.model fields = "__all__" labels = { "" } return ModelFormDemo else: return self.modelform_class # 新增的檢視函式 def add_view(self, request): # 例項化form類物件 ModelFormDemo = self.get_modelform_class() form = ModelFormDemo() for bfield in form: # 這個可以看原始碼,然後類呼叫所需屬性 from django.forms.boundfield import BoundField print(bfield.field) # 欄位物件 print("name",bfield.name) # 欄位名(字串) print(type(bfield.field)) # 欄位型別 # 看原始碼可得 多對多和一對多是ModelChoiceFiled的型別 from django.forms.models import ModelChoiceField if isinstance(bfield.field, ModelChoiceField): # 增加一個屬性,傳給前端做判斷,是否顯示這個 +按鈕 bfield.is_pop = True print("=======>", bfield.field.queryset.model) # 一對多或者多對多欄位的關聯模型表 # 通過下面兩個方法,找到表和app名字 related_model_name = bfield.field.queryset.model._meta.model_name related_app_label = bfield.field.queryset.model._meta.app_label # 拼接url傳給前端 _url = reverse("%s_%s_add" % (related_app_label, related_model_name)) # 建立一個新的屬性url 給前端呼叫 bfield.url = _url+"?pop_res_id=id_%s" % bfield.name # POST請求 if request.method == "POST": form = ModelFormDemo(request.POST) if form.is_valid(): obj = form.save() # 儲存資料,並返回一個obj pop_res_id = request.GET.get("pop_res_id") # 判斷是點選+按鈕帶引數訪問,還是通過add頁面直接訪問的 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()) return render(request, "add_view.html", locals()) # 刪除的檢視函式 def delete_view(self, request, id): url = self.get_list_url() if request.method == "POST": self.model.objects.filter(pk=id).delete() return redirect(url) return render(request, "delete_view.html", locals()) # 編輯的檢視函式 def change_view(self, request, id): ModelFormDemo = self.get_modelform_class() edit_obj = self.model.objects.filter(pk=id).first() if request.method == "POST": form = ModelFormDemo(request.POST, instance=edit_obj) if form.is_valid(): form.save() return redirect(self.get_list_url()) return render(request, "add_view.html", locals()) form = ModelFormDemo(instance=edit_obj) return render(request, "change_view.html", locals()) # 搜尋的檢視函式 def get_serach_conditon(self, request): key_word = request.GET.get("q", "") self.key_word = key_word search_connection = Q() if key_word: # self.search_fields # ["title","price"] search_connection.connector = "or" # 用Q的這種新增方法可以新增字串 for search_field in self.search_fields: # search_field+"__contains" ----> title__contains="o" 就是title欄位裡面包含字母o的 search_connection.children.append((search_field + "__contains", key_word)) return search_connection # 過濾filter的檢視函式 def get_filter_condition(self, request): filter_condition = Q() for filter_field, val in request.GET.items(): if filter_field in self.list_filter: filter_condition.children.append((filter_field, val)) return filter_condition # 檢視的檢視函式 def list_view(self, request): if request.method == "POST": # action print("POST:", request.POST) action = request.POST.get("action") # patch_init selected_pk = request.POST.getlist("selected_pk") action_func = getattr(self, action) queryset = self.model.objects.filter(pk__in=selected_pk) ret = action_func(request, queryset) # return ret # 獲取search的Q物件 search_connection = self.get_serach_conditon(request) # 獲取filter構建Q物件 filter_condition = self.get_filter_condition(request) # 篩選獲取當前表所有資料 data_list = self.model.objects.all().filter(search_connection).filter(filter_condition) # 【obj1,obj2,....】 # 按這ShowList展示頁面 showlist = ShowList(self, data_list, request) # 構建一個檢視URL add_url = self.get_add_url() return render(request, "list_view.html", locals()) # 獲取使用者配置類裡面的list_play[] def new_list_play(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) if not self.list_display_links: temp.append(ModelStark.edit) temp.append(ModelStark.deletes) return temp # 獲取使用者配置類裡面的actions 這個列表 def new_actions(self): temp=[] temp.append(ModelStark.patch_delete) temp.extend(self.actions) return temp """把url進行反向解析,解耦到各自的函式中,函式中直接返回了對應的url""" # 獲取修改頁面的url def get_change_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) return _url # 獲刪除改頁面的url def get_delete_url(self, obj): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,)) return _url # 獲取新增頁面的url def get_add_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_add" % (app_label, model_name)) return _url # 獲取檢視頁面的url 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 # 二級url分發函式 def get_urls_2(self): temp = [] model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp.append(url(r"^add/", self.add_view, name="%s_%s_add" % (app_label, model_name))) temp.append(url(r"^(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (app_label, model_name))) temp.append(url(r"^(\d+)/change/", self.change_view, name="%s_%s_change" % (app_label, model_name))) temp.append(url(r"^$", self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property def urls_2(self): print(self.model) return self.get_urls_2(), None, None class StarkSite(object): def __init__(self): self._registry = {} def register(self, model, stark_class=None): if not stark_class: stark_class = ModelStark self._registry[model] = stark_class(model, self) # 一級分發url函式 def get_urls(self): temp = [] for model, stark_class_obj in self._registry.items(): model_name = model._meta.model_name app_label = model._meta.app_label # 分發增刪改查 temp.append(url(r"^%s/%s/" % (app_label, model_name), stark_class_obj.urls_2)) ''' url(r"^app01/userinfo/",UserConfig(Userinfo).urls_2), url(r"^app01/book/",ModelStark(Book).urls_2), ''' return temp @property def urls(self): return self.get_urls(), None, None # 建立stark的一個單例物件 site = StarkSite()
app01/stark.py
from stark.service.stark import site,ModelStark from django.shortcuts import HttpResponse from .models import * from django.forms import ModelForm class BookModelForm(ModelForm): class Meta: model = Book fields = "__all__" labels = { "title": "書籍名稱", "price": "價格" } class BookConfig(ModelStark): # 自定義展示列表 list_display = ["title", "price", "publishDate", "publish", "authors"] # 自定義設定欄位為連線 list_display_links = ["title"] modelform_class = BookModelForm # 自定義搜尋欄位 search_fields = ["title", "price"] def patch_init(self, request, queryset): print(queryset) queryset.update(price=123) return HttpResponse("批量初始化OK") patch_init.short_description = "批量初始化" # 自定義處理函式 actions = [patch_init] # 自定義篩選欄位 list_filter=["title","publish","authors",] site.register(Book, BookConfig) site.register(Publish) site.register(Author) site.register(AuthorDetail)
app01/models.py
from django.db import models # Create your models here. from django.db import models # Create your models here. from django.db import models # Create your models here. class Author(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() # 與AuthorDetail建立一對一的關係 authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) def __str__(self): return self.name class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) def __str__(self): return self.telephone 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) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) # 與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