stark组件之注册与路由系统(三)
在文章stark组件前戏中已经提到过,django的注册功能是通过AdminSite的单例进行组册的,所以在这里也可以进行单例模式。
class AdminSite(object): def __init__(self): self._registry=[] self.app_name='stark' self.namespace='stark' def register(self,model_class,stark_class=None,prev=None): if not stark_class: stark_class=BaseStark self._registry.append(ModelStarkMapping(model_class,stark_class(model_class,self,prev),prev)) #self._registry.append({'model':model_class,'stark_class':stark_class(model_class,self,prev),prev}) # for k,v in self._registry.items(): # print(k,v)0 """
之前
_registry={ 'UserInfo':BaseStark(UserInfo,site) 封装UserInfo类和site 'Role':RoleStark(Role,site) 封装 Role类和site } """ def get_urls(self): urlpatterns=[] for item in self._registry: #循环得到的是每一个ModelStarkMapping对象 app_label=item.model_class._meta.app_label model_name=item.model_class._meta.model_name if item.prev: temp = path('%s/%s/%s/' % (app_label, model_name,item.prev), (item.stark_class.urls, None, None)) else: temp = path('%s/%s/' % (app_label, model_name,), (item.stark_class.urls, None, None)) urlpatterns.append(temp) return urlpatterns @property def urls(self): return self.get_urls(),self.app_name,self.namespace
可以看到之前_registry是一个字典,现在变成了一个列表,为什么要这样做呢?
(1)这是因为如果是字典的话,一个model只能注册一次,现在是model可以注册多次,只需要将model对应的类按照需要表换,这样在一个页面上来实现一个model不同的数据展示。
(2)pre前缀的功能是为了将不同model注册的url加以区别,如下所示:
site.register(models.Customer,PersonalCustomerStark,'per') site.register(models.Customer,CustomerStark)
另外将一个个字典的内容封存为一个对象,这样取值方便,当然,字典也是没问题的。
class ModelStarkMapping(object): def __init__(self,model_class,stark_class,prev): self.model_class=model_class self.stark_class=stark_class self.prev=prev
这就是注册功能的实现过程,接下来就是路由系统的构建。
可以看到上述在返回urlpatterns列表时,第二个元素是三个参数组成的元组,按理说应该是include()进行二级路由分发才对呀,其实这是include()方法的本质,在include()方法中返回的就是这么三个参数的元组。
def include(arg, namespace=None): app_name = None if isinstance(arg, tuple): # Callable returning a namespace hint. try: urlconf_module, app_name = arg except ValueError: if namespace: raise ImproperlyConfigured( 'Cannot override the namespace for a dynamic module that ' 'provides a namespace.' ) raise ImproperlyConfigured( 'Passing a %d-tuple to include() is not supported. Pass a ' '2-tuple containing the list of patterns and app_name, and ' 'provide the namespace argument to include() instead.' % len(arg) ) else: # No namespace hint - use manually provided namespace. urlconf_module = arg if isinstance(urlconf_module, str): urlconf_module = import_module(urlconf_module) patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module) app_name = getattr(urlconf_module, 'app_name', app_name) if namespace and not app_name: raise ImproperlyConfigured( 'Specifying a namespace in include() without providing an app_name ' 'is not supported. Set the app_name attribute in the included ' 'module, or pass a 2-tuple containing the list of patterns and ' 'app_name instead.', ) namespace = namespace or app_name # Make sure the patterns can be iterated through (without this, some # testcases will break). if isinstance(patterns, (list, tuple)): for url_pattern in patterns: pattern = getattr(url_pattern, 'pattern', None) if isinstance(pattern, LocalePrefixPattern): raise ImproperlyConfigured( 'Using i18n_patterns in an included URLconf is not allowed.' ) return (urlconf_module, app_name, namespace)include()
然后可以看看二级路由,在BaseStark这个基类中定义了二级路由。
...
def get_urls(self): urlpatterns = [ re_path('list/$', self.wrapper(self.changelist_view), name=self.get_list_url_name ), re_path('add/$', self.wrapper(self.add_view), name=self.get_add_url_name), re_path('(?P\d+)/change/$ ', self.wrapper(self.change_view), name=self.get_edit_url_name), re_path('(?P\d+)/del/$ ', self.wrapper(self.del_view), name=self.get_del_url_name), ] extra_urls = self.extra_urls() if extra_urls: urlpatterns.extend(extra_urls) return urlpatterns def extra_urls(self): # 用于扩展url 返回的是一个列表,一次性扩展多个值 pass @property def urls(self): return self.get_urls()
...
这样就完成了注册与路由构建。