Django admin 组件 原理分析与扩展使用 之 sites.py (一)
一 、 前言
Django 提供了admin 组件 为项目提供基本的管理后台功能(对数据表的增删改查)。
本篇文章通过 admin源码 简单分析admin 内部原理 ,扩展使用方式,为以后进行定制和自己开发组件做铺垫。
二、 简单使用
1.在app 目录下的admin.py 中通过注册表
- from django.contrib import admin
- from blog01.models import *
- admin.site.register([UserInfo,User,Blog])
- # 或者通过 @admin.register 装饰器实现
2. 创建root用户
- python manage.py createsuperuser
- #输入用户名
- #输入密码
- #再次输入密码
3. 登录admin后台进行管理
- 浏览器访问 http://127.0.0.1/admin/
三、admin简单分析
1. admin 是一个Django 提供的后台管理app,功能也比较强大,在敏捷开发的过程中可以考虑直接使用。
但是面对复杂的业务情况,要实现更高的定制,必然要求我们实现自己的admin组件,这样面对各种情况我们才能游刃有余。
2. admin 是通过”注册“类自动生成url,执行对应的视图函数,提供友好可视化界面,实现增删改查功能。
3. admin 内部 url 列表
- 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'),#将我们表格生成url
4. 注册类生成的url
- 127.0.0.1/admin/appname/classname/ #查看数据
- 127.0.0.1/admin/appname/classname/add #增加数据
- 127.0.0.1/admin/appname/classname/id/delete #删除数据
- 127.0.0.1/admin/appname/classname/id/change #更新数据
- 127.0.0.1/admin/appname/classname/id/history #历史记录
四、 admin 流程分析之sites.py 分析
1.从目录开始
下图是django.contrib.admin 目录。可以看见熟悉的static,templates,views,migrations目录,说明admin 是一个app。
2. 从 admin.site.register( model_or_iterable, admin_class=None,) 分析
admin 是什么?
是一个后台管理app
site 是什么?
点开发现是来自sites.py 中的一个实例,代表当前admin站点,也就是通过模块导入方式实现的单例模式。下面为site.py 中源码,后续如不说明,均为admin源码材料。
- # This global object represents the default admin site, for the common case.
- # You can instantiate AdminSite in your own code to create a custom admin site.
- # 这个全局对象代表了在一般情况下的默认admin 站点
- # 你可以在你自己的代码中实例化AdminSite来创造一个自定义的admin 站点
- site = AdminSite()
register 是什么?
是site的一个方法,也就是site的类AdminSite的一个方法,
- 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.'''
- '''用提供的admin 类给model(我们的表格)注册,必须给Model类,而不是实例
- 如果没有指定admin类,会用默认的ModelAdmin,如果给了关键词参数,如list_display,他们会被作为选项应用在admin类中
- 如果一个model 已经被注册了,会报AlreadyRegistered异常
- 如果一个model是抽象的,这会引起ImproperlyConfigured异常。'''
小结:
所以我们做的事是将 代表我们表格的类 传给 site.py中 AdminSite类 实例化的site对象 的register 方法 进行注册,默认是 用 ModelAdmin 管理 。
site 对象就是生成的admin 站点。
3. 进入 sites.py
顾名思义是生成站点的文件,一共两个对象,三个类
第一个对象是”弱集合“,
第二个对象是我们需要的站点
第一个类是已经注册的异常,继承了Exception,第二个类是没有注册的异常,同样继承了Exception,无内容,两个用来抛异常的类。
第三个是重点关注,生成站点的类AdminSite。
- 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.
- 一个AdminSite对象封装了Django管理应用程序的一个实例,准备被挂钩到你的URLconf。
- 使用register()方法向AdminSite注册模型,
- 然后可以使用get_urls()方法访问为注册模型集合提供完整管理界面的Django视图函数。
- """
- # Text to put at the end of each page's <title>.
- # 放在每页<title>的文本
- site_title = ugettext_lazy('Django site admin')
- # Text to put in each page's <h1>.
- # 放在每页<h1>的文本
- site_header = ugettext_lazy('Django administration')
- # Text to put at the top of the admin index page.
- # 放在admin 主页顶部的文本
- index_title = ugettext_lazy('Site administration')
- # URL for the "View site" link at the top of each admin page.
- # 根url
- 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
下面来看 AdminSite 的 25 个方法和相关内容
- def __init__(self, name='admin'):
- self._registry = {} # model_class class -> admin_class instance 将model_class类转为admin_class实例,也就是我们的表放的地方
- self.name = name # 站点名
- self._actions = {'delete_selected': actions.delete_selected} # 默认行为,删除选中,在actions.py 中只有这一个方法
- self._global_actions = self._actions.copy() # 全局行为,复制默认行为
- all_sites.add(self) # 将实例加入all_sites 这个’弱集合’
解释: 初始化一些变量,一些方法如 delete_selected,暂时不讨论内部如何实现。
- def check(self, app_configs):
- """
- Run the system checks on all ModelAdmins, except if they aren't customized at all.
- 如果没有自定义,就对所有ModelAdmins进行系统检查
- """
- if app_configs is None:
- app_configs = apps.get_app_configs() # 没有传配置,就去apps对象中拿配置信息
- app_configs = set(app_configs) # Speed up lookups below 加速下面查找(去重)
- errors = []
- modeladmins = (o for o in self._registry.values() if o.__class__ is not ModelAdmin)#生成器加递归检查,将不是ModelAdmin的对象放入erros列表
- for modeladmin in modeladmins:
- if modeladmin.model._meta.app_config in app_configs:
- errors.extend(modeladmin.check())
- return errors
解释:apps 是django.apps.register.py 中 Apps 类实例的一个对象,存储已安装应用程序配置的注册表。它也跟踪模型,例如。 提供反向关系。后续有时间研究。
这个方法主要拿到配置信息和错误对象。
- def register(self, model_or_iterable, admin_class=None, **options):
- """
- Registers the given model(s) with the given admin class.
- 用提供的admin 类 注册给的表 model
- The model(s) should be Model classes, not instances.
- 必须给Model类,而不是实例
- 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.
- 如果没有指定admin类,会用默认的ModelAdmin,如果给了关键词参数,如list_display,
- 他们会被作为选项应用在admin类中
- If a model is already registered, this will raise AlreadyRegistered.
- 如果一个model 已经被注册了,会报AlreadyRegistered异常
- If a model is abstract, this will raise ImproperlyConfigured.
- 如果一个model是抽象的,这会引起ImproperlyConfigured异常。
- """
- if not admin_class:
- admin_class = ModelAdmin # 如果没指定,就用ModelAdmin
- if isinstance(model_or_iterable, ModelBase): # 如果输入的是一个代表表格的类,就把它变成列表,所以能传类或者列表,ModelBase是Model的元类
- model_or_iterable = [model_or_iterable]
- for model in model_or_iterable: # 判断列表中每个类是不是抽象类,如果是,抛出异常,背后比较复杂,在ModelBase中实现,有空研究
- 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: #如果没有被 swapped,继续,同样在ModelBase 中属性,不太明白
- # 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__ # 作者也不知道为什么,就是要加__model__属性
- # 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) # 用type函数将自定义属性添加到默认的ModelAdmin 中,生成新的类
- # Instantiate the admin class to save in the registry # 将表格的类作为键,将ModelAdmin或自定义后的ModelAdmin 用 该类和site实例 生成的
- self._registry[model] = admin_class(model, self) # 作为键值
解释:1. 该函数目的是将我们的表格和管理的类结合一一对应下来,
2. ype函数有两种用法:
- type(object) -> the object's type
type(name, bases, dict) -> a new type
3. **options 是可扩展的功能,在admin 的options.py 中有详细列出,之后在高级定制中讨论。
- def unregister(self, model_or_iterable):
- """
- Unregisters the given model(s).
- If a model isn't already registered, this will raise NotRegistered.
- """
- if isinstance(model_or_iterable, ModelBase):
- model_or_iterable = [model_or_iterable]
- for model in model_or_iterable:
- if model not in self._registry:
- raise NotRegistered('The model %s is not registered' % model.__name__)
- del self._registry[model]
- def is_registered(self, model):
- """
- Check if a model class is registered with this `AdminSite`.
- """
- return model in self._registry
解释: 取消注册和判断是否注册,本质就是判断对象是否在我们生成的字典中
- def add_action(self, action, name=None):
- """
- Register an action to be available globally.
注册新的操作- """
- name = name or action.__name__
- self._actions[name] = action
- self._global_actions[name] = action
- def disable_action(self, name):
- """
- Disable a globally-registered action. Raises KeyError for invalid names.
删除已有操作- """
- del self._actions[name]
- def get_action(self, name):
- """
- Explicitly get a registered global action whether it's enabled or
- not. Raises KeyError for invalid names.
返回全局操作,无论是否运行 ,- """
- return self._global_actions[name]
- @property
- def actions(self):
- """
- Get all the enabled actions as an iterable of (name, func).
获得所有运行的操作组成的可迭代的元组,如(name,func),property装饰器将方法变为属性调用- """
- return six.iteritems(self._actions)
解释:1. 操作增删改查的行为,默认是删除选中这一种,
2. six.iteritems 目的, 兼容py2实现 将目标字典转为 迭代器
- @property
- def empty_value_display(self):
- return self._empty_value_display
- @empty_value_display.setter
- def empty_value_display(self, empty_value_display):
- self._empty_value_display = empty_value_display
解释:默认空值显示 ’-‘, 可以自定义空值符号,调用property的setter方法实现
- def has_permission(self, request):
- """
- Returns True if the given HttpRequest has permission to view #检查登录权限
- *at least one* page in the admin site.
- """
- return request.user.is_active and request.user.is_staff
- def admin_view(self, view, cacheable=False):
- """
- Decorator to create an admin view attached to this ``AdminSite``. This
- wraps the view and provides permission checking by calling
- ``self.has_permission``.
- You'll want to use this from within ``AdminSite.get_urls()``:
- class MyAdminSite(AdminSite):
- def get_urls(self):
- from django.conf.urls import url
- urls = super(MyAdminSite, self).get_urls()
- urls += [
- url(r'^my_view/$', self.admin_view(some_view))
- ]
- return urls
- By default, admin_views are marked non-cacheable using the
- ``never_cache`` decorator. If the view can be safely cached, set
- cacheable=True.
- 用来创造添在这个"AdminSite"的视图函数的装饰器,其中调用 self.has_permission 检查权限,
我们也可以用此函数来自定义我们需要在admin后台出现的视图
默认是不缓存,如果确认是安全缓存的,就设置 cacheable = False- """
- def inner(request, *args, **kwargs):
- if not self.has_permission(request): #如果没有权限,
- if request.path == reverse('admin:logout', current_app=self.name): #如果为登出,就转到首页
- index_path = reverse('admin:index', current_app=self.name)
- return HttpResponseRedirect(index_path)
- # Inner import to prevent django.contrib.admin (app) from # 在此处导入而不是开头是因为要防止从无关的用户认证组件导入
- # importing django.contrib.auth.models.User (unrelated model).
- from django.contrib.auth.views import redirect_to_login
- return redirect_to_login(
- request.get_full_path(),
- reverse('admin:login', current_app=self.name) #记录想去的页面之后,跳转登录页面,登录成功进入想去页面
- )
- return view(request, *args, **kwargs)
- if not cacheable:
- inner = never_cache(inner) # 通过 never_cache 闭包函数在request上加header 设置不缓存
- # We add csrf_protect here so this function can be used as a utility
- # function for any view, without having to repeat 'csrf_protect'.
- if not getattr(view, 'csrf_exempt', False): # 如果没有明确说 取消"csrf"机制,那就通过 csrf_poctect 闭包添加
- inner = csrf_protect(inner)
- return update_wrapper(inner, view)
解释:用来创立admin自己的视图函数。
- 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.
"""
这个模块在app 根包里导入了,它无法在其他app 里从模块水平导入,
- """
- 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
# 生成每一个表的视图函数和url列表,appname/modelname/ 开头,- 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,
# 如果有注册的表,生成到显示某个app内所有表格信息的页面。- 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
解释:很明显,这个函数是生成url的核心函数,url列表包括:固定的(login logout 等),根据注册表拼接的(app名/表名/),还有某一app(app名/)
- def each_context(self, request):
- """
- Returns a dictionary of variables to put in the template context for
- *every* page in the admin site.
- For sites running on a subpath, use the SCRIPT_NAME value if site_url
- hasn't been customized.
返回一个每页都有的变量组成的字典,在子路径的页面,如果没有定制,就用SCRIPT_NAME 的值- """
- script_name = request.META['SCRIPT_NAME']
- site_url = script_name if self.site_url == '/' and script_name else self.site_url
- return {
- 'site_title': self.site_title,
- 'site_header': self.site_header,
- 'site_url': site_url,
- 'has_permission': self.has_permission(request),
- 'available_apps': self.get_app_list(request),
- }
解释: 用来传递通用变量
- def password_change(self, request, extra_context=None):
- """
- Handles the "change password" task -- both form display and validation.
解决改密码任务, 表单展示和验证- """
- from django.contrib.admin.forms import AdminPasswordChangeForm
- from django.contrib.auth.views import PasswordChangeView
- url = reverse('admin:password_change_done', current_app=self.name)
- defaults = {
- 'form_class': AdminPasswordChangeForm,
- 'success_url': url,
- 'extra_context': dict(self.each_context(request), **(extra_context or {})),
- }
- if self.password_change_template is not None:
- defaults['template_name'] = self.password_change_template
- request.current_app = self.name
- return PasswordChangeView.as_view(**defaults)(request) #as_view 完整性检查
- def password_change_done(self, request, extra_context=None):
- """
- Displays the "success" page after a password change.
展示修改密码成功界面- """
- from django.contrib.auth.views import PasswordChangeDoneView
- defaults = {
- 'extra_context': dict(self.each_context(request), **(extra_context or {})),
- }
- if self.password_change_done_template is not None:
- defaults['template_name'] = self.password_change_done_template
- request.current_app = self.name
- return PasswordChangeDoneView.as_view(**defaults)(request)
- def i18n_javascript(self, request, extra_context=None):
- """
- Displays the i18n JavaScript that the Django admin requires.
- `extra_context` is unused but present for consistency with the other
- admin views.
展示 Django admin 需要的多语言js
- """
- return JavaScriptCatalog.as_view(packages=['django.contrib.admin'])(request)
解释: 逻辑一样,先设置默认字典,有成功后url,当前表单,额外上下文变量(在默认中添加),模板名(默认或自定义),
传入cbv的PasswordChangeView,实现修改密码,等视图函数
- @never_cache
- def logout(self, request, extra_context=None):
- """
- Logs out the user for the given HttpRequest.
- This should *not* assume the user is already logged in.
- """
- from django.contrib.auth.views import LogoutView
- defaults = {
- 'extra_context': dict(
- self.each_context(request),
- # Since the user isn't logged out at this point, the value of
- # has_permission must be overridden.
- has_permission=False,
- **(extra_context or {})
- ),
- }
- if self.logout_template is not None:
- defaults['template_name'] = self.logout_template
- request.current_app = self.name
- return LogoutView.as_view(**defaults)(request)
- @never_cache
- def login(self, request, extra_context=None):
- """
- Displays the login form for the given HttpRequest.
- """
- if request.method == 'GET' and self.has_permission(request):
- # Already logged-in, redirect to admin index
- index_path = reverse('admin:index', current_app=self.name)
- return HttpResponseRedirect(index_path)
- from django.contrib.auth.views import LoginView
- # 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.admin.forms eventually imports User.
- from django.contrib.admin.forms import AdminAuthenticationForm
- context = dict(
- self.each_context(request),
- title=_('Log in'),
- app_path=request.get_full_path(),
- username=request.user.get_username(),
- )
- if (REDIRECT_FIELD_NAME not in request.GET and
- REDIRECT_FIELD_NAME not in request.POST):
- context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
- context.update(extra_context or {})
- defaults = {
- 'extra_context': context,
- 'authentication_form': self.login_form or AdminAuthenticationForm,
- 'template_name': self.login_template or 'admin/login.html',
- }
- request.current_app = self.name
- return LoginView.as_view(**defaults)(request)
解释:login logout 同上
- def _build_app_dict(self, request, label=None):
- """
- Builds the app dictionary. Takes an optional label parameters to filter
- models of a specific app.
- """
- app_dict = {}
- if label:
- models = {
- m: m_a for m, m_a in self._registry.items()
- if m._meta.app_label == label
- }
- else:
- models = self._registry
- for model, model_admin in models.items():
- app_label = model._meta.app_label
- has_module_perms = model_admin.has_module_permission(request)
- if not has_module_perms:
- continue
- perms = model_admin.get_model_perms(request)
- # Check whether user has any perm for this module.
- # If so, add the module to the model_list.
- if True not in perms.values():
- continue
- info = (app_label, model._meta.model_name)
- model_dict = {
- 'name': capfirst(model._meta.verbose_name_plural),
- 'object_name': model._meta.object_name,
- 'perms': perms,
- }
- if perms.get('change'):
- try:
- model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
- except NoReverseMatch:
- pass
- if perms.get('add'):
- try:
- model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
- except NoReverseMatch:
- pass
- if app_label in app_dict:
- app_dict[app_label]['models'].append(model_dict)
- else:
- app_dict[app_label] = {
- 'name': apps.get_app_config(app_label).verbose_name,
- 'app_label': app_label,
- 'app_url': reverse(
- 'admin:app_list',
- kwargs={'app_label': app_label},
- current_app=self.name,
- ),
- 'has_module_perms': has_module_perms,
- 'models': [model_dict],
- }
- if label:
- return app_dict.get(label)
- return app_dict
- def get_app_list(self, request):
- """
- Returns a sorted list of all the installed apps that have been
- registered in this site.
- """
- app_dict = self._build_app_dict(request)
- # Sort the apps alphabetically.
- app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower())
- # Sort the models alphabetically within each app.
- for app in app_list:
- app['models'].sort(key=lambda x: x['name'])
- return app_list
解释: 建立app的字典 在排序
- @never_cache
- def index(self, request, extra_context=None):
- """
- Displays the main admin index page, which lists all of the installed
- apps that have been registered in this site.
- """
- app_list = self.get_app_list(request)
- context = dict(
- self.each_context(request),
- title=self.index_title,
- app_list=app_list,
- )
- context.update(extra_context or {})
- request.current_app = self.name
- return TemplateResponse(request, self.index_template or 'admin/index.html', context)
- def app_index(self, request, app_label, extra_context=None):
- app_dict = self._build_app_dict(request, app_label)
- if not app_dict:
- raise Http404('The requested admin page does not exist.')
- # Sort the models alphabetically within each app.
- app_dict['models'].sort(key=lambda x: x['name'])
- app_name = apps.get_app_config(app_label).verbose_name
- context = dict(
- self.each_context(request),
- title=_('%(app)s administration') % {'app': app_name},
- app_list=[app_dict],
- app_label=app_label,
- )
- context.update(extra_context or {})
- request.current_app = self.name
- return TemplateResponse(request, self.app_index_template or [
- 'admin/%s/app_index.html' % app_label,
- 'admin/app_index.html'
- ], context)
解释: index 好理解,就是将之前的处理数据渲染主页模板,app_index 就是 显示所有app 的页面
小结:25 种方法 实现了admin站点的基本功能和接口,有注册方面,操作方面,默认空值符,生成url,修改密码,登录登出,主页。
里面包含了许多编程思想和方法,值得继续深入研究。
五、总结
在这篇文章中,通过基本使用,分析了admin组件第一步相关的sites源码,理解了site 这个对象的构造方式和包含方法。
Django admin 组件 原理分析与扩展使用 之 sites.py (一)的更多相关文章
- 自定义 Django admin 组件
摘要:学习 Django admin 组件,仿照源码的逻辑,自定义了一个简易的 stark 组件,实现类似 admin 的功能. 可自动生成 url 路由,对于model 有与之相应的配置类对象,可进 ...
- Django admin组件使用
ADMIN 组件 介绍 admin 组件实现了更方便的WEB后台数据管理方式 settings.py 中第一个组件就是 : INSTALLED_APPS = [ 'django.contrib.adm ...
- Django admin组件源码流程
admin 组件 Django 自带的用户后台组件 用于用户便携的操作 admin 组件核心 启动 注册 设计url 启动核心代码 每个app 通过 apps.py 扫描 admin.py 文件 并执 ...
- Django——admin源码分析
在Django中,如果我们新建一个项目,只要在admin.py文件中注册,就可以对其相应的文件进行增删改查操作. 而我们在路由系统中只看到了一条信息:url(r'^admin/', admin.sit ...
- day 82 Django Admin组件.
一.先建表环境 modules文件 from django.db import models # Create your models here. from django.contrib.auth.m ...
- Django——admin组件
Django提供了基于web的管理工具. Django自动管理工具是django.contrib的一部分.你可以在项目的settings.py中的INSTALLED_APPS看到它: # Applic ...
- Django中的admin组件分析
admin的使用介绍 django-admin的使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.可以在项目的 setting ...
- python框架之Django(13)-admin组件
使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你可以在项目的 settings.py 中的 INSTALLED_APPS ...
- Django 之 admin组件使用&源码解析
admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.可以在项目的 settings.py 中的 INSTALLED ...
随机推荐
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 避免使用终结器,避免大对象,避免复制缓冲区
避免使用终结器 如果没有必要,是不需要实现一个终结器(Finalizer).终结器的代码主要是让GC回收非托管资源用.它会在GC完成标记对象为可回收后,放入一个终结器队列里,在由另外一个线程执行队列里 ...
- angular4升级angular5问题记录之this.location.back()
在之前的项目中,导航回上一个路由采用注入的Location服务,利用浏览器的历史堆栈,导航到上一步. 官方文档也就是这么写的 而然在升级到5.2的版本的时候,在浏览器运行的时候并没有什么问题,在项目打 ...
- PHP实现水印效果(文字、图片)
第一种 <?php /** * 功能:给一张图片加上水印效果 * $i 要加水印效果的图片 * $t 水印文字 * $size 文字大小 * $pos 水印的位置 * $color 文字的颜色 ...
- c#IO的学习
常见应用方面 一.路径的相关操作,如判定路径是否合法,路径类型,路径的特定部分,合并路径,系统文件夹路径等内容:二.相关通用文件对话框,这些对话框可以帮助我们操作文件系统中的文件和目录:三.文件.目录 ...
- 2015四川省acm B题
Carries frog has n integers a1,a2,-,an, and she wants to add them pairwise. Unfortunately, frog is s ...
- 沉淀,再出发——在Ubuntu Kylin15.04中配置Hadoop单机/伪分布式系统经验分享
在Ubuntu Kylin15.04中配置Hadoop单机/伪分布式系统经验分享 一.工作准备 首先,明确工作的重心,在Ubuntu Kylin15.04中配置Hadoop集群,这里我是用的双系统中的 ...
- jmeter 脚本规范
总结了一下公司正在用 jmeter 脚本规范. 使用 jmeter 进行接口级测试, 随着接口增多以及业务逻辑越来越复杂, 导致 jmeter 脚本的维护会更加困难.针对实际使用中发现的问题进行一些规 ...
- [JCIP笔记] (一)多线程的起源
在很久很久以前,那时的计算机还没有操作系统这种东西,所以只能有一个程序,从头到尾地跑.于是这个程序要负责使用所有的资源,还得响应外部请求.想想这个程序得多复杂啊--为了做成一件事,可能要先把内存啊.I ...
- 编译、裁剪、安装、删除 Ubuntu内核和模块管理
一.下载最新内核文件 地址:http://www.kernel.org,一般下载Full Source版本. 下载完毕后,放到任意文件夹中,使用命令: tar jxvf linux-x.x.x.tar ...
- 笔记本CPU低压和标压有什么区别?
笔记本CPU英文称Mobile CPU(移动CPU),它除了追求性能,也追求低热量和低耗电,最早的笔记本电脑直接使用台式机的CPU,但是随CPU主频的提高, 笔记本电脑狭窄的空间不能迅速散发CPU产生 ...