如果只是在admin中簡單的展示及管理模型,那么在admin.py
模塊中使用admin.site.register將模型注冊一下就好了:
from django.contrib import admin from myproject.myapp.models import Author admin.site.register(Author)
但是,很多時候這遠遠不夠,我們需要對admin進行各種深度定制,以滿足我們的需求。
這就要使用Django為我們提供的ModelAdmin類了。
ModelAdmin類是一個模型在admin頁面里的展示方法,如果你對默認的admin頁面滿意,那么你完全不需要定義這個類,直接使用最原始的樣子也行。通常,它們保存在app的admin.py文件里。下面是個簡單的例子:
from django.contrib import admin from myproject.myapp.models import Author # 創建一個ModelAdmin的子類 class AuthorAdmin(admin.ModelAdmin): pass # 注冊的時候,將原模型和ModelAdmin耦合起來 admin.site.register(Author, AuthorAdmin)
一、注冊裝飾器
除了常用的admin.site.register(Author, AuthorAdmin)
方式進行注冊,還可以用裝飾器的方式連接模型和ModelAdmin。如下所示:
from django.contrib import admin from .models import Author @admin.register(Author) class AuthorAdmin(admin.ModelAdmin): pass
這個裝飾器可以接收一些模型類作為參數,以及一個可選的關鍵字參數site(如果你使用的不是默認的AdminSite),比如。
from django.contrib import admin from .models import Author, Reader, Editor from myproject.admin_site import custom_admin_site @admin.register(Author, Reader, Editor, site=custom_admin_site) class PersonAdmin(admin.ModelAdmin): pass
二、 搜索admin文件
當你在INSTALLED_APPS
設置中添加了django.contrib.admin
后,Django將自動在每個應用中搜索admin模塊並導入它。也就是說,通常我們在每個app下都有一個admin.py文件,將當前app和admin有關的內容寫到內部的admin.py文件中就可以了,Django會自動搜索並應用它們。
- class apps.AdminConfig:admin默認的AppConfig類,當Django啟動時自動調用其autodiscover()方法
- class apps.SimpleAdminConfig:和上面的類似,但不調用autodiscover()
- autodiscover()[source]:自動搜索admin模塊的方法。在使用自定義的site時,必須禁用這個方法,你應該在
INSTALLED_APPS
設置中用django.contrib.admin.apps.SimpleAdminConfig
替代django.contrib.admin
三、ModelAdmin的屬性
真正用來定制admin的手段,大部分都集中在這些ModelAdmin內置的屬性上。
ModelAdmin非常靈活,它有許多內置屬性,幫助我們自定義admin的界面和功能。所有的屬性都定義在ModelAdmin的子類中,如下方式:
from django.contrib import admin class AuthorAdmin(admin.ModelAdmin): date_hierarchy = 'pub_date'
1. ModelAdmin.actions
一個列表,包含自定義的actions,后面有專門的敘述。
2. ModelAdmin.actions_on_top
是否在列表上方顯示actions的下拉框,默認為True
3. ModelAdmin.actions_on_bottom
是否在列表下方顯示actions的下拉框,默認為False。效果看下面的圖片,沒什么大用途。
4. ModelAdmin.actions_selection_counter
是否在actions下拉框右側顯示選中的對象的數量,默認為True,可改為False。
5. ModelAdmin.date_hierarchy
根據你指定的日期相關的字段,為頁面創建一個時間導航欄,可通過日期過濾對象。例如:
date_hierarchy = 'pub_date'
它的效果看起來是這樣的:
6. ModelAdmin.empty_value_display
指定空白顯示的內容。如果你有些字段沒有值(例如None,空字符串等等),默認情況下會顯示破折號“-”。這個選項可以讓你自定義顯示什么,如下例就顯示為“-empty-”:
from django.contrib import admin class AuthorAdmin(admin.ModelAdmin): empty_value_display = '-empty-'
你還可以為整個admin站點設置默認空白顯示值,通過設置AdminSite.empty_value_display="xxxxxxx"
。甚至為某個函數設置空白值,如下:
from django.contrib import admin class AuthorAdmin(admin.ModelAdmin): fields = ('name', 'title', 'view_birth_date') def view_birth_date(self, obj): return obj.birth_date # 注意下面這句 view_birth_date.empty_value_display = '???'
7. ModelAdmin.exclude
不顯示指定的某些字段。如下例有這么個模型:
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) title = models.CharField(max_length=3) birth_date = models.DateField(blank=True, null=True)
如果你不希望在頁面內顯示birth_date
字段,那么這么設置:
from django.contrib import admin class AuthorAdmin(admin.ModelAdmin): fields = ('name', 'title')
和這么設置是一樣的:
from django.contrib import admin class AuthorAdmin(admin.ModelAdmin): # 一定注意了,值是個元組!一個元素的時候,最后的逗號不能省略。 exclude = ('birth_date',)
8. ModelAdmin.fields
按你希望的順序,顯示指定的字段。與exclude相對。但要注意與list_display
區分。這里有個小技巧,你可以通過組合元組的方式,讓某些字段在同一行內顯示,例如下面的做法“url”和“title”將在一行內,而“content”則在下一行。
class FlatPageAdmin(admin.ModelAdmin): fields = (('url', 'title'), 'content')
如果沒有對field或fieldsets選項進行定義,那么Django將按照模型定義中的順序,每一行顯示一個字段的方式,逐個顯示所有的非AutoField和editable=True的字段。(自動字段,如主鍵,不可編輯字段是不會出現在頁面里的。)
9. ModelAdmin.fieldsets
這個功能其實就是根據字段對頁面進行分組顯示或布局了。fieldsets是一個二元元組的列表。每個二元元組代表一個<fieldset>
,是整個form的一部分。
二元元組的格式為(name,field_options)
,name是一個表示該filedset標題的字符串,field_options
是一個包含在該filedset內的字段列表。
下面是一個例子,有助於你理解:
from django.contrib import admin class FlatPageAdmin(admin.ModelAdmin): fieldsets = ( (None, { 'fields': ('url', 'title', 'content', 'sites') }), ('Advanced options', { 'classes': ('collapse',), 'fields': ('registration_required', 'template_name'), }), )
它的頁面看起來像下面的樣子:
在filed_options
字典內,可以使用下面這些關鍵字:
fields:一個必填的元組,包含要在fieldset中顯示的字段。例如:
{ 'fields': ('first_name', 'last_name', 'address', 'city', 'state'), }
同樣,它也可以像前面那樣通過組合元組,實現多個字段在一行內的效果:
{ 'fields': (('first_name', 'last_name'), 'address', 'city', 'state'), }
fileds可以包含readonly_fields
的值,作為只讀字段。
classes:一個包含額外的CSS類的元組,例如:
{ 'classes': ('wide', 'extrapretty'), }
兩個比較有用的樣式是collaspe
和wide
,前者將fieldsets折疊起來,后者讓它具備更寬的水平空間。
description:一個可選的額外的說明文本,放置在每個fieldset的頂部。但是,這里並沒有對description的HTML語法進行轉義,因此可能有時候會造成一些莫名其妙的顯示,要忽略HTML的影響,請使用django.utils.html.escape()
手動轉義。
10. ModelAdmin.filter_horizontal
水平擴展多對多字段。默認情況下,ManyTOManyField在admin的頁面中會顯示為一個select框。在需要選擇大量對象時,這會有點困難。將ManyTOManyField添加到這個屬性列表里后,頁面就會對字段進行擴展,並提供過濾功能。如下圖:
11. ModelAdmin.filter_vertical
與上面的類似,不過是改成垂直布置了。
12. ModelAdmin.form
默認情況下,admin系統會為你的模型動態的創建ModelForm,它用於創建你的添加/修改
頁面的表單。我們可以編寫自定義的ModelForm,在"添加/修改"頁面覆蓋默認的表單行為。
注意:如果你的ModelForm和ModelAdmin同時定義了exclude選項,那么ModelAdmin中的具有優先權,如下例所示,"age"字段將被排除,但是“name”字段將被顯示:
from django import forms from django.contrib import admin from myapp.models import Person class PersonForm(forms.ModelForm): class Meta: model = Person exclude = ['name'] class PersonAdmin(admin.ModelAdmin): exclude = ['age'] form = PersonForm
13. ModelAdmin.formfield_overrides
這個屬性比較難以理解,通過一個列子來解釋可能會更好一點。設想一下我們自己寫了個RichTextEditorWidget
(富文本控件),然后想用它來代替傳統的<textarea>
(文本域控件)用於輸入大段文字。我們可以這么做:
from django.db import models from django.contrib import admin # 從對應的目錄導入我們先前寫好的widget和model from myapp.widgets import RichTextEditorWidget from myapp.models import MyModel class MyModelAdmin(admin.ModelAdmin): formfield_overrides = { models.TextField: {'widget': RichTextEditorWidget}, }
注意在上面的外層字典中的鍵是一個實際的字段類,而不是字符串,對應的值又是一個字典;這些參數將被傳遞給form字段的__init__()
方法。
警告:如果你想使用一個帶有關系字段的自定義widget。請確保你沒有在raw_id_fields
或radio_field
s之中include那些字段的名字。
14. ModelAdmin.inlines
參考InlineModelAdmin對象,就像ModelAdmin.get_formsets_with_inlines()
一樣。
15. ModelAdmin.list_display
指定顯示在修改頁面上的字段。這是一個很常用也是最重要的技巧之一。例如:
list_display = ('first_name', 'last_name')
如果你不設置這個屬性,admin站點將只顯示一列,內容是每個對象的__str__()
(Python2使用__unicode__()
)方法返回的內容。
在list_display中,你可以設置四種值:
1.模型的字段名
class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name')
2.一個函數,它接收一個模型實例作為參數
def upper_case_name(obj): return ("%s %s" % (obj.first_name, obj.last_name)).upper() upper_case_name.short_description = 'Name' class PersonAdmin(admin.ModelAdmin): list_display = (upper_case_name,)
3.一個表示ModelAdmin的某個屬性的字符串
類似上面的函數調用,通過反射獲取函數名,換了種寫法而已,例如:
class PersonAdmin(admin.ModelAdmin): list_display = ('upper_case_name',) def upper_case_name(self, obj): return ("%s %s" % (obj.first_name, obj.last_name)).upper() upper_case_name.short_description = 'Name'
4.一個表示模型的某個屬性的字符串
類似第二種,但是此處的self是模型實例,引用的是模型的屬性。參考下面的例子:
from django.db import models from django.contrib import admin class Person(models.Model): name = models.CharField(max_length=50) birthday = models.DateField() def decade_born_in(self): return self.birthday.strftime('%Y')[:3] + "0's" decade_born_in.short_description = 'Birth decade' class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'decade_born_in')
下面是對list_display
屬性的一些特別提醒:
- 對於Foreignkey字段,顯示的將是其
__str__()
方法的值。 - 不支持ManyToMany字段。如果你非要顯示它,請自定義方法。
- 對於BooleanField或NullBooleanField字段,會用on/off圖標代替True/False。
- 如果給list_display提供的值是一個模型的、ModelAdmin的或者可調用的方法,默認情況下會自動對返回結果進行HTML轉義,這可能不是你想要的。
下面是一個完整的例子:
from django.db import models from django.contrib import admin from django.utils.html import format_html class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) color_code = models.CharField(max_length=6) def colored_name(self): # 關鍵是這句!!!!!請自己調整縮進。 return '<span style="color: #%s;">%s %s</span>'%( self.color_code, self.first_name, self.last_name, ) class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'colored_name')
實際的效果如下圖所示:
很明顯,你是想要有個CSS效果,但Django把它當普通的字符串了。怎么辦呢?用format_html()
或者format_html_join()
或者mark_safe()
方法!
from django.db import models from django.contrib import admin # 需要先導入! from django.utils.html import format_html class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) color_code = models.CharField(max_length=6) def colored_name(self): # 這里還是重點,注意調用方式,‘%’變成‘{}’了! return format_html( '<span style="color: #{};">{} {}</span>', self.color_code, self.first_name, self.last_name, ) class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'colored_name')
下面看起來就會是你想要的結果了:
- 如果某個字段的值為None或空字符串或空的可迭代對象,那么默認顯示為短橫杠“-”,你可以使用
AdminSite.empty_value_display
在全局改寫這一行為:
from django.contrib import admin admin.site.empty_value_display = '(None)'
或者使用ModelAdmin.empty_value_display
只改變某個類的行為:
class PersonAdmin(admin.ModelAdmin): empty_value_display = 'unknown'
或者更細粒度的只改變某個字段的這一行為:
class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'birth_date_view') def birth_date_view(self, obj): return obj.birth_date birth_date_view.empty_value_display = 'unknown'
- 默認情況下,一個返回布爾值的方法在list_display中顯示為True或者False的:
但如果你給這個方法添加一個boolean的屬性並賦值為True,它將顯示為on/off的圖標,如下圖:
from django.db import models from django.contrib import admin class Person(models.Model): first_name = models.CharField(max_length=50) birthday = models.DateField() def born_in_fifties(self): return self.birthday.strftime('%Y')[:3] == '195' # 關鍵在這里 born_in_fifties.boolean = True class PersonAdmin(admin.ModelAdmin): # 官方文檔這里有錯,將'name'改為'first_name' list_display = ('first_name', 'born_in_fifties')
- 通常情況下,在
list_display
列表里的元素如果不是數據庫內的某個具體字段,是不能根據它進行排序的。但是如果給這個字段添加一個admin_order_field
屬性,並賦值一個具體的數據庫內的字段,則可以按這個字段對原字段進行排序,如下所示:
from django.db import models from django.contrib import admin from django.utils.html import format_html class Person(models.Model): first_name = models.CharField(max_length=50) color_code = models.CharField(max_length=6) def colored_first_name(self): return format_html( '<span style="color: #{};">{}</span>', self.color_code, self.first_name, ) # 就是這一句了! colored_first_name.admin_order_field = 'first_name' class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'colored_first_name')
本來colored_first_name
是不能排序的,給它的admin_order_field
賦值first_name
后,就依托first_name
進行排序了。
要降序的話,使用連字符“-”前綴:
colored_first_name.admin_order_field = '-first_name'
還可以跨表跨關系引用:
class Blog(models.Model): title = models.CharField(max_length=255) author = models.ForeignKey(Person, on_delete=models.CASCADE) class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'author', 'author_first_name') def author_first_name(self, obj): return obj.author.first_name # 指定了另一張表的first_name作為排序的依據 author_first_name.admin_order_field = 'author__first_name'
list_display
里的元素還可以是某個屬性。但是請注意的是,如果使用python的@property
方式來構造一個屬性,則不能給它添加short_description
描述,只有使用property()
函數的方法構造屬性的時候,才可以添加short_description
描述,如下:
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def my_property(self): return self.first_name + ' ' + self.last_name my_property.short_description = "Full name of the person" full_name = property(my_property) class PersonAdmin(admin.ModelAdmin): list_display = ('full_name',)
list_display
中的每個字段名在HTML中都將自動生成CSS類屬性,在th
標簽中以column-<field_name>
的格式,你可以通過它,對前端進行自定義或調整,例如設置寬度等等。- Django將按下面的順序,解釋
list_display
中的每個元素: - 模型的字段
- 可調用對象
- ModelAdmin的屬性
- 模型的屬性
16. ModelAdmin.list_display_links
指定用於鏈接修改頁面的字段。通常情況,list_display
列表中的第一個元素被作為指向目標修改頁面的超級鏈接點。但是,使用list_display_links
可以幫你修改這一默認配置。
- 如果設置為None,則根本沒有鏈接了,你無法跳到目標的修改頁面。
- 或者設置為一個字段的元組或列表(和
list_display
的格式一樣),這里面的每一個元素都是一個指向修改頁面的鏈接。你可以指定和list_display
一樣多的元素個數,Django不關系它的多少。唯一需要注意的是,如果你要使用list_display_links
,你必須先有list_display
。
下面這個例子中first_name
和last_name
都可以點擊並跳轉到修改頁面。
class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'birthday') list_display_links = ('first_name', 'last_name')
而如果這樣,你將沒有任何鏈接:
class AuditEntryAdmin(admin.ModelAdmin): list_display = ('timestamp', 'message') list_display_links = None
17. ModelAdmin.list_editable
這個選項是讓你指定在修改列表頁面中哪些字段可以被編輯。指定的字段將顯示為編輯框,可修改后直接批量保存,如下圖:
在這里,我們將last_name
設置為了list_editable
。
需要注意的是:一是不能將list_display
中沒有的元素設置為list_editable
,二是不能將list_display_links
中的元素設置為list_editable
。原因很簡單,你不能編輯沒顯示的字段或者作為超級鏈接的字段。
18. ModelAdmin.list_filter
設置list_filter
屬性后,可以激活修改列表頁面
的右側邊欄,用於對列表元素進行過濾,如下圖:
list_filter
必須是一個元組或列表,其元素是如下類型之一:
- 某個字段名,但該字段必須是BooleanField、CharField、DateField、DateTimeField、IntegerField、ForeignKey或者ManyToManyField中的一種。例如:
class PersonAdmin(admin.ModelAdmin): list_filter = ('is_staff', 'company')
在這里,你可以利用雙下划線進行跨表關聯,如下例:
class PersonAdmin(admin.UserAdmin): list_filter = ('company__name',)
- 一個繼承django.contrib.admin.SimpleListFilter的類。你要給這個類提供title和parameter_name的值,並重寫lookups和queryset方法。例如:
from datetime import date from django.contrib import admin from django.utils.translation import ugettext_lazy as _ class DecadeBornListFilter(admin.SimpleListFilter): # 提供一個可讀的標題 title = _('出生年代') # 用於URL查詢的參數. parameter_name = 'decade' def lookups(self, request, model_admin): """ 返回一個二維元組。每個元組的第一個元素是用於URL查詢的真實值, 這個值會被self.value()方法獲取,並作為queryset方法的選擇條件。 第二個元素則是可讀的顯示在admin頁面右邊側欄的過濾選項。 """ return ( ('80s', _('80年代')), ('90s', _('90年代')), ) def queryset(self, request, queryset): """ 根據self.value()方法獲取的條件值的不同執行具體的查詢操作。 並返回相應的結果。 """ if self.value() == '80s': return queryset.filter(birthday__gte=date(1980, 1, 1), birthday__lte=date(1989, 12, 31)) if self.value() == '90s': return queryset.filter(birthday__gte=date(1990, 1, 1), birthday__lte=date(1999, 12, 31)) class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', "colored_first_name",'birthday') list_filter = (DecadeBornListFilter,)
其效果如下圖:
注意:為了方便,我們通常會將HttpRequest對象傳遞給lookups和queryset方法,如下所示:
class AuthDecadeBornListFilter(DecadeBornListFilter): def lookups(self, request, model_admin): if request.user.is_superuser: return super(AuthDecadeBornListFilter, self).lookups(request, model_admin) def queryset(self, request, queryset): if request.user.is_superuser: return super(AuthDecadeBornListFilter, self).queryset(request, queryset)
同樣的,我們默認將ModelAdmin對象傳遞給lookups方法。下面的例子根據查詢結果,調整過濾選項,如果某個年代沒有符合的對象,則這個選項不會在右邊的過濾欄中顯示:
class AdvancedDecadeBornListFilter(DecadeBornListFilter): def lookups(self, request, model_admin): """ 只有存在確切的對象,並且它出生在對應年代時,才會出現這個過濾選項。 """ qs = model_admin.get_queryset(request) if qs.filter(birthday__gte=date(1980, 1, 1), birthday__lte=date(1989, 12, 31)).exists(): yield ('80s', _('in the eighties')) if qs.filter(birthday__gte=date(1990, 1, 1), birthday__lte=date(1999, 12, 31)).exists(): yield ('90s', _('in the nineties'))
- 也可以是一個元組。它的第一個元素是個字段名,第二個元素則是繼承了
django.contrib.admin.FieldListFilter
的類。例如:
class PersonAdmin(admin.ModelAdmin): list_filter = ( ('is_staff', admin.BooleanFieldListFilter), )
你可以使用RelatedOnlyFieldListFilter
限制關聯的對象。假設author是關聯User模型的ForeignKey,下面的用法將只選擇那些出過書的user而不是所有的user:
class BookAdmin(admin.ModelAdmin): list_filter = ( ('author', admin.RelatedOnlyFieldListFilter), )
另外,其template屬性可以指定渲染的模板,如下則指定了一個自定義的模板。(Django默認的模板為admin/filter.html)
class FilterWithCustomTemplate(admin.SimpleListFilter): template = "custom_template.html"
19. ModelAdmin.list_max_show_all
設置一個數值,當列表元素總數小於這個值的時候,將顯示一個“show all”鏈接,點擊后就能看到一個展示了所有元素的頁面。該值默認為200.
20. ModelAdmin.list_per_page
設置每頁顯示多少個元素。Django自動幫你分頁。默認為100。
21. ModelAdmin.list_select_related
如果設置了list_select_related
屬性,Django將會使用select_related()
方法查詢數據,這可能會幫助你減少一些數據庫訪問。
屬性的值可以是布爾值、元組或列表,默認為False。當值為True時,將始終調用select_related()
方法;如果值為False,Django將查看list_display
屬性,只對ForeignKey字段調用select_related()
方法。
如果你需要更細粒度的控制,請賦值一個元組(或列表)。空元組將阻止select_related()
方法,否則元組會被當做參數傳遞給select_related()
方法。例如:
class ArticleAdmin(admin.ModelAdmin): list_select_related = ('author', 'category')
這將會調用select_related('author', 'category')
。
22. ModelAdmin.ordering
設置排序的方式。
屬性的值必須為一個元組或列表,格式和模型的ordering參數一樣。如果不設置這個屬性,Django將按默認方式進行排序。如果你想進行動態排序,請自己實現get_ordering()方法。
23. ModelAdmin.paginator
指定用於分頁的分頁器。默認情況下,分頁器用的是Django自帶的django.core.paginator.Paginator
。如果自定義分頁器的構造函數接口和django.core.paginator.Paginator
的不一樣,那你還需要自己實現ModelAdmin.get_paginator()
方法。
24. ModelAdmin.prepopulated_fields
設置預填充字段。不接收DateTimeField、ForeignKey和ManyToManyField類型的字段。
class ArticleAdmin(admin.ModelAdmin): prepopulated_fields = {"slug": ("title",)}
25. ModelAdmin.preserve_filters
默認情況下,當你對目標進行創建、編輯或刪除操作后,頁面會依然保持原來的過濾狀態。將preserve_filters
設為False后,則會返回未過濾狀態。
26. ModelAdmin.radio_fields
默認情況下,Django使用select標簽顯示ForeignKey或choices集合。如果將這種字段設置為radio_fields
,則會以radio_box
標簽的形式展示。下面的例子假設group是Person模型的ForeignKey字段,:
class PersonAdmin(admin.ModelAdmin): # 垂直布局。(肯定也有水平布局HORIZONTAL的啦) radio_fields = {"group": admin.VERTICAL}
注意:不要將ForeignKey或choices集合之外的字段類型設置給這個屬性。
27. ModelAdmin.raw_id_fields
這個屬性會改變默認的ForeignKey和ManyToManyField的展示方式,它會變成一個輸入框,用於輸入關聯對象的主鍵id。對於ManyToManyField,id以逗號分隔。並且再輸入框右側提供一個放大鏡的圖標,你可以點擊進入選擇界面。例如:
class PersonAdmin(admin.ModelAdmin): raw_id_fields = ("group",)
28. ModelAdmin.readonly_fields
該屬性包含的字段在頁面內將展示為不可編輯狀態。它還可以展示模型或者ModelAdmin本身的方法的返回值,類似ModelAdmin.list_display
的行為。參考下面的例子:
from django.contrib import admin from django.utils.html import format_html_join from django.utils.safestring import mark_safe class PersonAdmin(admin.ModelAdmin): readonly_fields = ('address_report',) def address_report(self, instance): # assuming get_full_address() returns a list of strings # for each line of the address and you want to separate each # line by a linebreak return format_html_join( mark_safe('<br/>'), '{}', ((line,) for line in instance.get_full_address()), ) or mark_safe("<span class='errors'>I can't determine this address.</span>") # short_description functions like a model field's verbose_name address_report.short_description = "Address"
29. ModelAdmin.save_as
默認情況下,它的值為False。如果設置為True,那么右下角的“Save and add another”按鈕將被替換成“Save as new”,意思也變成保存為一個新的對象。
30. ModelAdmin.save_as_continue
默認值為True, 在保存新對象后跳轉到該對象的修改頁面。但是如果這時save_as_continue=False
,則會跳轉到元素列表頁面。
31. ModelAdmin.save_on_top
默認為False。 設為True時,頁面的頂部會提供同樣的一系列保存按鈕。
32. ModelAdmin.search_fields
設置這個屬性,可以為admin的修改列表頁面添加一個搜索框。
被搜索的字段可以是CharField或者TextField文本類型,也可以通過雙下划線進行ForeignKey或者ManyToManyField的查詢,格式為search_fields = ['foreign_key__related_fieldname']
.
例如:如果作者是博客的ForeignKey字段,下面的方式將通過作者的email地址來查詢對應的博客,也就是email地址是查詢值的作者所寫的所有博客。
search_fields = ['user__email']
當你在搜索框里輸入一些文本的時候,Django會將文本分割成一個一個的關鍵字,並返回所有包含這些關鍵字的對象,必須注意的是,每個關鍵詞至少得是search_fields
其中之一。例如,如果search_fields
是['first_name', 'last_name']
,當用戶輸入John lennon
時(注意中間的空格),Django將執行等同於下面的SQL語法WHERE子句:
WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%') AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')
如果要執行更加嚴格的匹配或搜索,可以使用一些元字符,例如“^”。類似正則,它代表從開頭匹配。例如,如果search_fields
是['^first_name','^last_name']
,當用戶輸入“John lennon”時(注意中間的空格),Django將執行等同於下面的SQL語法WHERE子句:
WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%') AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%')
也可以使用“=”,來進行區分大小寫的並絕對相等的嚴格匹配。例如,如果search_fields
是['=first_name','=last_name']
,當用戶輸入“John lennon”時(注意中間的空格),Django將執行等同於下面的SQL語法WHERE子句:
WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john') AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon')
33. ModelAdmin.show_full_result_count
用於設置是否顯示一個過濾后的對象總數的提示信息,例如“99 results (103 total)”。如果它被設置為False,那么顯示的將是“ 99 results (Show all)”。 默認情況下,它的值為True,這將會對整個表進行一個count操作,在表很大的時候,可能會耗費一定的時間和資源。
34. ModelAdmin.view_on_site
這個屬性可以控制是否在admin頁面顯示“View site”的鏈接。這個鏈接主要用於跳轉到你指定的URL頁面。
屬性的值可以是布爾值或某個調用。如果是True(默認值),對象的get_absolute_url()
方法將被調用並生成rul。
如果你的模型有一個get_absolute_url()
方法,但你不想顯示“View site”鏈接,你只需要將view_on_site
屬性設置為False。
from django.contrib import admin class PersonAdmin(admin.ModelAdmin): view_on_site = False
如果屬性的值是一個調用,它將接收一個模型實例作為參數:
from django.contrib import admin from django.urls import reverse class PersonAdmin(admin.ModelAdmin): def view_on_site(self, obj): url = reverse('person-detail', kwargs={'slug': obj.slug}) return 'https://example.com' + url
ModelAdmin