在網路管理應用程式中,整合 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))
內容解密:
modify_dhcp函式:此函式負責處理對DHCP網路設定的修改請求。它接收請求和位址引數,並根據位址查詢相關的DHCP網路物件。address.split('/'):將傳入的位址字串分割成IP位址和網路大小兩部分。NetworkAddress.objects.get():根據IP位址和網路大小查詢對應的網路位址物件。DHCPNetwork.objects.get():根據網路位址物件查詢對應的DHCP網路物件。DHCPNetworkAddForm:使用Django的表單類別處理DHCP網路設定的修改。如果請求方法是POST,則驗證表單資料並儲存更改;否則,顯示包含現有資料的表單。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()
內容解密:
DHCPAddressPool類別:定義了DHCP位址池的資料模型,包含三個欄位。DHCPNetwork外部索引鍵:將位址池與其所屬的DHCP網路關聯起來。range_start和range_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'),
內容解密:
- 網址對映規則:定義了用於呼叫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))
內容解密:
display_dhcp函式:此函式負責處理 DHCP 詳情頁面的顯示請求。它首先透過get_dhcp_object_from_address函式取得對應的 DHCP 網路物件,然後查詢與該 DHCP 網路相關聯的所有 DHCP 池,並將這些資訊傳遞給範本display_dhcp.html進行渲染。get_network_object_from_address函式:此函式根據提供的位址(格式為 “IP/網路大小”)來取得對應的NetworkAddress物件。它透過分割輸入字串來取得 IP 位址和網路大小,然後在資料函式庫中查詢相應的物件。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>
內容解密:
- 範本解析器:Django 的範本解析器非常智慧,允許從傳遞給範本的物件中參照相關物件。這意味著即使我們沒有直接傳遞
PhysicalNetwork物件,也能透過dhcp_net.physical_net.address存取其屬性。 - 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)
內容解密:
- URL 生成:Django 框架允許在模型類別中定義一個方法來傳回物件的絕對 URL。這使得管理和維護 URL 結構變得更加容易,尤其是在專案規模擴大時。
- 重構的必要性:隨著模型和 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}'
})
內容解密:
@models.permalink裝飾器用於修飾get_absolute_url方法,使其能夠根據檢視函式名稱和引數傳回對應的 URL。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'),
]
內容解密:
name引數用於為 URL 模式命名,使得我們可以在範本和檢視中使用名稱來參照 URL。- 這樣,即使多個 URL 模式對應到同一個檢視函式,我們仍然可以透過名稱來區分它們。
在範本中使用 URL 參照
Django 提供了一個 url 範本標籤,可以用來根據名稱參照 URL:
{% url 'networkaddress-display' address %}
內容解密:
{% url %}範本標籤用於根據名稱生成 URL。'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 %}
內容解密:
{% if addresses_list %}和{% else %}用於判斷是否有位址列表。{% for address in addresses_list %}用於遍歷位址列表。{% url 'networkaddress-display' address %}用於生成顯示位址的 URL。{% ifequal %}用於判斷網路大小,以決定顯示「host」還是「network」。