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 = "指定列数据为空时,默认显示"
  

 

 

 

  

 

posted @ 2017-10-18 19:09  皖心  阅读(2787)  评论(0编辑  收藏  举报