源码版本:H版
一、写在前面
二、horizon前端请求
主要用浏览器 chrome 的开发者工具分析页面发出的具体请求,此处是分析前端的一个 button 按钮执行情况。
请求链接地址:
其他相关元素:
总结如下:
三、URL后端绑定及APP的加载
根据Django的框架结构,使用URLconf文件(https://docs.djangoproject.com/en/1.4/topics/http/urls/)进行链接请求和后端处理(view)的绑定,使用view进行后端处理,使用template进行页面渲染。
1、目录结构:
horizon-------------组件,提供部分功能
|---__init__.py-----------控制着horizon包导入行为
|---base.py-----------horizon提供的Site类、Dashboard类、Panel类,负责整个基本架构
|---site_urls.py
openstack_dashboard----------------网站project根目录
|---settings.py------------网站基本设置【1】
|---urls.py----------------网站基本URL设置
|---views.py---------------网站基本view
|---templates--------------网站基本template
|---dashboards
|---admin ---------------dashboard【2】
|---instances --------------panel【3】
|---panel.py----------------------负责往dashboard中注册panel
|---urls.py
|---views.py
...
|---dashboard.py --------------负责往Horizon中注册dashboard
|---models.py
...
Horizon项目架构: 主要分为两个部分: horizon和openstack_dashboard。 horizon提供库和功能组件, openstack_dashboard 是一个使用了horizon的Django项目。2、URL绑定分析:
ROOT_URLCONF = 'openstack_dashboard.urls'
openstack_dashboard/urls.py
这里 使用了一个小trick,在导入horizon这个package时,进行如下处理:
可见,在package的__init__.py中控制包的导入行为,这样可以使包的使用更加简洁方便。接着分析如下:
part2:
part3:
综上所述,最终的include()导入的是Site类的_lazy_urls。
3、Site、Dashboard、Panel三者的加载
这里先说明Site、Dashboard和Panel三者的加载过程以便后面的进一步分析。Horizon采用了注册机制,即以一个对象为根对象,将其他组件注册到根对象的属性中,这种机制使得软件的可扩展性更强,并且条理清晰,貌似像一种设计模式来着。Horizon是一个Site类对象,往其中注册Dashboard类时会构建一个Dashboard对象注册到其属性_registry字典中;Panel类往Dashboard类中注册,注册时会构建Panel对象注册到Dashboard对象的_registry字典里。具体情况如下:
3.1 一个dashboard注册过程import horizon class SystemPanels(horizon.PanelGroup): slug = "admin" name = _("System Panel") panels = ('overview', 'metering', 'hypervisors', 'instances', 'volumes', 'flavors', 'images', 'networks', 'routers', 'defaults', 'info') class IdentityPanels(horizon.PanelGroup): slug = "identity" name = _("Identity Panel") panels = ('domains', 'projects', 'users', 'groups', 'roles') class Admin(horizon.Dashboard): name = _("Admin")#用于display slug = "admin"#用于内部引用 """panels可以用来发现与该Dashboard相关的所有Panel,以便在以后往Dashboard中注册这些Panel""" panels = (SystemPanels, IdentityPanels) default_panel = 'overview' permissions = ('openstack.roles.admin',) horizon.register(Admin)
horizon/base.py
Site类: def register(self, dashboard): """Registers a :class:`~horizon.Dashboard` with Horizon.""" return self._register(dashboard)
Registry类: def _register(self, cls): if not inspect.isclass(cls): raise ValueError('Only classes may be registered.') elif not issubclass(cls, self._registerable_class): raise ValueError('Only %s classes or subclasses may be registered.' % self._registerable_class.__name__) if cls not in self._registry: cls._registered_with = self self._registry[cls] = cls() return self._registry[cls]
在horizon/base.py中的Horizon对象的_registy映射中添加了Dashboard类à类实例的映射!
3.2 一个panel的注册过程
以admin这个Dashboard中的instancs为例:import horizon """将admin这个Dashboard的dashboard.py导入""" from openstack_dashboard.dashboards.admin import dashboard class Aggregates(horizon.Panel): name = _("Host Aggregates") slug = 'aggregates' permissions = ('openstack.services.compute',) """在Dashboard为Admin中进行注册""" dashboard.Admin.register(Aggregates)
horizon/base.py
Dashboard类: @classmethod def register(cls, panel): """检查cls是否已经注册到Horizon对象中,并且在cls中注册panel""" panel_class = Horizon.register_panel(cls, panel) panel_mod = import_module(panel.__module__) panel_dir = os.path.dirname(panel_mod.__file__) template_dir = os.path.join(panel_dir, "templates") if os.path.exists(template_dir): key = os.path.join(cls.slug, panel.slug) loaders.panel_template_dirs[key] = template_dir return panel_class
Site类: def _urls(self): """实际调用HorizonComponent._get_default_urlpatterns,获取site_urls.py中的urlpatterns""" urlpatterns = self._get_default_urlpatterns()【1】 """实际调用Site._autodiscover ,导入openstack_dashboard/settings.py中的HORIZON_CONFIG, INSTALLED_APPS配置的模块中的dashboard.py和panel.py,此处会进行Dashboard的注册""" """可参考文档:http://docs.openstack.org/developer/horizon/topics/settings.html""" self._autodiscover()【2】 """发现每个Dashboard中的Panel,导入每个Panel的panel.py,从而注册每个Panel""" for dash in self._registry.values(): dash._autodiscover()【3】 ... # Compile the dynamic urlconf. """dash为Dashboard类""" for dash in self._registry.values(): urlpatterns += patterns('', url(r'^%s/' % dash.slug, include(dash._decorated_urls))) # Return the three arguments to django.conf.urls.include return urlpatterns, self.namespace, self.slug
Dashboard类: @property def _decorated_urls(self): urlpatterns = self._get_default_urlpatterns()【1】 default_panel = None # Add in each panel's views except for the default view. """panel为Panel类""" for panel in self._registry.values(): if panel.slug == self.default_panel: default_panel = panel continue url_slug = panel.slug.replace('.', '/') urlpatterns += patterns('', url(r'^%s/' % url_slug, include(panel._decorated_urls))) ... # Return the three arguments to django.conf.urls.include return urlpatterns, self.slug, self.slug
Panel类: @property def _decorated_urls(self): """即查找panel的urls.py文件""" urlpatterns = self._get_default_urlpatterns()【1】
# Apply access controls to all views in the patterns permissions = getattr(self, 'permissions', []) _decorate_urlconf(urlpatterns, require_perms, permissions) _decorate_urlconf(urlpatterns, _current_component, panel=self)
# Return the three arguments to django.conf.urls.include return urlpatterns, self.slug, self.slug
def _autodiscover(self): """Discovers modules to register from ``settings.INSTALLED_APPS``. This makes sure that the appropriate modules get imported to register themselves with Horizon. """ if not getattr(self, '_registerable_class', None): raise ImproperlyConfigured('You must set a ' '"_registerable_class" property ' 'in order to use autodiscovery.') # Discover both dashboards and panels, in that order for mod_name in ('dashboard', 'panel'): """settings.INSTALLED_APPS感觉应该和openstack_dashboard/settings.py中的 ORIZON_CONFIG, INSTALLED_APPS有关""" for app in settings.INSTALLED_APPS: mod = import_module(app) try: before_import_registry = copy.copy(self._registry) import_module('%s.%s' % (app, mod_name)) except Exception: self._registry = before_import_registry if module_has_submodule(mod, mod_name): raise
4、结论如下:
对于请求: