一、相關知識點回顧 1、什麼是反射? 可以用字元串的方式去訪問對象的屬性 2、反射有四種方法? 3、創建ModelForm的兩種方式 二、具體流程以及相關知識點 1、路由系統 讓url對應視圖,這時的視圖可以是一個元組,元組的裡面放三個參數,第一個是個列表,這兩種表示方式都是一樣的,用那種都行 na ...
一、相關知識點回顧
1、什麼是反射?
可以用字元串的方式去訪問對象的屬性
2、反射有四種方法?
hasattr(object,name):判斷一個對象是不是有name屬性或者方法
getattr:獲取對象的屬性或者方法, 需要註意的是,如果返回的是對象的方法,返回出來的是對象的記憶體地址, 如果需要運行這個方法,可以在後面添加一對()
setattr:給對象的屬性賦值,如果屬性不存在,先創建後賦值
delattr:刪除該對象指定的一個屬性
3、創建ModelForm的兩種方式
# 方式一定義ModelForm class TestModelForm(ModelForm): class Meta: model = self.model_class fields = "__all__" 方式二定義 Meta = type("Meta", (object,), {"model": self.model_class, "fields": "__all__"}) TestModelForm = type("TestModelForm", (ModelForm,), {"Meta": Meta})
二、具體流程以及相關知識點
1、路由系統
讓url對應視圖,這時的視圖可以是一個元組,元組的裡面放三個參數,第一個是個列表,這兩種表示方式都是一樣的,用那種都行
namespace的用法如下
v => ([],None,None) namespace(是第三個參數),用於區分相同name的url,通過namespace為url添加一個首碼
如圖:
namespace
流程:
首先創建三個應用
app01
app02
strak
1、一旦運行的時候都會去執行admin.py ,現在我們讓你開始執行stark.py 文件,加上下麵的這些(參考的是admin的源碼,在每一個admin文件的裡面點擊進入)
from django.utils.module_loading import autodiscover_modules class StarkConfig(AppConfig): name = 'stark' #應用名稱 def ready(self): autodiscover_modules('stark') #在應用中創建的py文件和這個名字一樣
切記一定要在sessings中配置一下:
'app02.apps.App02Config',
'stark.apps.StarkConfig',
2、然後再每個應用下麵也創建一個stark.py的文件
這樣就像admin一樣了,就開始執行site.redister了
3、這時候還沒有site呢,需要自己實例化一個site
在應用下創建一個service的文件夾,再創建一個v1.py文件,在裡面寫代碼
4、在stark裡面註冊
需要註意的是:
v1.site.register(models.UserInfo) -> 執行 StackConfig的 changelist_view方法/add_view.... class UserInfoConfig(v1.StarkConfig): def changelist_view(self,request,*args,**kwargs): return HttpResponse('你猜我是誰?') v1.site.register(models.UserInfo,UserInfoConfig) ---> 優先查看自己config中是否存在方法,不存在則執行基類 StackConfig的 changelist_view方法/add_view....
如圖:
5、註冊完成之後走urls。仿照admin的urls.。。。v1.site.urls
6、完了在v1.py中寫代碼
class StarkSite(object): def __init__(self): self._registry ={} #放置處理請求對應關係 ''' _registry = { models.Role: StarkConfig(models.Role,v1.site), models.UserInfo: StarkConfig(models.UserInfo,v1.site) models.UserType: StarkConfig(models.UserType,v1.site) models.Article: StarkConfig(models.Article,v1.site) } ''' def register(self,model_class,stark_config_class=None): if not stark_config_class: '''stark_config_class是類對象,如果沒有這個類就重新賦值,去執行StarkConfig''' stark_config_class = StarkConfig self._registry[model_class] = stark_config_class(model_class,self) #如果用戶自己傳進去類了,就用自己的,自己的需要繼承StarkConfig。如果自己沒有就找基類的,自己有就用自己的 def get_urls(self): url_list = [] for model_calss,stark_config_obj in self._registry.items(): app_name = model_calss._meta.app_label#應用名稱 model_name = model_calss._meta.model_name#表的名稱 cur_url = url(r'^{0}/{1}/'.format(app_name,model_name),(stark_config_obj.urls,None,None)) #這是的stark_config_obj是上面StarkConfig的實例對象。stark_config_obj.urls就會去找上面類的urls url_list.append(cur_url) return url_list @property #吧方法當屬性來用 def urls(self): return (self.get_urls(),None,'stark') #第三個參數是namesapce
動態生成類名和應用名圖示:
在v1裡面有兩個類
- StarkConfig,用於為每一個類生成URL對應關係,並編寫視圖函數處理用戶請求。 [ ^$ -> self.changelist_view ^add/$ -> self.add_view ^delete/$ -> self.delete_view ^change/$ -> self.dchange_view ] - StarkSite 是一個容器,用於放置處理請求對應關係。 { model.UserInfo: UserInfoConfig(model.UserInfo,self), model.UserType: StarkConfig(model.UserType,self), }
具體來寫:當然前面的StarkSite類已經寫過了,那我們來看看StarkConfig這個類
以下的功能實現都是在StarkConfig這個類裡面的
功能實現一:展示頁面,讓頁面上動態顯示表格
class StarkConfig(object): list_display = [] def __init__(self, model_class, site): self.model_class = model_class self.site = site def change_list_views(self,request,*args,**kwargs): data_list = self.model_class.objects.all() '''展示th的信息''' head_list = [] for field_name in self.list_display: if isinstance(field_name,str): verbose_name = self.model_class._meta.get_field(field_name).verbose_name else: verbose_name = field_name(self,is_header=True) # yield {"verbose_name":verbose_name} head_list.append(verbose_name) '''展示td的信息''' # [["id","name"],["id","name"],["id","name"],] new_data_list = [] for row in data_list: temp = [] for field_name in self.list_display: if isinstance(field_name,str): #如果是字元串類型的就是用getattr的方式,因為對象不能.字元串 val = getattr(row,field_name) else: val = field_name(self,row) temp.append(val) # yield {"val":val} new_data_list.append(temp) return render(request, "stark/change_list_views.html", {"data_list":new_data_list,"head_list":head_list})
在stark.py 中
strak.py print("sssss6666666") from app01 import models from stark.service import v1 from django.utils.safestring import mark_safe class UserInfoConfig(v1.StarkConfig): def checkbox(self,obj=None,is_header=False): if is_header: return "選擇" return mark_safe("<input type='checkbox' name='zzzz' value='%s'/>"%obj.id) def edit(self,obj=None,is_header=False): if is_header: return "操作" return mark_safe("<a href='edit/%s'>編輯</a>"%obj.id) list_display = [checkbox,"id","name",edit] v1.site.register(models.UserInfo,UserInfoConfig) v1.site.register(models.Role,UserInfoConfig) v1.site.register(models.UserType)
功能二:當然我們現在把按鈕是自己定製的,有與編輯,刪除等都是我們很常用的,所以我們可以搞成預設的。在StarkConfig類里寫
# ===============吧刪除,編輯,覆選框設置預設按鈕================== def checkbox(self,obj=None,is_header=False): if is_header: return "選擇" return mark_safe("<input type='checkbox' name='zzzz' value='%s'/>"%obj.id) def edit(self,obj=None,is_header=False): if is_header: return "操作" return mark_safe("<a href='%s'>編輯</a>"%(self.get_change_url(obj.id),)) def delete(self,obj=None,is_header=False): if is_header: return "刪除" #動態跳轉路徑,反向解析,因為每次都要用到,我們可以吧它封裝到一個函數 return mark_safe("<a href='%s'>刪除</a>"%self.get_delete_url(obj.id)) #做預設的刪除和編輯。對這個方法重寫的時候可以吧許可權管理加進去, # 當它都什麼許可權的時候顯示什麼按鈕。 def get_list_display(self): data = [] if self.list_display: data.extend(self.list_display) #在新的列表裡面吧list_display擴展進來 data.append(StarkConfig.edit) #因為是預設的,直接在類裡面去調用edit data.append(StarkConfig.delete) data.insert(0,StarkConfig.checkbox) return data
功能三:預設顯示添加按鈕
show_add_btn = True # ======這個方法可自定製(如果把show_add_btn設置為False就不會顯示添加按鈕)===== def get_show_add_btn(self): return self.show_add_btn
功能四:當點擊編輯,刪除,添加按鈕的時候的跳轉路徑,動態生成
return render(request, "stark/change_list_views.html",{"add_url":self.get_add_url(),"show_add_btn":self.get_show_add_btn()})
利用反向解析reverse
# =================url相關,reverse反向解析============= def get_change_url(self,nid): name = "stark:%s_%s_change"%(self.model_class._meta.app_label,self.model_class._meta.model_name) edit_url = reverse(name,args=(nid,)) #反向解析只要找到他的name屬性,就會找到他對應的路徑 return edit_url def get_add_url(self): name = "stark:%s_%s_add" % (self.model_class._meta.app_label, self.model_class._meta.model_name) edit_url = reverse(name) return edit_url def get_delete_url(self, nid): name = "stark:%s_%s_delete" % (self.model_class._meta.app_label, self.model_class._meta.model_name) edit_url = reverse(name,args=(nid,)) return edit_url def get_list_url(self): name = "stark:%s_%s_changelist" % (self.model_class._meta.app_label, self.model_class._meta.model_name) edit_url = reverse(name) return edit_url
功能五:添加,刪除,編輯功能(利用MOdelForm)
model_form_class=None def get_model_form_class(self): if self.model_form_class: #如果自己定製了就用自己的,在這就什麼也不返回了,如果沒有自己定義就返回預設的這個Form return self.model_form_class # 方式一定義ModelForm # class TestModelForm(ModelForm): # class Meta: # model = self.model_class # fields = "__all__" # return TestModelForm # 方式二定義 Meta = type("Meta", (object,), {"model": self.model_class, "fields": "__all__"}) TestModelForm = type("TestModelForm", (ModelForm,), {"Meta": Meta}) return TestModelForm
def add_views(self,request,*args,**kwargs): model_form_class = self.get_model_form_class() if request.method=="GET": form = model_form_class() return render(request,"stark/add_view.html",{"form":form}) else: form = model_form_class(request.POST) if form.is_valid(): form.save() return redirect(self.get_list_url()) else: return render(request, "stark/add_view.html", {"form": form}) def delete_view(self, request,nid, *args, **kwargs): self.model_class.objects.filter(pk=nid).delete() return redirect(self.get_list_url()) def change_views(self, request,nid, *args, **kwargs): model_form_class = self.get_model_form_class() obj = self.model_class.objects.filter(pk=nid).first() if not obj: return redirect(self.get_list_url()) if request.method == "GET": form = model_form_class(instance=obj) return render(request, "stark/edit_view.html", {"form": form}) else: form = model_form_class(data=request.POST,instance=obj) if form.is_valid(): form.save() return redirect(self.get_list_url()) else: return render(request, "stark/edit_view.html", {"form": form})
功能六:額外擴展url
# =============路由系統,對應相應的視圖函數===================== def get_urls(self): app_model_name = (self.model_class._meta.app_label,self.model_class._meta.model_name) all_url = [ url(r'^$', self.change_list_views,name="%s_%s_changelist"%app_model_name), url(r'^add/$', self.add_views,name="%s_%s_add"%app_model_name), url(r'^(\d+)/delete/$', self.delete_view,name="%s_%s_delete"%app_model_name), url(r'^(\d+)/change/$', self.change_views,name="%s_%s_change"%app_model_name), ] all_url.extend(self.extra_urls()) return all_url # ===========額外擴展url(用戶可以進行隨意擴展)========== def extra_urls(self): return [] @property def urls(self): return self.get_urls()
三、使用
#!usr/bin/env python # -*- coding:utf-8 -*- print("sssss6666666") from app01 import models from django.conf.urls import url from stark.service import v1 from django.shortcuts import render,HttpResponse,redirect from django.forms import ModelForm class UserInfoConfig(v1.StarkConfig): # 1、 list_display = ["id","name","email"] # list_display = [] # 2、 def extra_urls(self): url_list =[ url(r'^xxxx/$',self.func), ] return url_list def func(self,request): return HttpResponse("我是額外添加的路徑哦....") # 3、 # show_add_btn=False #預設是True的,如果不讓顯示添加按鈕可以自定製 # 4、 def get_model_form_class(self): class MyModelForm(ModelForm): class Meta: model = self.model_class fields = "__all__" error_messages={ "name":{"required":"用戶名不能為空"}, "email":{"invalid":"郵箱格式不正確"} } return MyModelForm # 5、 def delete_view(self, request,nid, *args, **kwargs): if request.method=="GET": return render(request,"stark/delete_view.html",{"quxiao_url":self.get_list_url()}) else: self.model_class.objects.filter(pk=nid).delete() return redirect(self.get_list_url()) v1.site.register(models.UserInfo,UserInfoConfig)
=================================================== class RoleConfig(v1.StarkConfig): list_display = ["id","name"] # list_display = [] def extra_urls(self): url_list =[ url(r'^aaaa/$',self.func), ] return url_list def func(self,request): return HttpResponse("我是額外添加的路徑哦....") # show_add_btn=False #預設是True的,如果不讓顯示添加按鈕可以自定製 def get_model_form_class(self): class MyModelForm(ModelForm): class Meta: model = self.model_class fields = "__all__" error_messages={ "name":{"required":"用戶名不能為空"}, } return MyModelForm v1.site.register(models.Role,RoleConfig) v1.site.register(models.UserType)
====================================================== class HostConfig(v1.StarkConfig): def ip_port(self, obj= None,is_header=False): if is_header: #如果是True就返回的是th的,預設就是True return "自定義列" return "%s_%s"%(obj.id,obj.port,) #當是False的時候就返回的是td的 list_display = ["id","name","ip","port",ip_port] # =====擴展一個url路徑====== def extra_urls(self): url_list = [ url(r'^report/$', self.report_view), ] return url_list def report_view(self,request): return HttpResponse("<h3>這是我給報表另外添加的一個路徑</h3>") v1.site.register(models.Host,HostConfig)
四、總結:
註意:如果是在應用裡面建的static文件,是不用再settings裡面配置的,就可以用 需要知道的知識點 1、for model_calss,stark_config_obj in self._registry.items(): app_name = model_calss._meta.app_label#應用名稱 model_name = model_calss._meta.model_name#表的名稱 self.model_class._meta.get_field(field_name).verbose_name #得到欄位的verbose_name 2、 if isinstance(field_name,str): #如果是字元串類型的就是用getattr的方式,因為對象不能.字元串 val = getattr(row,field_name) 3、 for field_name in self.list_display: if isinstance(field_name,str): #如果是字元串類型的就是用getattr的方式,因為對象不能.字元串 val = getattr(row,field_name) else: #如果不是字元串傳進來的就是函數,所以得加括弧調用函數 val = field_name(field_name,row) temp.append(val) new_data_list.append(temp) 4、吧方法當屬性來用 @property def urls(self): return self.get_urls()
一個小知識:
1、什麼時候加括弧,什麼時候不加括弧
當需要一個返回值的時候,就去執行函數,加括弧
當你不需要返回什麼,直接告訴它函數名,讓他去找這個函數名。就不用加括弧
補充:用bootstrap修改樣式
1、include:用include直接把單獨的標簽可以引入到html中。目的是減少代碼的冗餘
{% include "stark/form.html" %}
stark/form.html
<form method="post" class="form-horizontal" novalidate> {% csrf_token %} {% for field in form %} <div class="col-sm-6"> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-10"> {{ field }} {{ field.errors.0 }} </div> </div> </div> {% endfor %} <div class="col-sm-offset-11 col-sm-1"> <input type="submit" class="btn btn-primary" value="提交"> </div> </form>
css樣式
.form-horizontal input[type="text"],input[type='email'],input[type='password'],input[type='checkbox'],input[type='number'],select,textarea{ 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; }
補充二:需要優化的導入靜態文件的方式
{% load staticfiles %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width"> <title>Title</title> <link rel="stylesheet" href="{% static '/bootstrap-3.3.7-dist/css/bootstrap.css'%} "> <link rel="stylesheet" href="{% static '/css/stark_form.css' %}"> </head> <body> <h3>添加頁面</h3> {% include "stark/form.html" %} </body> </html>