Django admin 組件 原理分析與擴展使用 之 sites.py (一)

来源:https://www.cnblogs.com/xinghuaikang/archive/2018/03/18/8590625.html
-Advertisement-
Play Games

一 、 前言 Django 提供了admin 組件 為項目提供基本的管理後臺功能(對數據表的增刪改查)。 本篇文章通過 admin源碼 簡單分析admin 內部原理 ,擴展使用方式,為以後進行定製和自己開發組件做鋪墊。 二、 簡單使用 1.在app 目錄下的admin.py 中通過註冊表 2. 創建 ...


一 、 前言 

  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 這個對象的構造方式和包含方法。 

 

 

 

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本教程案例github:https://github.com/axel10/ngrx_demo-counter-and-list angular2+ 的學習成本應該是三大框架中最高的一個,教程及案例稀缺,流程較為複雜,這裡我用計數器和線上獲取用戶數據並渲染成列表這兩個案例來幫大家快速入手angula ...
  • 閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包實現。 下麵就是我的學習筆記,對於Javascript初學者應該是很有用的。 一、變數的作用域 要理解閉包,首先必須理解Javascript特殊的變數作用域。 變數的作用域無非就是兩種:全局變數和局部變 ...
  • 開始接觸不太清楚rtmp協議的流,後來折騰了一番採用了videojs的插件下麵是案例 videojs案例 剛開始採用的是videojs的形式展示rtmp流媒體視頻的,後面發win10的谷歌不相容然後採用了flowplayer的插件播放 ...
  • Cas的全稱是Centeral Authentication Service,是對單點登錄SSO(Single Sign On)的一種實現。其由Cas Server和Cas Client兩部分組成,Cas Server是核心,而Cas Client通常就對應於我們的應用。一個Cas Server可以 ...
  • 6.1.2 介面不是類,不能實例化一個介面;但是可以聲明介面的變數;Comparable x; 介面變數必須引用實現了介面的類對象;x = new Employee(); 檢查一個對象是否屬於某個特定類(instanceof); 檢查一個對象是否屬於某個特定的介面(instance);if (anO ...
  • webService入門 Web service是一個平臺獨立的,低耦合的,自包含的、基於可編程的web的應用程式,可使用開放的XML(標準通用標記語言下的一個子集)標準來描述、發佈、發現、協調和配置這些應用程式,用於開發分散式的互操作的應用程式。[1] Webservice跨平臺跨語言; Java... ...
  • WebService介紹 首先我們來談一下為什麼需要學習webService這樣的一個技術吧.... 問題一 如果我們的網站需要提供一個 天氣預報 這樣一個需求的話,那我們該怎麼做????? 天氣預報這麼一個功能並不是簡單的JS組件就能夠實現的,它的數據是依賴資料庫分析出來的,甚至需要衛星探測..我 ...
  • c/s是客戶機/伺服器,b/s是瀏覽器/伺服器。 ipv4 192.168.....(內網ip) 通過路由器的NAT(維護了一張表,用來轉換ip對應外網)。 當ipv6推廣開,就不用使用路由器了。 通信原理: 寫ip地址訪問太麻煩,於是有了功能變數名稱,它會先訪問本地的hosts,沒有則去網路訪問DNS服務 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...