Admin簡介:
Admin:是django的后臺 管理的wed版本
我們現在models.py文件里面建幾張表:
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) class AuthorDetail(models.Model):nid = models.AutoField(primary_key=True)birthday=models.DateField()telephone=models.BigIntegerField()addr=models.CharField( max_length=64) 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()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',)
然后執行數據庫遷移的兩條命令。
python3 manage.py makemigrations
python3 manage.py migrate
現在直接打開項目去訪問admin肯定是不行的。他會提示你要先登陸。所以要先創建一個超級用戶:python3 manage.py createsuperuser
然后再登陸進去,這時候發現登陸進去并不是我們創建的那些表,因為我們還沒有在admin中注冊。所以在app01下的admin中注冊
from app01 import models admin.site.register(models.Book,Bookconfig) admin.site.register(models.AuthorDetail) admin.site.register(models.Author) admin.site.register(models.Publish)
注冊之后,然后再訪問admin地址,就會發現,我們注冊了那張表就會有那個。
我們會發現每個表都是一個可點 的標簽。我們可以進行曾刪改查:
http://127.0.0.1:8000/admin/app02/food/???? 查
??????????????????????????? http://127.0.0.1:8000/admin/app02/food/add/? 增
??????????????????????????? http://127.0.0.1:8000/admin/app02/food/1/change/ 改
??????????????????????????? http://127.0.0.1:8000/admin/app02/food/2/delete/? 刪
現在我們點擊book的那張表,是這個樣子:
只能看到書的名字,那我們想看到書的其他字段呢?怎么辦?
找到admin.py注冊的地方,我們只是在admin中注冊了,現在我們加上配置類,就可以看到其他的字段了。
?
class Bookconfig(admin.ModelAdmin):# list_display must not be a ManyToManyField 這里面不能放多對多的字段。list_display = ['title','price','publish','publishDate'] # 設置可以顯示的字段list_display_links = ['price','title','publishDate'] # 設置有鏈接的字段list_filter = ['title','publish', 'authors__nid','authors'] #設置過濾的條件,search_fields = ['title','price'] # 設置根據搜尋的字段,模糊查找# 進行批量操作,actionsdef patch_init(self,request,queryset):queryset.update(price=0) # 初始化價格為0patch_init.short_description = '價格初始化' # 批量操作的名字actions = [patch_init] # 進行批量操作
這里有一個注意的地方,就是list_filter里面可以放外鍵關聯的字段,也可以放多對多字段。舉例:就像book表多對多author表,list_filter = ['title','publish', 'authors__nid','authors']
?
咱們寫的這個類繼承 admin.ModelAdmin,而admin.ModelAdmin這是個默認配置類,也就是說這個默認配置類自己配置了這些參數,如果我們不想用默認配置類的這些參數,我們就自己寫參數覆蓋默認配置類的參數就行了。
然后將我們寫的這個類實例對象添加進admin.site.register(models.Book,Bookconfig)就行了。
?
上面說的是一些常用的配置參數,下面是一些其他的參數:
ModelAdmin中提供了大量的可定制功能,如
?1.?list_display,列表時,定制顯示的列。
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd', 'xxxxx') def xxxxx(self, obj): return "xxxxx"
2.?list_display_links,列表時,定制列可以點擊跳轉。
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd', 'xxxxx') list_display_links = ('pwd',)
3.?list_filter,列表時,定制右側快速篩選。
4.?list_select_related,列表時,連表查詢是否自動select_related
5. list_editable,列表時,可以編輯的列?
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):list_display = ('user', 'pwd','ug',) list_editable = ('ug',)
6.?search_fields,列表時,模糊搜索的功能
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):search_fields = ('user', 'pwd')
7.?date_hierarchy,列表時,對Date和DateTime類型進行搜索
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):date_hierarchy = 'ctime'
8??inlines,詳細頁面,如果有其他表和當前表做FK,那么詳細頁面可以進行動態增加和刪除
class UserInfoInline(admin.StackedInline): # TabularInlineextra = 0model = models.UserInfoclass GroupAdminMode(admin.ModelAdmin): list_display = ('id', 'title',) inlines = [UserInfoInline, ]
9 action,列表時,定制action中的操作
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):# 定制Action行為具體方法def func(self, request, queryset): print(self, request, queryset) print(request.POST.getlist('_selected_action')) func.short_description = "中文顯示自定義Actions" actions = [func, ] # Action選項都是在頁面上方顯示 actions_on_top = True # Action選項都是在頁面下方顯示 actions_on_bottom = False # 是否顯示選擇個數 actions_selection_counter = True
10 定制HTML模板
add_form_template = None
change_form_template = None
change_list_template = None
delete_confirmation_template = None
delete_selected_confirmation_template = None
object_history_template = None
11 raw_id_fields,詳細頁面,針對FK和M2M字段變成以Input框形式
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):raw_id_fields = ('FK字段', 'M2M字段',)
12??fields,詳細頁面時,顯示字段的字段
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):fields = ('user',)
13?exclude,詳細頁面時,排除的字段
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):exclude = ('user',)
14??readonly_fields,詳細頁面時,只讀字段
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):readonly_fields = ('user',)
15?fieldsets,詳細頁面時,使用fieldsets標簽對數據進行分割顯示
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):fieldsets = (('基本數據', { 'fields': ('user', 'pwd', 'ctime',) }), ('其他', { 'classes': ('collapse', 'wide', 'extrapretty'), # 'collapse','wide', 'extrapretty' 'fields': ('user', 'pwd'), }), )
16 詳細頁面時,M2M顯示時,數據移動選擇(方向:上下和左右)
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):filter_vertical = ("m2m字段",) # 或filter_horizontal = ("m2m字段",)
17 ordering,列表時,數據排序規則
@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):ordering = ('-id',) 或 def get_ordering(self, request): return ['-id', ]
18.?radio_fields,詳細頁面時,使用radio顯示選項(FK默認使用select)
radio_fields = {"ug": admin.VERTICAL} # 或admin.HORIZONTAL
19?form = ModelForm,用于定制用戶請求時候表單驗證
from app01 import models
from django.forms import ModelForm from django.forms import fields class MyForm(ModelForm): others = fields.CharField() class Meta: model = models = models.UserInfo fields = "__all__" @admin.register(models.UserInfo) class UserAdmin(admin.ModelAdmin): form = MyForm
20 empty_value_display = "列數據為空時,顯示默認值"
@admin.register(models.UserInfo) class UserAdmin(admin.ModelAdmin):empty_value_display = "列數據為空時,默認顯示" list_display = ('user','pwd','up') def up(self,obj): return obj.user up.empty_value_display = "指定列數據為空時,默認顯示"
這里就涉及到我們之前學的類的知識了。我們一起來回顧一下:
Class A(object):X = 10 Class B(A):X = 8 # 類變量 存在類里 .b = B() Print(b.x)
這個結果是什么呢?答案肯定是8
# class A(object): # x=10 # y=12 # # class B(A): # def __init__(self): # 實例變量 存在實例里 # self.y=10 # x=8 # y=20 # # b=B() # print(b.x) # print(b.y) 這個答案是多少?
答案是8和10,那如果這樣呢?
# class A(object): # def __init__(self): # self.y=10 # x=10 # y=12 # class B(A): # x=8 # y=20 # b=B() # print(b.y) # print(b.x) 答案仍然是8和10 為什么呢?
這里面一定要區分類存儲和實例存儲的關系,先在實例空間中尋找,再找類空間,一個實例對象在存儲的時候 先在自己的實例空間里尋找,如果找不到就從父類的實例空間中尋找,如果找不到,就在自己的類空間中尋找,然后再父的類空間中尋找。說白了,就是實例化一個對象時,要先執行__init__實例初始化,如果自己的類中沒有__init__就去父類中去實例初始化。然后在類空間中尋找。
上面只是說了admin的使用,接下來我們來看一下admin的源碼,看一下他是怎么工作的,我們先來復習一下單例模式。
?
什么是單例模式:
單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。他可以實例化多次,但都屬于著一個類的,當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。
在 Python 中,我們可以用多種方法來實現單例模式:
?
- 使用模塊
- 使用?__new__
- 使用裝飾器(decorator)
- 使用元類(metaclass)
?
1.使用?__new__
class Singleton(object):_instance = Nonedef __new__(cls, *args, **kw): #實例化的時候,執行__init__之前if not cls._instance:cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) return cls._instances1 = Singleton()s2 = Singleton()
s1實例化之后,先走__new__,_instance默認為none,進入if判斷,重新對cls.instance進行賦值,賦值的結果是執行了object類的__new__方法,其實就是拿到了一個實例化對象。最后返回cls.instance。
此時此刻,cls.instance的值就是object類實例化的那個對象。
S2實例化之后,走__new__,但是這時候,cls.instance已經有值了,所以不會再走if判斷。直接返回。
所以兩次實例化都是指向s1的內存空間。S1和s2是完全一樣的。這就是__new__的思路。
?
2.使用模塊
我們先創建一個main.py文件,在創建一個func.py文件。
大家覺得這兩個id一樣嗎?答案是一樣的
第二次導入的時候不會再執行func文件了,直接實例化,因為第一次已經導入過了。
在創建一個cal文件夾
大家看這個執行結果呢?這個結果肯定是一樣的
只要導入一次,在整個程序中不管在調用多少次,他都不會在執行導入的那個模塊中的內容,都是直接使用的。記住這句話就行了。
?還有一個例子:
這個打印結果是什么?答案是三者的id都不一樣。雖然他們都是用的同一個類但是實例化出了三個對象。
Admin源碼是分為三部分:啟動,注冊階段,設計url,
先看注冊,admin.site.register(models.Book,Bookconfig)? 我們需要重點了解一下site是什么,register方法中寫了什么,以及models.Book,Bookconfig? 這兩個參數代表什么意思?
?接下來我們看一下源碼:
第一步:啟動
數據庫遷移完成之后,我們只在admin中xie了這些代碼:
admin.site.register(models.Book,)
admin.site.register(models.AuthorDetail)
admin.site.register(models.Author)
admin.site.register(models.Publish)
django啟動過程中,一定會先去加載settings中的INSTALL_APPS,拿INSTALL_APPS的第一個來說,'django.contrib.admin',這句話和 from django.contrib import admin的意思是一樣的,加載的時候他會去加載from django.contrib import admin
?點進去admin,這時候會執行from django.contrib.admin.sites import AdminSite, site。然后點進去sites,會發現有這么一行site = AdminSite()。這就是用到了單例模式。意思就是說,只要引入site模塊,就實例化AdminSite這么一個對象。然后會執行
def autodiscover():
autodiscover_modules('admin', register_to=site)
這句話的意思是告訴django啟動后先會執行app下的admin.py文件。
第二步:注冊
運行app下的admin.py文件,只要遇到admin.site就是實例化一個site單例對象。
admin.site.register(models.Publish)
?
class Publishconfig(admin.ModelAdmin): pass
admin.site.register(models.Publish,Publishconfig)這是兩種注冊方法,不過是多了一個我們自己添加的類
接下來就是看register方法了:(這個register方法是site = AdminSite()類中的方法)點進去
我們只看兩句話:第一句:
第二個參數默認是none,意思是可以不傳這個參數,如果admin_class不傳,就admin_class就等于Modeladmin。如果傳第二個參數,就用的是自己的那個類,所以我們寫的那個類要繼承Modeladmin。
第二句:
self._registry[model] = admin_class(model, self)
這個_registry是在上面初始化的時候,賦值給_register一個空的字典。
def __init__(self, name='admin'):
self._registry = {}
?所謂的注冊功能就是在全局的單例對象的_registry字典里面添加鍵值對,以model為鍵,以 admin_class為值。而 admin_class就是第一句我們自己寫的類或者默認類的一個實例對象
所以,admin.site.register(models.Publish)這句話執行完self._registry里就有一個Publish鍵值對了
在當我們注冊admin.site.register(models.Author)的時候,self._registry里就會多一個Author鍵值對了。
我們可以打印一下print(admin.site._register)看看里面有幾個鍵值對。在admin中注冊了幾個就有幾個鍵值對。
可以根據下面的源碼縷一縷:
解析admin的源碼:
??????? 1 啟動
?? ??? ??? django啟動后,會加載settings中的install_app
?? ??? ??? admin.py:
?? ??? ???????? from django.contrib.admin.sites import AdminSite, site
?? ??? ??? ??? ?autodiscover_modules('admin', register_to=site):加載每一個app下的admin.py文件
?? ??? ?
?? ??? ?2 注冊
?? ??? ?
?? ??? ?? admin.site.register(Author)
?? ??? ?? class BookConfig(admin.ModelAdmin):
?? ??? ??????? pass
?? ??? ?? admin.site.register(Book,BookConfig)
?? ??? ? ?
?? ??? ? ?
?? ??? ?? 源碼:
?? ??? ?????? admin.py---sites.py---->
?? ??? ??? ?? class AdminSite(object):
?? ??? ??? ? ?
?? ??? ??? ?????? def __init__(self):
?? ??? ??? ??? ??? ??? ?self._registry = {}
?? ??? ??? ? ?
?? ??? ??? ?????? def register(self,model,admin_class=None):
?? ??? ??? ??? ??????? if not admin_class:
?? ??? ??? ??? ??? ??? ??? ?admin_class = ModelAdmin
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? self._registry[model] = admin_class(model, self)?? ??? ?
?? ??? ??? ? ?
?? ??? ??? ? ?
?? ??? ??? ?? site = AdminSite()
?? ??? ??? ???????
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?