IP地址資源管理系統(tǒng)

項(xiàng)目背景:傳統(tǒng)的企業(yè)內(nèi)網(wǎng)ip地址管理是采用電子文檔的形式,管理流程以手工為主,對(duì)ip地址和子網(wǎng)的使用情況無(wú)法進(jìn)行監(jiān)控和統(tǒng)計(jì),而且數(shù)據(jù)難以共享。隨著網(wǎng)絡(luò)變得越來(lái)越大,IP設(shè)備越來(lái)越多,手工IP地址管理將會(huì)成為網(wǎng)絡(luò)管理和擴(kuò)展的瓶頸。《IP地址資源管理系統(tǒng)》主要是針對(duì)傳統(tǒng)IP地址管理中存在的問(wèn)題提出的一個(gè)IP地址管理的解決方案,通過(guò)對(duì)IP地址資源從分配到回收的閉環(huán)管理,形成一個(gè)完善的、可以共享的、方便查詢統(tǒng)計(jì)的ip地址資源臺(tái)賬,以此提高管理的效率和精度。

一、項(xiàng)目開發(fā)環(huán)境

??本項(xiàng)目采用Python+Django開發(fā)。Python是時(shí)下大熱的一門開發(fā)語(yǔ)言,它的應(yīng)用領(lǐng)域非常廣泛,包括科學(xué)計(jì)算、數(shù)據(jù)分析、人工智能和web開發(fā)等。其中Django就是Python在web領(lǐng)域的一個(gè)強(qiáng)大的web框架,它的功能完善,要素齊全,自帶后臺(tái)管理和大量的工具及組件非常適合快速開發(fā)企業(yè)級(jí)應(yīng)用。在網(wǎng)絡(luò)應(yīng)用開發(fā)領(lǐng)域最著名的就是網(wǎng)絡(luò)爬蟲了,爬蟲爬出來(lái)的結(jié)構(gòu)化數(shù)據(jù)大多生成一個(gè)txt或csv文件,或者存儲(chǔ)在一個(gè)數(shù)據(jù)庫(kù)中,如果把這些內(nèi)容以web的形式呈現(xiàn)給讀者,或者開發(fā)一個(gè)后臺(tái)程序?qū)ζ溥M(jìn)行管理,哪最好的工具就是Django了,畢竟爬蟲和Django都是Python寫的,一個(gè)服務(wù)器上或者一種開發(fā)環(huán)境下兼容性完全不是問(wèn)題。

二、項(xiàng)目主要技術(shù)

1、權(quán)限控制

??在一個(gè)web應(yīng)用程序中,權(quán)限控制是必不可少的。本項(xiàng)目采用了基于角色的權(quán)限控制(RBAC)設(shè)計(jì),一個(gè)權(quán)限對(duì)應(yīng)多個(gè)角色,一個(gè)角色可以包含多個(gè)權(quán)限,一個(gè)用戶可以擁有多個(gè)角色,一個(gè)角色同樣也可以對(duì)應(yīng)多個(gè)用戶,權(quán)限和角色是多對(duì)多關(guān)系,角色和用戶是多對(duì)多關(guān)系,這種對(duì)應(yīng)關(guān)系是對(duì)現(xiàn)實(shí)的抽象,在數(shù)據(jù)庫(kù)中表現(xiàn)為五張表。實(shí)體關(guān)系模型如下,:


實(shí)體關(guān)系模型

??web應(yīng)用權(quán)限的本質(zhì)就是url,實(shí)現(xiàn)權(quán)限控制就是實(shí)現(xiàn)對(duì)url的訪問(wèn)控制。該項(xiàng)目權(quán)限控制的工作原理分為四步:


Django框架

1、GET請(qǐng)求,登錄頁(yè)面是否有訪問(wèn)權(quán)限;
2、POST請(qǐng)求,用戶提交用戶名和密碼,校驗(yàn)是否合法;

3、登錄成功后從數(shù)據(jù)庫(kù)中獲取當(dāng)前用戶的所有權(quán)限并放入session中進(jìn)行存儲(chǔ),由于web應(yīng)用基于http協(xié)議,它的請(qǐng)求應(yīng)答模式是無(wú)狀態(tài)的,就是每次請(qǐng)求都是獨(dú)立的,執(zhí)行情況和結(jié)果與前面和后面的請(qǐng)求無(wú)直接關(guān)系,所以每次發(fā)起請(qǐng)求,后臺(tái)程序都會(huì)到數(shù)據(jù)庫(kù)去查詢是否有權(quán)限,為避免對(duì)數(shù)據(jù)庫(kù)的頻繁操作,減輕數(shù)據(jù)庫(kù)的壓力,所以把權(quán)限存儲(chǔ)在session中。
4、當(dāng)用戶再次發(fā)起請(qǐng)求時(shí),在后臺(tái)編寫中間件對(duì)用戶當(dāng)前發(fā)起的url進(jìn)行權(quán)限判斷(在session中)
django處理流程圖如下:


GET請(qǐng)求,返回登錄頁(yè)面

POST請(qǐng)求,通過(guò)ORM到數(shù)據(jù)庫(kù)中查詢當(dāng)前用戶的相關(guān)權(quán)限,并放入session中

用戶再次發(fā)起請(qǐng)求,就到session中對(duì)url權(quán)限進(jìn)行判斷
import re
from django.conf import settings
from django.shortcuts import HttpResponse, redirect, render
from django.utils.deprecation import MiddlewareMixin
from django.urls import reverse

class RbacMiddleWare(MiddlewareMixin):
    """
    權(quán)限校驗(yàn)中間件
    """
    def process_request(self, request):
        current_url = request.path_info
        for valid_url in settings.VALID_URL_LIST:
            regx = '^%s$' % valid_url
            if re.match(regx, current_url):
                return None
        permission_dict = request.session.get(settings.MOBILEDJ_PERMISSION_SESSION_KEY)
        if not permission_dict:
            # return HttpResponse('未獲取到權(quán)限信息,請(qǐng)登錄')
            return redirect(reverse('login'))
        url_record = [{'title': '首頁(yè)', 'url': '#'}]
        # 此處代碼進(jìn)行判斷
        for url in settings.NO_PERMISSION_LIST:
            regx = '^%s$' % url
            if re.match(regx, request.path_info):
                # 需要登錄,但無(wú)需權(quán)限校驗(yàn)
                request.current_selected_permission = 0
                request.breadcrumb = url_record
                return None
        flag = False
        for item in permission_dict.values():
            regx = '^%s$' % item['url']
            if re.match(regx, current_url):
                flag = True
                if item['pid']:
                    url_record.extend([
                        {'title': item['p_title'], 'url': item['p_url']},
                        {'title': item['title'], 'url': item['url'], 'class': 'active'}
                    ])
                else:
                    url_record.extend([
                        {'title': item['title'], 'url': item['url'], 'class': 'active'},
                    ])
                request.breadcrumb = url_record
                request.current_selected_permission = item['pid'] or item['id']
                break
        if not flag:
            return render(request, 'web/404.html')
        return None

??權(quán)限分配是對(duì)后臺(tái)數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行前端的呈現(xiàn)和操作,權(quán)限分配包括了單項(xiàng)權(quán)限分配和批量權(quán)限批操作,其中權(quán)限批量操作是一個(gè)集合的差集、并集和交集的運(yùn)算,待新建權(quán)限是程序中有而數(shù)據(jù)庫(kù)中沒(méi)有的權(quán)限,待刪除權(quán)限是程序中沒(méi)有而數(shù)據(jù)庫(kù)中有的權(quán)限,界面如下:


添加和編輯權(quán)限

權(quán)限分配

權(quán)限批量操作

2、增刪改查組件

??在一個(gè)采用數(shù)據(jù)庫(kù)的管理系統(tǒng)中,開發(fā)人員大量的工作就是編寫數(shù)據(jù)庫(kù)表的增刪改查代碼。例如在Django項(xiàng)目中,開發(fā)人員首先用ORM創(chuàng)建模型并遷移至數(shù)據(jù)庫(kù),然后為每個(gè)操作(增刪改查)寫視圖函數(shù)和編寫靜態(tài)頁(yè)面模板,最后加入視圖函數(shù)的路由,項(xiàng)目中的每個(gè)模型都要重復(fù)以上幾個(gè)步驟。為了減少這種繁復(fù)的工作,項(xiàng)目設(shè)計(jì)了一個(gè)通用的增刪改查組件,該組件可以快速實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)表的增刪改查。
??通常Django項(xiàng)目在啟動(dòng)時(shí)會(huì)自動(dòng)注冊(cè)項(xiàng)目中的app,同時(shí)加載項(xiàng)目路由,如果在加載路由前能夠動(dòng)態(tài)生成app中的路由和視圖函數(shù),那么簡(jiǎn)化重復(fù)編碼的過(guò)程就可以迎刃而解,而且封裝后的代碼重用性提高,可以放在任何項(xiàng)目中使用。通過(guò)分析Django源碼發(fā)現(xiàn),Django中的autodiscover_modules模塊可以導(dǎo)入一個(gè)py文件,這個(gè)py文件會(huì)在路由加載前執(zhí)行(只要注冊(cè)的app中有stark.py文件,都會(huì)被執(zhí)行)。

from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules
class StarkConfig(AppConfig):
    name = 'stark'
    def ready(self):
        autodiscover_modules('stark')

??接著利用單例模式,實(shí)現(xiàn)訪問(wèn)唯一對(duì)象的方式。在python中,如果導(dǎo)入的文件再次被重新導(dǎo)入,python不會(huì)再重新解釋一遍,而是選擇從內(nèi)存中直接將原來(lái)導(dǎo)入的值拿來(lái)使用,通過(guò)python的這種特性實(shí)現(xiàn)單例模式。

class StarkSite(object):
    def __init__(self):
        self._registry = []
        self.app_name = 'stark'
        self.namespace = 'stark'
    def register(self, model_class, handler=None, prev=None):
        if not handler:
            handler = StarkHandler
        self._registry.append(
            {'model_class': model_class, 'handler': handler(model_class, prev, self), 'prev': prev})
    def get_urls(self):
        patterns = []
        for item in self._registry:
            model_class = item['model_class']
            handler = item['handler']
            prev = item['prev']
            app_name, model_name = model_class._meta.app_label, model_class._meta.model_name
            if prev:
                patterns.extend([
                    re_path(r'^%s/%s/%s/' % (app_name, model_name, prev), (handler.get_urls(), None, None)),
                ])
            else:
                patterns.extend([
                    re_path(r'^%s/%s/' % (app_name, model_name), (handler.get_urls(), None, None)),
                ])
        return patterns
    @property
    def urls(self):
        return self.get_urls(), self.app_name, self.namespace
site = StarkSite()

??加載路由時(shí)導(dǎo)入from stark.service.v1 import site 實(shí)例

from django.contrib import admin
from django.urls import re_path, include
from stark.service.v1 import site
from web.views import account, userinfo
urlpatterns = [
    re_path('admin/', admin.site.urls),
    re_path(r'stark/', site.urls),
    re_path(r'login/$', account.login, name='login'),
    re_path(r'logout/$', account.logout, name='logout'),
    re_path(r'current/userinfo/$', userinfo.current_userinfo_change, name='current_userinfo'),
    re_path(r'^rbac/', include(('rbac.urls', 'rbac'), 'rbac'))
]

??注冊(cè)模型,通過(guò)單實(shí)例自動(dòng)生成路由和增刪改查的視圖函數(shù)

from stark.service.v1 import site
from web import models
from web.views.depart import DepartHandler
site.register(models.Depart, DepartHandler)

??下面是一個(gè)簡(jiǎn)化版的模型操作類,可以根據(jù)模型自動(dòng)生成路由和視圖函數(shù),可以自定義顯示字段,自定義查詢條件,自定義分頁(yè)

class StarkHandler(object):
    list_template = None
    add_template = None
    change_template = None
    delete_template = None
    list_display = []
    per_page = 10

    def __init__(self, model_class, prev, site):
        self.model_class = model_class
        self.prev = prev
        self.site = site
        self.request = None
    def list_view(self, request, *args, **kwargs):
        """
        列表頁(yè)面
        :param request:
        :return:
        """
        # ##################1.批量操作 ###############
        action_list = self.get_action_list()
        action_dict = {func.__name__: func.text for func in action_list}

        if request.method == 'POST':
            action_func_name = request.POST.get('action')
            if action_func_name and action_func_name in action_dict:
                func_response = getattr(self, action_func_name)(request, *args, **kwargs)
                if func_response:
                    return func_response
        # 獲取搜索條件

        search_list = self.get_search_list()
        """
        1.如果search_list沒(méi)有值則不顯示搜索框
        2.獲取用戶提交的關(guān)鍵字
        """
        search_value = request.GET.get('q', '')
        from django.db.models import Q
        """
        Q用于構(gòu)造復(fù)雜的ORM查詢條件
        """
        conn = Q()
        conn.connector = 'OR'
        if search_value:
            for item in search_list:
                conn.children.append((item, search_value))
        # 1.獲取排序
        order_list = self.get_order_list()
        search_group_condition = self.get_search_group_condition(request)
        prev_queryset = self.get_queryset(request, *args, **kwargs)

        queryset = prev_queryset.filter(conn).filter(**search_group_condition).order_by(*order_list)
        # 處理分頁(yè)
        all_count = queryset.count()
        query_params = request.GET.copy()
        query_params._mutable = True
        pager = Pagination(
            current_page=request.GET.get('page'),
            all_count=all_count,
            base_url=request.path_info,
            query_params=query_params,
            per_page=self.per_page
        )
        # 1.處理表頭
        header_list = []
        list_display = self.get_list_display(request, *args, **kwargs)
        if list_display:
            for key_or_func in list_display:
                if isinstance(key_or_func, FunctionType):
                    header_list.append(key_or_func(self, None, True))
                else:
                    verbose_name = self.model_class._meta.get_field(key_or_func).verbose_name
                    header_list.append(verbose_name)
        else:
            header_list.append(self.model_class._meta.model_name)
        # 2.處理表格內(nèi)容
        data_list = queryset[pager.start:pager.end]
        body_list = []
        for row in data_list:
            tr_list = []
            if list_display:
                for key_or_func in list_display:
                    if isinstance(key_or_func, FunctionType):
                        tr_list.append(key_or_func(self, row, False, *args, **kwargs))
                    else:
                        tr_list.append('' if getattr(row, key_or_func) == None else getattr(row, key_or_func))
            else:
                tr_list.append(row)
            body_list.append(tr_list)
        # 按鈕添加
        add_btn = self.get_add_btn(request, *args, **kwargs)
        # 組合搜索
        search_group = self.get_search_group(request)
        search_group_list = []
        for option_object in search_group:
            search_group_list.append(option_object.get_queryset_or_tuple(request, self.model_class))
        return render(
            request,
            self.list_template or 'stark/list.html',
            {
                'body_list': body_list,
                'header_list': header_list,
                'pager': pager,
                'add_btn': add_btn,
                'search_list': search_list,
                'search_value': search_value,
                'action_dict': action_dict,
                'search_group_row_list': search_group_list
            }
        )

    def add_view(self, request, *args, **kwargs):
        """
        添加頁(yè)面
        :return:
        """
        model_form_class = self.get_model_class_form(True, request, None, *args, **kwargs)
        if request.method == 'GET':
            form = model_form_class()
            return render(request, self.add_template or 'stark/change.html', {'form': form})
        form = model_form_class(data=request.POST)
        if form.is_valid():
            response = self.save(request, form, False, *args, **kwargs)
            return response or redirect(self.reverse_list_url(*args, **kwargs))
        return render(request, self.add_template or 'stark/change.html', {'form': form})
    def change_view(self, request, pk, *args, **kwargs):
        """
        編輯頁(yè)面
        :param request:
        :param pk:
        :return:
        """
        model_form_class = self.get_model_class_form(False, request, pk, *args, **kwargs)
        current_change_obj = self.get_change_object(request, pk, *args, **kwargs)
        if not current_change_obj:
            info = '數(shù)據(jù)不存在,請(qǐng)重新選擇!'
            return render(request, 'stark/hint.html', {'msg': info})
        if request.method == "GET":
            form = model_form_class(instance=current_change_obj)
            return render(request, self.change_template or 'stark/change.html', {'form': form})
        form = model_form_class(instance=current_change_obj, data=request.POST)
        if form.is_valid():
            response = self.save(request, form, is_update=True, *args, **kwargs)
            return response or redirect(self.reverse_list_url(*args, **kwargs))
        return render(request, self.change_template or 'stark/change.html', {'form': form})
    def delete_view(self, request, pk, *args, **kwargs):
        """
        刪除頁(yè)面
        :param request:
        :return:
        """
        origin_url = self.reverse_list_url(*args, **kwargs)
        current_delete_obj = self.get_delete_object(request, pk, *args, **kwargs)
        if not current_delete_obj:
            info = '數(shù)據(jù)不存在,請(qǐng)重新選擇!'
            return render(request, 'stark/hint.html', {'msg': info})
        if request.method == 'GET':
            return render(request, self.delete_template or 'stark/delete.html', {'cancel_url': origin_url})
        current_delete_obj.delete()
        return redirect(origin_url)
    def get_urls(self):
        """
        二次路由分發(fā)
        :return:
        """
        patterns = [
            re_path(r'^list/$', self.wrapper(self.list_view), name=self.get_list_url_name),
            re_path(r'^add/$', self.wrapper(self.add_view), name=self.get_add_url_name),
            re_path(r'^change/(?P<pk>\d+)/$', self.wrapper(self.change_view),
                    name=self.get_change_url_name),
            re_path(r'^delete/(?P<pk>\d+)/$', self.wrapper(self.delete_view),
                    name=self.get_delete_url_name)
        ]
        patterns.extend(self.extra_url())
        return patterns
    def wrapper(self, func):
        @functools.wraps(func)
        def inner(request, *args, **kwargs):
            self.request = request
            return func(request, *args, **kwargs)
        return inner
    def save(self, request, form, is_update=False, *args, **kwargs):
        """
        自定義保存函數(shù)
        :param request:
        :param form: 表單
        :param is_update: 判斷是添加還是更新
        :return:
        """
        form.save()

3、業(yè)務(wù)開發(fā)

??權(quán)限控制組件和增刪改查組件開發(fā)完成后,業(yè)務(wù)開發(fā)就變得非常簡(jiǎn)單了,權(quán)限控制組件和增刪改查組件就好比大樓的地基,地基牢固了,上面建造的樓房才不會(huì)傾覆。
??業(yè)務(wù)流程,首先由系統(tǒng)管理員分配超網(wǎng)地址,比如一個(gè)B類地址,系統(tǒng)會(huì)根據(jù)B類地址的網(wǎng)絡(luò)號(hào)自動(dòng)生成所屬的C類子網(wǎng)和IP地址,然后網(wǎng)絡(luò)管理員對(duì)生成的C類子網(wǎng)進(jìn)行規(guī)劃,通常業(yè)務(wù)網(wǎng)絡(luò)的最大顆粒為C類,為節(jié)約網(wǎng)絡(luò)資源,還可以對(duì)C類子網(wǎng)按需求劃分為掩碼長(zhǎng)度為25為、26位、27位、28位、29位、30位不等的子網(wǎng),子網(wǎng)規(guī)劃完成后,接著分配規(guī)劃好的子網(wǎng),子網(wǎng)分配完成后,對(duì)應(yīng)二級(jí)單位的管理員可以在頁(yè)面中看到分配給自己部門的子網(wǎng)號(hào)和IP地址范圍,拿到IP地址范圍后,管理員就可以對(duì)相應(yīng)的IP地址的使用進(jìn)行維護(hù)和管理了。對(duì)于撤銷的單位同時(shí)回收子網(wǎng)號(hào),從而實(shí)現(xiàn)了對(duì)IP地址的規(guī)劃,分配,回收的整個(gè)生命周期的管理。
??業(yè)務(wù)系統(tǒng)主要分為網(wǎng)絡(luò)管理和主機(jī)管理兩個(gè)模塊:
??網(wǎng)絡(luò)管理包括子網(wǎng)規(guī)劃和子網(wǎng)分配,子網(wǎng)規(guī)劃支持自動(dòng)生成子網(wǎng)和手動(dòng)創(chuàng)建子網(wǎng),并且具備自動(dòng)校驗(yàn)子網(wǎng)功能,防止輸入錯(cuò)誤,子網(wǎng)分配功能支持豐富的關(guān)鍵字查詢和組合搜索,可以快速定位需要分配的子網(wǎng),同時(shí)記錄子網(wǎng)分配日志;

    def action_multi_init_subnet(self, request, net_count, *args, **kwargs):
        """
        根據(jù)子網(wǎng)掩碼長(zhǎng)度自動(dòng)生成子網(wǎng),同時(shí)更新主機(jī)表中的廣播地址和網(wǎng)絡(luò)地址以及IP所屬的子網(wǎng)號(hào)
        :param request: 
        :param net_count: 子網(wǎng)掩碼長(zhǎng)度
        :param args: 
        :param kwargs: 
        :return: 
        """
        pk_list = request.POST.getlist('pk')
        for pk in pk_list:
            ipv4_subnet_object = models.IpSubnet.objects.filter(id=pk).first()
            if not ipv4_subnet_object:
                continue
            child_subnet_exists = models.IpSubnet.objects.filter(pid__isnull=False, pid=pk)
            if child_subnet_exists:
                continue
            subnet = ipv4_subnet_object.subnet
            ipv4_network_object = IPv4Network(subnet)
            prefix_length = ipv4_network_object.prefixlen
            if prefix_length > 24:
                continue
            ipv4_network_list = [item for item in list(ipv4_network_object.subnets(new_prefix=net_count))]
            ipv4_object_list = []
            ip_network_id = ipv4_subnet_object.ip_network_id
            for item in ipv4_network_list:
                ipv4_object_list.append(models.IpSubnet(ip_network_id=ip_network_id, pid_id=pk, subnet=str(item),
                                                        subnet_num=int(item.network_address)))
            models.IpSubnet.objects.bulk_create(ipv4_object_list, batch_size=30)

            ip_subnet_queryset = models.IpSubnet.objects.filter(subnet__in=ipv4_network_list)
            for item in ip_subnet_queryset:
                # network_address = str(IPv4Network(item).network_address)
                # network_broadcast_address = str(IPv4Network(item).broadcast_address)
                # 更新主機(jī)列表的網(wǎng)絡(luò)地址和廣播地址
                network_address = ipv4_tools.get_network_address(item.subnet)
                broadcast_address = ipv4_tools.get_broadcast_address(item.subnet)
                models.Hosts.objects.filter(ip_address=network_address).update(ip_type=settings.NET_ADDR_IP_TYPE)
                models.Hosts.objects.filter(ip_address=broadcast_address).update(
                    ip_type=settings.BROADCAST_ADDR_IP_TYPE)

                # 更新主機(jī)列表的子網(wǎng)號(hào)
                network_address_num = ipv4_tools.get_network_address(item.subnet, data_type='int')
                broadcast_address_num = ipv4_tools.get_broadcast_address(item.subnet, data_type='int')
                subnet_id = item.id
                models.Hosts.objects.filter(ip_address_num__lte=broadcast_address_num,
                                            ip_address_num__gte=network_address_num).update(ip_subnet_id=subnet_id)
建立超網(wǎng)

子網(wǎng)規(guī)劃

Snipaste_2020-06-19_18-03-22.png

Snipaste_2020-06-19_18-03-37.png

Snipaste_2020-06-19_18-03-48.png

子網(wǎng)分配

??在主機(jī)管理中,系統(tǒng)根據(jù)劃分的子網(wǎng)自動(dòng)生成ip,管理人員可以對(duì)ip進(jìn)行分配,并記錄分配日志,實(shí)現(xiàn)IP地址的精確管理,同時(shí)為統(tǒng)計(jì)分析提供數(shù)據(jù)。


超網(wǎng)使用情況統(tǒng)計(jì)

統(tǒng)計(jì)類,一個(gè)通用的統(tǒng)計(jì)類
class SubnetHostsAccount(object):
    """
    統(tǒng)計(jì)類
    """
    def __init__(self, subnet, *args, **kwargs):
        self.subnet = subnet
        self.args = args
        self.account = kwargs

    @property
    def header(self):
        header_list = [str(k) for k in self.account.keys()]
        return header_list

    @property
    def content(self):
        content_list = [v for k, v in self.account.items()]
        return content_list

三、項(xiàng)目結(jié)語(yǔ)

??Python web框架有很多,比如Tornado和輕便的flask。但Django屬于重量級(jí)框架,一些輕量級(jí)的應(yīng)用不需要的功能模塊,Django也自帶了,比如用戶驗(yàn)證(Auth),管理后臺(tái)(Admin)和緩存管理(Cache)等功能,Django有完善的文檔,DJango有強(qiáng)大的數(shù)據(jù)庫(kù)訪問(wèn)組件(ORM,其訪問(wèn)數(shù)據(jù)庫(kù)的效率接近原生sql),使開發(fā)者無(wú)需學(xué)習(xí)sql語(yǔ)言一樣可以輕松操作數(shù)據(jù)庫(kù),Django這種基于MVC開發(fā)模式的傳統(tǒng)框架,非常適合開發(fā)基于PC的傳統(tǒng)網(wǎng)站,因?yàn)樗瑫r(shí)包括了后端(邏輯層,數(shù)據(jù)庫(kù)層)和前端的開發(fā)(如模板語(yǔ)言,樣式),基于PC的網(wǎng)站不會(huì)消失,不過(guò)其重要性會(huì)隨著移動(dòng)端的app和小程序的逐漸普及而降低。現(xiàn)代網(wǎng)絡(luò)應(yīng)用Web APP或者大型網(wǎng)站一般是一個(gè)后臺(tái),然后對(duì)應(yīng)各種客戶端(IOS,android,瀏覽器)。由于客戶端的開發(fā)語(yǔ)言與后臺(tái)的開發(fā)語(yǔ)言經(jīng)常不一樣,這時(shí)就需要后臺(tái)可以提供跨平臺(tái)跨語(yǔ)言的一種標(biāo)準(zhǔn)的資源或數(shù)據(jù)(如Json格式)供前后端溝通,這就是WEB API的作用了。Django本身開發(fā)不了符合REST規(guī)范的WEB API,不過(guò)借助Django-rest-framework(DRF)可以快速開發(fā)出優(yōu)秀的web api。
[項(xiàng)目源碼地址]https://gitee.com/mobiledj/mobiledj_ipm.git

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。