django-cidrfield開発小記


若水斎に先発する.
DjangoでGenericIPAddressFieldは、IPv 4またはIPv 6アドレスを格納できますが、セグメントを格納するためのフィールドはありません.文字列でセグメントを格納すると、含まれているIPアドレスでセグメントをフィルタできないなどの意味が失われます.Djangoプラグインdjango-netfieldsは、セグメントを格納する機能を実現していますが、PostgreSQLのみがサポートされており、PostgreSQLが提供するセグメント関連フィールドを直接使用しています.すべてのデータベースをサポートするストレージ・セグメント専用のフィールドIPNetworkFieldを設計しました.この記事では、IPNetworkFieldの使用方法と実装原理について説明します.
後の文の中でIPセグメントとネットセグメントは同義語で、行文によって便利に使用します.

使用方法


まずpipを使用してdjango-cidrfieldを取り付けます.
pip install django-cidrfield
modelを定義するとき、次の例に従ってストレージ・セグメントのフィールドを定義します.
from django.db import models
from cidrfield.models import IPNetworkField

class MyModel(models.Model):

    # the regular params should work well enough here
    ip_network = IPNetworkField()
    # ... and so on
modelを作成した後、次の例に従ってセグメントを格納します.
MyModel(ip_network='192.168.1.0/24').save()
__containsを使用して、特定のIPまたはIPセグメントを含むネットワークセグメントを問い合わせることができる.
MyModel.objects.filter(ip_network__contains='192.168.1.1')
MyModel.objects.filter(ip_network__contains='192.168.1.250/30')
__icontainsで効果は同じです.__inを使用して、特定のセグメントに属するセグメントを問い合わせることができます.
MyModel.objects.filter(ip_network__in='192.168.0.0/16')

実装の原理


設計目標


実現原理を論述する前に,需要を撫でておく.私たちの目標は次のとおりです.
  • は、1つのセグメント
  • を記憶することができる.
  • は、特定のIPアドレスまたはアドレスセグメントを含むネットワークセグメント
  • を問い合わせることができる.
  • は、特定のIPアドレスセグメントに属するネットワークセグメント
  • を問い合わせることができる.

    保存方法


    すべてのデータベースがPostgreSQLのように、ネットワークセグメントのストレージとクエリーをオリジナルでサポートしているわけではありません.したがって、整数または文字列を使用してセグメントを格納するしかありません.1つのデータベースフィールドでセグメントを格納するには、1つのIPアドレスが整数であり、セグメントがIPアドレスに追加のデータ(マスクまたはCIDR)を加えるため、整数を使用するのは力不足です.ただし、文字列でセグメントを格納するとします.また、演算が困難な問題に直面し、これらの問題を解決するために、簡単な実装を設計しましたが、効率の悪いデータ構造:文字列でバイナリ数を格納します.IPv 4アドレスセグメントを例に挙げます.
    192.168.56.0/24
    

    このアドレスセグメントは、IPアドレスと"/24"(CIDR)からなる.このIPアドレスを表す方式は点分十進法で、人間が読むのに便利だが、計算には不便だ.最も計算しやすいのは間違いなくバイナリ形式なので、上記のアドレスセグメントを次のように変換します.
    11000000101010000011100000000000/24
    

    しかし、上記の値を文字列形式でデータベースに保存しても計算できません.問題は主に「/24」にあります.「/24」を直接捨てると一部の情報が失われます.私の方法は「/24」を捨てると同時に文字列の上位24ビットだけを残し、セグメントが次のようになります.
    110000001010100000111000
    

    変換ルールが分かれば、最初のセグメントに復元できますか?答えは肯定的だ.私たちは情報を失っていないからです.「24」という情報は文字列の長さに現れ、文字列が失われた部分は全0である.
    しかし、最終的に格納されるデータは、2つのIPプロトコルのセグメントを区別するために文字列の前に「IPv 4」または「IPv 6」を付け、データベース文字列検索を容易にするために文字列の後ろに「%」を付ける必要があります.
    まとめて、セグメント:
    192.168.56.0/24
    

    データベースに格納されている実績は次のとおりです.
    IPv4110000001010100000111000%
    

    IPv 6アドレスの格納は同じです.

    クエリーの方法


    セグメント:
    192.168.0.0/16
    

    データベースに格納するとどのようになりますか?これは計算しやすいです.読者の比較を容易にするために、2つのセグメントに実際に格納されているデータを一緒に書きます.
    IPv41100000010101000%                # 192.168.0.0/16
    IPv4110000001010100000111000%        # 192.168.56.0/24
    

    これで読者は、セグメントのクエリーがどのように実現されるかを理解することができ、SQLlikeを直接使えばよい.これも末尾に「%」を加える理由である.
    文章の完全性のために、簡単な説明が必要だと思います.
  • ネットワークセグメントAがネットワークセグメントBに属する場合、データベースに格納されたデータは、A like Bが必ず成立する.
  • セグメントAがセグメントBを含む場合、データベースに格納されたデータは、必ずB like Aが成立する.

  • プログラミング実装


    公式ドキュメント「カスタムモデルフィールドの作成(model fields)」を参照すると、カスタムモデルフィールドの作成方法を学習できます.完全なコードはGithubを見て、長さはとても短くて、興味のある読者は自分で読むことができます.