Django內置Admin解析


Django 內置的admin是對於model中對應的數據表進行增刪改查提供的組建

一.Django admin的內部依賴:

依賴的app 

django.contrib.auth
django.contrib.contenttypes
django.contrib.messages
django.contrib.sessions

模版:

 django.contrib.auth.context_processors.auth
 django.contrib.messages.context_processors.messages

中間件:

 django.contrib.auth.middleware.AuthenticationMiddleware 
 django.contrib.messages.middleware.MessageMiddleware

二:配置路由

from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

當前配置的路由可以創建一些路由映射關系:

/admin/
/admin/login/
/admin/logout/
/admin/password_change/
/admin/password_change/done/

/admin/app名稱/model名稱/
/admin/app名稱/model名稱/add/
/admin/app名稱/model名稱/ID值/history/
/admin/app名稱/model名稱/ID值/change/
/admin/app名稱/model名稱/ID值/delete/

class AdminSite(object):
    """
    An AdminSite object encapsulates an instance of the Django admin application, ready
    to be hooked in to your URLconf. Models are registered with the AdminSite using the
    register() method, and the get_urls() method can then be used to access Django view
    functions that present a full admin interface for the collection of registered
    models.
    """

    # Text to put at the end of each page's <title>.
    site_title = ugettext_lazy('Django site admin')

    # Text to put in each page's <h1>.
    site_header = ugettext_lazy('Django administration')

    # Text to put at the top of the admin index page.
    index_title = ugettext_lazy('Site administration')

    # URL for the "View site" link at the top of each admin page.
    site_url = '/'

    _empty_value_display = '-'

    login_form = None
    index_template = None
    app_index_template = None
    login_template = None
    logout_template = None
    password_change_template = None
    password_change_done_template = None

    def __init__(self, name='admin'):
        self._registry = {}  # model_class class -> admin_class instance
        self.name = name
        self._actions = {'delete_selected': actions.delete_selected}
        self._global_actions = self._actions.copy()
        all_sites.add(self)

    

    def register(self, model_or_iterable, admin_class=None, **options):
        """
        Registers the given model(s) with the given admin class.

        The model(s) should be Model classes, not instances.

        If an admin class isn't given, it will use ModelAdmin (the default
        admin options). If keyword arguments are given -- e.g., list_display --
        they'll be applied as options to the admin class.

        If a model is already registered, this will raise AlreadyRegistered.

        If a model is abstract, this will raise ImproperlyConfigured.
        """
        if not admin_class:
            admin_class = ModelAdmin

        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if model._meta.abstract:
                raise ImproperlyConfigured(
                    'The model %s is abstract, so it cannot be registered with admin.' % model.__name__
                )

            if model in self._registry:
                raise AlreadyRegistered('The model %s is already registered' % model.__name__)

            # Ignore the registration if the model has been
            # swapped out.
            if not model._meta.swapped:
                # If we got **options then dynamically construct a subclass of
                # admin_class with those **options.
                if options:
                    # For reasons I don't quite understand, without a __module__
                    # the created class appears to "live" in the wrong place,
                    # which causes issues later on.
                    options['__module__'] = __name__
                    admin_class = type("%sAdmin" % model.__name__, (admin_class,), options)

                # Instantiate the admin class to save in the registry
                self._registry[model] = admin_class(model, self)

    def get_urls(self):
        from django.conf.urls import url, include
        # Since this module gets imported in the application's root package,
        # it cannot import models from other applications at the module level,
        # and django.contrib.contenttypes.views imports ContentType.
        from django.contrib.contenttypes import views as contenttype_views

        def wrap(view, cacheable=False):
            def wrapper(*args, **kwargs):
                return self.admin_view(view, cacheable)(*args, **kwargs)
            wrapper.admin_site = self
            return update_wrapper(wrapper, view)

        # Admin-site-wide views.
        urlpatterns = [
            url(r'^$', wrap(self.index), name='index'),
            url(r'^login/$', self.login, name='login'),
            url(r'^logout/$', wrap(self.logout), name='logout'),
            url(r'^password_change/$', wrap(self.password_change, cacheable=True), name='password_change'),
            url(r'^password_change/done/$', wrap(self.password_change_done, cacheable=True),
                name='password_change_done'),
            url(r'^jsi18n/$', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'),
            url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', wrap(contenttype_views.shortcut),
                name='view_on_site'),
        ]

        # Add in each model's views, and create a list of valid URLS for the
        # app_index
        valid_app_labels = []
        for model, model_admin in self._registry.items():
            urlpatterns += [
                url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
            ]
            if model._meta.app_label not in valid_app_labels:
                valid_app_labels.append(model._meta.app_label)

        # If there were ModelAdmins registered, we should have a list of app
        # labels for which we need to allow access to the app_index view,
        if valid_app_labels:
            regex = r'^(?P<app_label>' + '|'.join(valid_app_labels) + ')/$'
            urlpatterns += [
                url(regex, wrap(self.app_index), name='app_list'),
            ]
        return urlpatterns

    @property
    def urls(self):
        return self.get_urls(), 'admin', self.name
源碼摘要參考
 option.py  BaseModelAdmin類

def get_urls(self):
        from django.conf.urls import url

        def wrap(view):
            def wrapper(*args, **kwargs):
                return self.admin_site.admin_view(view)(*args, **kwargs)
            wrapper.model_admin = self
            return update_wrapper(wrapper, view)

        info = self.model._meta.app_label, self.model._meta.model_name

        urlpatterns = [
            url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info),
            url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info),
            url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info),
            url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info),
            url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info),
            # For backwards compatibility (was the change url before 1.9)
            url(r'^(.+)/$', wrap(RedirectView.as_view(
                pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
            ))),
        ]
        return urlpatterns

    @property
    def urls(self):
        return self.get_urls()
源碼參考2

三:定制admin

在admin.py中只需注冊某個類,即可在admin中實現增刪改查的功能。

admin.site.register(UserInfo)

為類定制更多的信息,需為其定制modelAdmin

   1.應用方式:

#方式一:裝飾器
@admin.register(UserInfo)
class UserAdmin(admin.ModelAdmin):
    list_display = ['name','nickname','email']

#方式二:參數傳入
# admin.site.register(UserInfo,UserAdmin)

 2.定制功能:

   list_display,列表時,定制顯示的列。

   list_display 有四種賦值方式:

#模型的字段
class PersonAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name')
#一個接受對象實例作為參數的可調用對象
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,)

#一個表示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'

#表示模型中某個屬性的字符串。它的行為與可調用對象幾乎相同,但這時的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 數據類型

實例:

@admin.register(UserInfo)
class UserAdmin(admin.ModelAdmin):
    list_display = ['name','nickname','email','xxx']
    list_display_links = ('nickname',)

    def xxx(self,obj):
        a = '<a href="www.baidu.com>點擊跳轉</a>'
        return mark_safe(a)

   list_display_links,指定列表顯示的哪列可以點擊跳轉,定制列可以點擊跳轉。

  list_filter 設置激活激活Admin 修改列表頁面右側欄中的過濾器

#字段名稱,其指定的字段應該是BooleanField、CharField、DateField、DateTimeField、#IntegerField、ForeignKey 或ManyToManyField,
class UserAdmin(admin.ModelAdmin):
    list_filter = ('group','roles')

#list_filter 中的字段名稱也可以使用__ 查找跨關聯關系
list_filter = ('group__title','roles')

#一個繼承自django.contrib.admin.SimpleListFilter 的類,你需要給它提供title 和 parameter_name 屬性來重寫lookups 和queryset 方法

from django.contrib import admin
from app01.models import *
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _

class Ugg(admin.SimpleListFilter):
    title = _('decade born')
    parameter_name = 'xxxxxx'

    def lookups(self, request, model_admin):
        """
        顯示篩選選項
        :param request:
        :param model_admin:
        :return:
        """
        return UserGroup.objects.values_list('id', 'title')

    def queryset(self, request, queryset):
        """
        點擊查詢時,進行篩選
        :param request:
        :param queryset:
        :return:
        """
        v = self.value()
        return queryset


 list_filter = ('group',Ugg)
#一個元組,第一個元素是字段名稱,第二個元素是從繼承自django.contrib.admin.FieldListFilter 的一個類

class PersonAdmin(admin.ModelAdmin):
    list_filter = (
        ('is_staff', admin.BooleanFieldListFilter),
    )
list_filter 數據類型

    list_select_related,設置list_select_related以告訴Django在檢索管理更改列表頁面上的對象列表時使用  select_ralated。這可以節省大量的數據庫查詢。該值應該是布爾值,列表或元組。默認值為False

list_select_related = ['group']

  list_max_show_all 控制在“顯示所有”管理更改列表頁面上可以顯示的項目數,默認情況下,設置為200

  list_per_page. 設置控制Admin 修改列表頁面每頁中顯示多少項。默認設置為100

  ordering以指定如何在Django管理視圖中對對象列表進行排序 

 ordering = ['-id', ]. 從大到小
 ordering = ['id', ]. 從小到大

  paginator 默認情況下,使用django.core.paginator.Paginator

  prepopulated_fields設置為將字段名稱映射到其應預先填充的字段的字典:

 prepopulated_fields = {'name':('nickname','email')} #nickname 和email和name的值相同

          prepopulated_fields不接受DateTimeFieldForeignKeyManyToManyField字段。

  list_editable指定列表中可以編輯的列

list_editable = ('name',)

 search_fields,模糊搜索時列表中的搜索列范圍,

search_fields = ('namer', 'email')

date_hierarchy,列表時,對Date和DateTime類型進行搜索

preserve_filters,詳細頁面,刪除、修改,更新后跳轉回列表后,是否保留原搜索條件

save_as = False,詳細頁面,按鈕為“Sava as new” 或 “Sava and add another”

save_as_continue = True,點擊保存並繼續編輯

save_on_top = False,詳細頁面,在頁面上方是否也顯示保存刪除等按鈕

fields,詳細頁面時,顯示字段的字段

exclude,詳細頁面時,排除的字段

readonly_fields,詳細頁面時,只讀字段

fieldsets,詳細頁面時,使用fieldsets標簽對數據進行分組顯示

   fieldsets = (
        ('基本數據', {
            'fields': ('name', )
        }),
        ('其他', {
            'classes': ('collapse', 'wide', 'extrapretty'),  # 'collapse','wide', 'extrapretty'
            'fields': ('email','group',),
        }),
    )

多對多或一對多顯示時添加數據移動選擇(方向:上下和左右)

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):
    filter_vertical = ("m2m字段",) # 或filter_horizontal = ("m2m字段",)

inlines,詳細頁面,如果有其他表和當前表做FK,那么詳細頁面可以進行動態增加和刪除

inlines 關聯的類必須繼承:StackedInline,且放在外鍵的類里

class UserInfoInline(admin.StackedInline): # TabularInline
    extra = 0
    model = UserInfo

class UserGropuAdmin(admin.ModelAdmin):
    inlines = [UserInfoInline,]

action  定制action中的操作

  def func(self, request, queryset): (必須有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

  在整個站點應用該操作:

from django.contrib import admin

admin.site.add_action(fund)

  為全局ModelAdmin禁用某個操作 

admin.site.disable_action('delete_selected')

  為特定的ModelAdmin禁用所有操作ModelAdmin

class MyModelAdmin(admin.ModelAdmin):
    actions = None

  按需啟用或禁用操作

class MyModelAdmin(admin.ModelAdmin):
    ...

    def get_actions(self, request):
        actions = super(MyModelAdmin, self).get_actions(request)
        if request.user.username[0].upper() != 'J':
            if 'delete_selected' in actions:
                del actions['delete_selected']
        return actions

 定制HTML模板

  Admin模板文件位於contrib/admin/templates/admin 目錄中。

       為一個特定的app重寫admin模板, 需要拷貝django/contrib/admin/templates/admin 目錄到你剛才創       建的目錄下, 並且修改它們.

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

view_on_site,編輯時,是否在頁面上顯示view on set

view_on_site = False
或
def view_on_site(self, obj):
    return 'https://www.baidu.com'

radio_fields,詳細頁面時,使用radio顯示選項(FK默認使用select)

radio_fields = {"ug": admin.VERTICAL} # 或admin.HORIZONTAL

show_full_result_count = True,列表時,模糊搜索后面顯示的數據個數樣式

@admin.register(models.UserInfo)
class UserAdmin(admin.ModelAdmin):
    # show_full_result_count = True # 1 result (12 total)
    # show_full_result_count = False  # 1 result (Show all)
    search_fields = ('user',)

form = ModelForm,用於定制用戶請求時候表單驗證

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 = "指定列數據為空時,默認顯示"
  

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM