返回文章列表

Django整合IP位址應用與DHCP

本文探討如何在 Django 應用程式中整合 IP 位址管理與 DHCP 功能,涵蓋修改、刪除 DHCP 網路設定,以及設計 DHCP 位址池資料模型。文章提供程式碼範例,詳解如何實作 DHCP 網路詳情頁面、位址池顯示、新增與刪除功能,並探討如何使用 `get_absolute_url` 方法和反向 URL

Web 開發 後端開發

在網路管理應用程式中,整合 IP 位址管理和 DHCP 功能至關重要。本文將逐步講解如何在 Django 框架下實作這些功能,包含 DHCP 網路設定的修改和刪除,DHCP 位址池的資料模型設計,以及 DHCP 詳情頁面的呈現。我們將探討如何有效地顯示、新增和刪除 DHCP 位址池,同時介紹如何利用 Django 的 get_absolute_url 方法和 URL 反向解析機制簡化 URL 管理,提升程式碼的可讀性和可維護性。文章將提供程式碼範例,演示如何在 Django 範本中使用 url 標籤,並解釋如何透過命名 URL 模式來避免 URL 解析衝突,從而構建更穩健且易於維護的網路應用程式。

第4章:將IP位址應用程式與DHCP整合

修改功能

修改(Modify)檢視函式與新增(Add)功能非常相似,不同之處在於它不是為初始檢視建立一個空表單,而是檢索現有資料並將其顯示在表單中。列表4-3首先搜尋現有的DHCP網路物件,然後將其傳遞給表單類別。

清單4-3:處理DHCP網路修改功能的檢視

def modify_dhcp(request, address=None):
    ip, net_size = address.split('/')
    network_addr = NetworkAddress.objects.get(address=ip, network_size=int(net_size))
    dhcp_net = DHCPNetwork.objects.get(physical_net=network_addr)
    if request.method == 'POST':
        # 提交更改
        form = DHCPNetworkAddForm(request.POST, instance=dhcp_net)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect("../..")
    else:
        # 首次顯示
        form = DHCPNetworkAddForm(instance=dhcp_net)
    return render_to_response('add.html',
                              {'form': form,},
                              context_instance=RequestContext(request))

內容解密:

  1. modify_dhcp函式:此函式負責處理對DHCP網路設定的修改請求。它接收請求和位址引數,並根據位址查詢相關的DHCP網路物件。
  2. address.split('/'):將傳入的位址字串分割成IP位址和網路大小兩部分。
  3. NetworkAddress.objects.get():根據IP位址和網路大小查詢對應的網路位址物件。
  4. DHCPNetwork.objects.get():根據網路位址物件查詢對應的DHCP網路物件。
  5. DHCPNetworkAddForm:使用Django的表單類別處理DHCP網路設定的修改。如果請求方法是POST,則驗證表單資料並儲存更改;否則,顯示包含現有資料的表單。
  6. render_to_response:渲染add.html範本並傳回HTTP回應。

刪除功能

刪除功能是一個簡單的函式,負責搜尋並刪除指定的DHCP網路物件。由於尚未定義相關的資料結構,如DHCP池或規則,因此這裡僅說明基本刪除邏輯。

內容解密:

  • 刪除DHCP網路物件時,所有相關的物件也將被自動刪除,這得益於Django的模型關聯設定。

擴充套件DHCP組態與位址池

現在,我們將進入第二階段的開發,加入對位址池資料的支援。位址池是一段範圍內的IP位址,可以根據客戶端的類別分配給特定的客戶端。

位址池資料模型

一個典型的位址池允許定義額外的DHCP選項,例如可以增加特定池的DHCP租期。在這個例子中,我們不允許任何額外的選項,因此模型類別相對簡單,僅包含三個欄位:指向其父DHCP網路物件的指標和兩個邊界位址。

清單4-4:DHCP位址池資料模型類別

class DHCPAddressPool(models.Model):
    DHCPNetwork = models.ForeignKey(DHCPNetwork)
    range_start = models.IPAddressField()
    range_finish = models.IPAddressField()

內容解密:

  1. DHCPAddressPool類別:定義了DHCP位址池的資料模型,包含三個欄位。
  2. DHCPNetwork外部索引鍵:將位址池與其所屬的DHCP網路關聯起來。
  3. range_startrange_finish:定義了位址池的起始和結束IP位址。

DHCP網路詳情

作為第一個工作流程,我們將定義DHCP網路檢視函式。目前,我們已經在實體網路列表頁面上顯示了一些基本資訊,但現在我們要在一個單獨的頁面上顯示更多與DHCP組態相關的資訊,包括位址池和靜態IP分配規則等。

網址對映規則

(r'^networkaddress/(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2})/dhcp/$',
 'display_dhcp'),

內容解密:

  1. 網址對映規則:定義了用於呼叫DHCP顯示檢視的URL模式,將IP位址和網路大小作為引數傳遞給display_dhcp檢視函式。

為了實作DHCP顯示檢視,我們引入了兩個新的函式:一個用於從URL編碼的IP/網路大小對中取得位址物件,另一個用於取得DHCP網路物件。由於大多數函式都需要執行這些操作,因此現在是將它們分離出來的適當時機,如列表4-5所示。

將 IP 位址應用程式與 DHCP 整合

DHCP 池顯示檢視與輔助函式

在實作 IP 位址應用程式與 DHCP 的整合過程中,DHCP 池的顯示是一個重要功能。以下程式碼展示瞭如何實作 DHCP 池的顯示檢視及其輔助函式:

def display_dhcp(request, address=None):
    dhcp_net = get_dhcp_object_from_address(address)
    dhcp_pools = DHCPAddressPool.objects.filter(dhcp_network=dhcp_net)
    return render_to_response('display_dhcp.html', {'dhcp_net': dhcp_net, 'dhcp_pools': dhcp_pools})

def get_network_object_from_address(address):
    ip, net_size = address.split('/')
    return NetworkAddress.objects.get(address=ip, network_size=int(net_size))

def get_dhcp_object_from_address(address):
    return DHCPNetwork.objects.get(physical_net=get_network_object_from_address(address))

內容解密:

  1. display_dhcp 函式:此函式負責處理 DHCP 詳情頁面的顯示請求。它首先透過 get_dhcp_object_from_address 函式取得對應的 DHCP 網路物件,然後查詢與該 DHCP 網路相關聯的所有 DHCP 池,並將這些資訊傳遞給範本 display_dhcp.html 進行渲染。
  2. get_network_object_from_address 函式:此函式根據提供的位址(格式為 “IP/網路大小”)來取得對應的 NetworkAddress 物件。它透過分割輸入字串來取得 IP 位址和網路大小,然後在資料函式庫中查詢相應的物件。
  3. get_dhcp_object_from_address 函式:此函式利用 get_network_object_from_address 取得的 NetworkAddress 物件來查詢對應的 DHCPNetwork 物件。這是透過外部索引鍵關聯實作的,因為 DHCPNetwork 物件與 NetworkAddress 物件之間存在關聯。

DHCP 詳情顯示頁面

DHCP 詳情頁面不僅顯示了 DHCP 網路的基本資訊,也列出了所有可用的 DHCP 池(如果已定義)。以下是一個範例範本:

<h1> DHCP 詳情 for {{ dhcp_net.physical_net.address }}/{{ dhcp_net.physical_net.network_size }} 網路</h1>
<h2><a href="../">傳回網路詳情</a></h2>
<ul>
    <li>路由器:{{ dhcp_net.router }}
    <li>DNS:{{ dhcp_net.dns_server }}
    <li>網域名稱:{{ dhcp_net.domain_name }}
</ul>
<p>( <a href="modify/">修改</a> | <a href="delete/">刪除</a> )</p>
{% if dhcp_pools %}
    <p>
        <h3>可用的 DHCP 池:</h3>
        <ul>
            {% for pool in dhcp_pools %}
                <li>{{ pool.range_start }} - {{ pool.range_finish }}
                    ( <a href="../dhcp_pool/{{ pool.range_start }}/{{ pool.range_finish }}/delete/">刪除</a> )
                </li>
            {% endfor %}
        </ul>
    </p>
{% else %}
    <h3>尚未定義 DHCP 池</h3>
{% endif %}
<p>
    ( <a href="../dhcp_pool/add/">新增池</a> )
</p>

內容解密:

  1. 範本解析器:Django 的範本解析器非常智慧,允許從傳遞給範本的物件中參照相關物件。這意味著即使我們沒有直接傳遞 PhysicalNetwork 物件,也能透過 dhcp_net.physical_net.address 存取其屬性。
  2. URL 結構:在刪除功能的連結中,URL變得相當長且包含冗餘資訊。雖然從資料建模的角度來看這不是最優的,但由於在範本中使用了相對 URL,這樣做簡化了開發過程。

新增與刪除功能

新增和刪除功能的結構和功能幾乎與實體網路和 DHCP 網路檢視中的對應功能相同。新增功能重用了相同的 add.html 範本,而刪除功能則參照了 DHCPAddressPool

重構 URL 結構

在 Django 中,可以為每個模型定義一個額外的方法來傳回物件的絕對 URL。例如,為 NetworkAddress 類別定義 get_absolute_url 方法:

def get_absolute_url(self):
    return '/networkaddress/%s/%s/' % (self.address, self.network_size)

內容解密:

  1. URL 生成:Django 框架允許在模型類別中定義一個方法來傳回物件的絕對 URL。這使得管理和維護 URL 結構變得更加容易,尤其是在專案規模擴大時。
  2. 重構的必要性:隨著模型和 URL 的數量增加,管理和維護所有 URL 成為一項挑戰。定義一個統一的方法來生成 URL 可以提高程式碼的可維護性和靈活性。

將 IP 位址應用程式與 DHCP 整合

使用 get_absolute_url 方法

在 Django 中,為了避免在範本和檢視中硬編碼 URL,我們可以使用 get_absolute_url 方法來取得物件的絕對 URL。首先,我們需要在模型類別中定義這個方法:

from django.db import models
from django.urls import reverse

class NetworkAddress(models.Model):
    # ... 其他欄位定義 ...

    def get_absolute_url(self):
        return reverse('networkaddress-display', kwargs={
            'address': f'{self.address}/{self.network_size}'
        })

這樣,我們就可以在範本中使用 {{ address.get_absolute_url }} 來取得物件的 URL。

反向解析 URL

然而,這種方法仍然需要在 URL 組態檔案和模型定義中都指定 URL 的結構。為瞭解決這個問題,Django 提供了 permalink 裝飾器。我們可以使用它來進一步解耦模型和 URL 組態檔案:

from django.db import models
from django.urls import reverse

class NetworkAddress(models.Model):
    # ... 其他欄位定義 ...

    @models.permalink
    def get_absolute_url(self):
        return ('networkaddress-display', (), {
            'address': f'{self.address}/{self.network_size}'
        })

內容解密:

  1. @models.permalink 裝飾器用於修飾 get_absolute_url 方法,使其能夠根據檢視函式名稱和引數傳回對應的 URL。
  2. return ('networkaddress-display', (), {'address': f'{self.address}/{self.network_size}'}) 這行程式碼指定了檢視函式名稱和引數,permalink 裝飾器會根據這些資訊找到匹配的 URL。

為 URL 模式命名

如果有多個 URL 模式對應到同一個檢視函式,Django 的反向 URL 解析器可能會混淆。為瞭解決這個問題,我們可以為 URL 模式命名:

from django.urls import path
from . import views

urlpatterns = [
    path('networkaddress/', views.networkaddress_display, name='networkaddress-displaytop'),
    path('networkaddress/<str:address>/', views.networkaddress_display, name='networkaddress-display'),
]

內容解密:

  1. name 引數用於為 URL 模式命名,使得我們可以在範本和檢視中使用名稱來參照 URL。
  2. 這樣,即使多個 URL 模式對應到同一個檢視函式,我們仍然可以透過名稱來區分它們。

在範本中使用 URL 參照

Django 提供了一個 url 範本標籤,可以用來根據名稱參照 URL:

{% url 'networkaddress-display' address %}

內容解密:

  1. {% url %} 範本標籤用於根據名稱生成 URL。
  2. 'networkaddress-display' 是 URL 模式的名稱,address 是傳遞給 URL 的引數。

示例程式碼

以下是如何在範本中使用 url 範本標籤的示例:

{% if addresses_list %}
    <ul>
    {% for address in addresses_list %}
        <li>
            <a href="{% url 'networkaddress-display' address %}">
                {{ address.address }}/{{ address.network_size }}
            </a>
            {% ifequal address.network_size 32 %}(host){% else %}(network){% endifequal %}
            {{ address.description }}
            (<a href="{% url 'networkaddress-delete' address %}">delete</a> |
             <a href="{% url 'networkaddress-modify' address %}">modify</a>)
        </li>
    {% endfor %}
    </ul>
{% else %}
    {% ifequal parent.network_size 32 %}
        This is a node IP
        <ul>
            <li>Description: {{ parent.description }}
                ( <a href="{% url 'networkaddress-modify' parent %}">modify</a> )
            </li>
        </ul>
    {% else %}
        No addresses or subnets in this range
    {% endifequal %}
{% endif %}

內容解密:

  1. {% if addresses_list %}{% else %} 用於判斷是否有位址列表。
  2. {% for address in addresses_list %} 用於遍歷位址列表。
  3. {% url 'networkaddress-display' address %} 用於生成顯示位址的 URL。
  4. {% ifequal %} 用於判斷網路大小,以決定顯示「host」還是「network」。