模仿admin寫一個類似功能的組件,叫做stark
admin流程之啓動
單例模式
單例:只允許一個類實例化出一個對象
使用 new
爲了使類只能出現一個實例,我們可以使用 new 來控制實例的創建過程,代碼如下:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kw):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) # 構建一個實例對象
return cls._instance
egon=Animal()
alex=Animal()
wupeiqi=Animal()
print(id(egon))
print(id(alex))
print(id(wupeiqi))
#三者完全相同
在上面的代碼中,我們將類的實例和一個類變量 _instance 關聯起來,
如果 cls._instance 爲 None 則創建實例,否則直接返回 cls._instance。
使用模塊
其實,Python 的模塊就是天然的單例模式,因爲模塊在第一次導入時,會生成.pyc文件,當第二次導入時,就會直接加載 .pyc 文件,而不會再次執行模塊代碼。
因此,我們只需把相關的函數和數據定義在一個模塊中,就可以獲得一個單例對象了。
如果我們真的想要一個單例類,可以考慮這樣做:
#mysingleton.py
class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()
將上面的代碼保存在文件 mysingleton.py 中,然後這樣使用:
from mysingleton import my_singleton
my_singleton.foo()
啓動
(settings---->installapp------>admin)
def autodiscover():autodiscover_modules('admin', register_to=site)
admin流程之註冊
admin.site : 單例對象
class AdminSite():
def __init__(self, name='admin'):
self._registry = {}
#model就是表對象(Book),admin_class就是表的配置類(BookConfig)
def register(self, model, admin_class=None, **options):
if not admin_class:
#配置類
admin_class = ModelAdmin
self._registry[model] = admin_class(model, self)
site=AdminSite()
admin.site.register(Book,BookConfig)
admin.site.register(Book) # site._registry:{Book:ModelAdmin(Book)}
admin.site.register(Publish) # site._registry:{Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)}
#site._registry:{模型類:該模型類的配置類對象,........}
知識點:url分發擴展
url()方法的擴展應用
from django.shortcuts import HttpResponse
url(r'^index/', index), # 一旦匹配成功, index(request)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^yuan/', ([
url(r'^test01/', ([
url(r'^test1_1/', test01),
url(r'^test1_2/', test01),
],None,None)),
url(r'^test02/', test02),
url(r'^test03/', test03),
],None,None)),
]
格式,元祖裏面是一個列表+None+None
admin流程之設計url
admin中:
from app01 import models
admin.site.register(models.Book)
admin.site.register(models.Publish)
admin.site.register(models.Author)
urls.py中:
from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import render, HttpResponse, redirect
def list_view(request):
return HttpResponse("list_view")
def change(request, id):
return HttpResponse("change")
def delete(request, id):
return HttpResponse("delete")
def add(request):
return HttpResponse("add")
def get_urls2():
temp = [
url("^add/$", add),
url("^$", list_view),
url("^(\d+)/change/$", change),
url("^(\d+)/delete/$", delete)
]
return temp
def get_urls():
temp = []
print("_registry==>", admin.site._registry.items())
#{Book:ModelAdmin(Book),....}
#(<class 'app01.models.Book'>, <django.contrib.admin.options.ModelAdmin object at 0x00000234441F99B0>)
#Django啓動會先加載settings,因此_registry中有值
for model, model_class_obj in admin.site._registry.items():
app_name = model._meta.app_label
#model._meta.app_label可以獲取當前model所在app的名字
model_name = model._meta.model_name
#model._meta.model_name可以獲得當前model的名稱字符串
#用stark+app的名字+模型表的名字
#舉例:http://127.0.0.1:8000/stark/app01/book/02/change/
temp.append(url(r"%s/%s/" % (app_name, model_name), (get_urls2(), None, None)))
return temp
urlpatterns = [
url(r'^admin/', admin.site.urls),
# [
# url(r'^test01/', test01),
# url(r'^test02/', test02),
# ]
url(r"^stark",(get_urls(),None,None))
]
最終url示例:
^stark auth/group/
^stark auth/user/
^stark app01/book/ ^add/$
^stark app01/book/ ^$
^stark app01/book/ ^(\d+)/change/$
^stark app01/book/ ^(\d+)/delete/$
^stark app01/publish/
^stark app01/author/
知識點回顧
函數和實例方法的區別
class Animal():
def running(self):
print("running...")
#從實例對象調
alex = Animal()
#alex會作爲self參數傳進去
alex.running()
#結果:
#running...
#當做函數調,必須傳個參數
Animal.running(123)
#結果:
#running...
合併列表
temp = []
temp.append(1)
l = [2, 3, 4]
temp.extend(l)
print(temp)
#結果:
[1, 2, 3, 4]
根據字符串名稱拿字段對象和數據
Model _meta API
它提供的方法可用於:
檢索模型的所有字段實例
按名稱檢索模型的單個字段實例
from django.contrib.auth.models import User
# A field on the model
User._meta.get_field('username')
<django.db.models.fields.CharField: username>
# A field from another model that has a relation with the current model
User._meta.get_field('logentry')
<ManyToOneRel: admin.logentry>
按名稱(字符串)檢索模型的單個字段實例
filter_field_obj = self.model._meta.get_field(filter_field)
print("filter_field_obj==>", filter_field_obj)
得到app01.Book.publish和app01.Book.authors
拿對象下面的數據
.rel .to 來自於ForeignKey和ManyToManyField這倆類
.rel可以看到對象的數據類型,加上.to可以拿到對象下面的數據,.to只對一對一,一對多,多對多有效
print(filter_field_obj.rel.to)
結果:
< class 'app01.models.Publish'>
< class 'app01.models.Author'>
data_list = filter_field_obj.rel.to.objects.all()
print(filter_field_obj.rel.to)
結果:
< QuerySet[ < Publish: 圖靈新知 >, < Publish: dzm >, < Publish: 老男孩出版社 >] >
< QuerySet[ < Author: egon >, < Author: alex >] >
獲取字段對象的中文名
from app01.models import Book
#price是book表中一個字段對象
obj = Book._meta.get_field("price")
obj.verbose_name
結果:
'價格'
沒有單獨設置verbose_name時,則返回英文名,若想使用中文名則要在models定義verbose_name
例:
class Book(models.Model):
nid = models.AutoField(primary_key=True, verbose_name="編號")
title = models.CharField(max_length=32, verbose_name="名稱")
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="價格")
總結:自制Admin組件
這裏我們叫它Stark
從訪問說起:下面的思路是模擬Django原生Admin的寫法
url流程
1.urls.py中:
from stark.service.sites import site
urlpatterns = [
# url(r"^stark",(get_urls(),None,None))
url(r'^stark/', site.urls)
]
2.site是StarkSite這個類的實例化結果,如下面
site = StarkSite()
3.site調用urls,返回的是get_urls中的temp列表
model的註冊
在app01目錄下面有stark.py文件
我們在這個文件中對model進行註冊
在Django啓動的時候會遍歷每個app中是否有stark.py文件(settings中設置的),找到後會運行,
因此裏面的內容在Django啓動時就運行過了
1.site.register(Book) 註冊Book表
2.register方法會執行:
self._registry[model] = admin_class(model, self)
3.也就是向_registry = {}這個字典中添加了如上內容
內容簡單理解爲:{Book:ModelStark(Book)},這裏的鍵Book是model對象
此時_registry = {}中就有了內容
訪問,url分發開始
1.循環_registry = {},使用site._registry.items()分別得到表的對象和配置類
2.用表對象得到app的名字和表的名字,由此拼出一級分發
3.二級分發用配置類調用urls2得出
配置類對象是ModelStark(Book)
接下來我們去找ModelStark這個類來看一下
class ModelStark()就是默認的配置類,如果註冊表的時候沒有自定義配置類則走這裏,
同時自定義配置類也要繼承ModelStark作爲父類
在這個類下面:
class ModelStark(): #配置類
list_display = ["__str__", ]
def __init__(self, model, site):
self.model = model
這個self.model = model是關鍵,誰調用的它self.model就是那個表對象
如果是Book的話,那麼self.model = Book
我們回到urls2
urls2返回值構建了一個return self.get_urls2(), None, None的格式
我們再去找get_urls2()這個函數
get_urls2和urls2都是class ModelStark下面的方法
get_urls2裏面負責處理二級分發
這裏舉例訪問到視圖函數list_view中
def list_view(self, request):
data_list = self.model.objects.all()
此時model是Book,所以可以進行查詢
self.model是誰
注:在視圖函數中self.model到底是誰?這裏是原生Admin組件寫法的關鍵
這要看self是誰,一層一層往上找,最後會找到model_class_obj.urls2
model_class_obj來自於site._registry循環結果中的配置類,配置類是stark.py中註冊的一個個模型的配置類
循環到誰就是誰調用urls2,self.model也就是那個對應的模型對象注:我們無法確定用戶訪問那個url,因此提前準備好所有url,此時訪問book,後面的model就是book,因此可以拿到相應數據
表單數據之多對多字段的處理
方法1 使用函數
在stark.py的BookConfig類中再增加一個函數
def display_atuhors(self, obj=None, is_header=False):
#判斷is_header有沒有值,有值的話就表示這是標題行,直接返回一個固定標題名
if is_header:
return "作者"
s = []
#obj是傳過來的一個個的對象<Book: go語言第二版>, <Book: linux>,
#把這個對象中authors字段所有的內容循環起來分別添加到一個列表中
for author in obj.authors.all():
s.append(author.name)
return ",".join(s)
list_display = ["nid", "title", "price", "publish", display_atuhors,]
方法2
通過判斷是不是多對多函數來做處理
data_list = self.model.objects.all()
for obj in data_list:
temp = []
#循環需要顯示的字段
for field in self.list_display:
#判斷這個字段是不是字符串
if isinstance(field, str):
try:
#引入ManyToManyField
from django.db.models.fields.related import ManyToManyField
#獲取相應名稱字段的對象
field_obj = self.model._meta.get_field(field) #注意,這裏沒法找出__str__,因此會報錯
#判斷這個對象是不是多對多
if isinstance(field_obj, ManyToManyField):
l = []
#利用反射,循環obj.field.all(),例如<Book: Book object>.authors,再加上.all()取這個對象下authors字段的全部內容
for i in getattr(obj, field).all():
#把循環結果添加到l列表
l.append(str(i)) #l==> ['egon', 'alex']
#用逗號把列表裏面的元素拼起來
val = ",".join(l)
except Exception as e:
val = getattr(obj, field)
#添加到temp列表
temp.append(val)