A field to handle software version information in Django models.
Our main goal is to have useful lookups, i.e. to be able to make order comparisons between versions. This requires transforming the input into an integer. We have two desired properties:
- To be able to reconstruct the input without losing information.
- To preserve the order when transforming inputs into integers.
The main constraint is the max size of the integer that can be stored in the database (8-bytes). This constraint forces us to reject inputs that are too large to fit into our scheme. Please read the Internals section below for more details.
Currently the package is not available on PyPI because of name collision (django-version-field is too close to another package, maybe django-versionfield). So you must install it from this repo:
pip install git+https://github.com/situation-sh/django-version-field@v0.3.0Important
Invalid version strings, versions with local version information and inputs too large to be encoded will be rejected. Please read the Internals section for details.
from django.db import models
from django_version_field import VersionField
class TestModel(models.Model):
version = VersionField()Comparison in python
M0 = TestModel(version="5.2")
M1 = TestModel(version="4.9.12")
assert M0.version > M1.versionComparison in the database
TestModel.objects.bulk_create([
TestModel(version="1.0.5"),
TestModel(version="1.2.17"),
TestModel(version="2.0.1"),
])
assert TestModel.objects.filter(version___gte="1.2").count() == 2To run tests:
poetry run python runtests.pyTo run an interactive server with the test app:
poetry run python dev.pyThen you can visit http://127.0.0.1:8000/admin/ with admin/admin credentials.
The field parses the input string using the packaging.version parse method. parse expects an input complying with the following scheme,
[N!]N(.N)*[{a|b|rc}N][.postN][.devN]
where N are positive integers. Inputs not satisfying this condition will raise the InvalidVersion error.
Once the input is parsed, the field tries to create a bit stream by concatenating:
- The epoch number encoded into 4 bits.
- The major encoded into 12 bits.
- The minor encoded into 8 bits.
- The patch number (micro) encoded into 16 bits.
- The 4th part of the 'release' segment encoded into 8 bits.
- The pre-release number and symbol (
a,borrc) encoded into 6 bits and 2 bits, respectively. - The post-release number and post-release flag encoded into 4 bits (3 bits for the number).
- The dev-release number encoded into 4 bits.
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Epoch | Major | ||||||||||||||
| 2 | Minor | Patch | ||||||||||||||
| 4 | Patch | Additional release segment | ||||||||||||||
| 6 | F | Pre-release | Post-release | Dev-release | ||||||||||||
Inputs where a segment is too large to be encoded are rejected, raising ValueError.
The local version information is not encoded in our scheme, so inputs with local version information are rejected.
The input can contain more than 4 parts in the 'release' segment, but these must contain only '0', otherwise the input is rejected.
After comparing the release portion of two versions, a version which is post-release should be greater than a version which is not post-release. E.g. '1.1post0'>'1.1' and '1.2'>'1.1post0'. The post-release flag in our code is used to distinguish between post-release versions where the post-release number is 0 (such as '1.1post0') and pure release versions (such as '1.1').
After comparing the release portion of two versions, a version which is pre-release should be lesser than a version which is not pre-release. E.g. '1.1a3'<'1.1' and '1.2a3'>'1.1post0'. After comparing the release portion of two versions, a version which is dev-release should be lesser than any other version, including pre-release versions. E.g. '1.1dev5'<'1.1', '1.1dev5'<'1.1a5', and naturally '1.1a5dev5'<'1.1a5'.
Test data was extracted from the Common Platform Enumeration (CPE) records from the National Vulnerability Database (NVD), the CPE dictionary version 2.3, which contains an exhaustive list of software products with their versions.
The data was extracted and separated into three categories:
- "error": versions that do not respect the syntax established by
packaging.version. - "warning": versions that respect the correct syntax but are to large to encode with our 8-byte scheme, causing potential loss of information.
- "white": versions that respect the correct syntax and can be encoded correctly.