如何在 Django 数据库中存储版本号字符串以便它们正确 comparable/sortable
How do I store version number strings in a django database so they are correctly comparable/sortable
我是 developing/maintaining/curating 从各种可穿戴研究设备收集的测试结果数据库。每个设备都有三个主要组件,每个组件都有两个版本号(固件和硬件)。我正在使用 Django 应用程序为数据库提供 Web 界面。版本号表示为直接整数或三元组(主要、次要、内部版本)。整数很容易处理,我显然可以将三元组存储为字符串,但作为字符串它们不会正确排序或正确比较,例如,如果我只想要固件版本低于 [=22 的设备产生的测试结果=].
由于第二个 'decimal point' 分隔符,我不能使用浮点数。我想也许可以通过将其存储为日期来进行黑客攻击,但这会将次要号码限制为小于 12,并将版本号限制为小于 29,此外我 知道 这是一个可怕的解决方案。我什至不应该承认我想到了它。
缺少一些 PL/SQL 扩展数据库以提供正确处理字符串的比较函数,有没有简单的方法可以做到这一点?如果没有,我什至可以将我的自定义 SQL 函数与 django 一起使用吗?
将它们存储为零填充字符串:
>>> def sortable_version(version):
... return '.'.join(bit.zfill(5) for bit in version.split('.'))
...
>>> sortable_version('1.1')
'00001.00001'
>>> sortable_version('2')
'00002'
>>> sortable_version('2.1.10')
'00002.00001.00010'
>>> sortable_version('10.1')
'00010.00001'
>>> sortable_version('2') > sortable_version('1.3.4')
True
>>> sortable_version('10') > sortable_version('2')
True
>>> sortable_version('2.3.4') > sortable_version('2')
True
并且您始终可以使用这种零填充格式显示常规版本:
>>> def normalize_version(padded_version):
... return '.'.join(bit.lstrip('0') for bit in padded_version.split('.'))
...
>>> normalize_version('00010')
'10'
>>> normalize_version('00002.00001.00010')
'2.1.10'
您要找的是"natural sort"。
另一种实现方式是使用数据库排序。这是 Postgres 的示例:
class VersionRecordManager(models.Manager):
def get_queryset(self):
return super().get_queryset().extra(
select={
'natural_version':
"string_to_array( "
" regexp_replace( " # Remove everything except digits
" version, '[^\d\.]+', '', 'g' " # and dots, then split string into
" ), '.' " # an array of integers.
")::int[] "
}
)
def available_versions(self):
return self.filter(available=True).order_by('-natural_version')
def last_stable(self):
return self.available_versions().filter(stable=True).first()
class VersionRecord(models.Model):
objects = VersionRecordManager()
version = models.CharField(max_length=64, db_index=True)
available = models.BooleanField(default=False, db_index=True)
stable = models.BooleanField(default=False, db_index=True)
我是 developing/maintaining/curating 从各种可穿戴研究设备收集的测试结果数据库。每个设备都有三个主要组件,每个组件都有两个版本号(固件和硬件)。我正在使用 Django 应用程序为数据库提供 Web 界面。版本号表示为直接整数或三元组(主要、次要、内部版本)。整数很容易处理,我显然可以将三元组存储为字符串,但作为字符串它们不会正确排序或正确比较,例如,如果我只想要固件版本低于 [=22 的设备产生的测试结果=].
由于第二个 'decimal point' 分隔符,我不能使用浮点数。我想也许可以通过将其存储为日期来进行黑客攻击,但这会将次要号码限制为小于 12,并将版本号限制为小于 29,此外我 知道 这是一个可怕的解决方案。我什至不应该承认我想到了它。
缺少一些 PL/SQL 扩展数据库以提供正确处理字符串的比较函数,有没有简单的方法可以做到这一点?如果没有,我什至可以将我的自定义 SQL 函数与 django 一起使用吗?
将它们存储为零填充字符串:
>>> def sortable_version(version):
... return '.'.join(bit.zfill(5) for bit in version.split('.'))
...
>>> sortable_version('1.1')
'00001.00001'
>>> sortable_version('2')
'00002'
>>> sortable_version('2.1.10')
'00002.00001.00010'
>>> sortable_version('10.1')
'00010.00001'
>>> sortable_version('2') > sortable_version('1.3.4')
True
>>> sortable_version('10') > sortable_version('2')
True
>>> sortable_version('2.3.4') > sortable_version('2')
True
并且您始终可以使用这种零填充格式显示常规版本:
>>> def normalize_version(padded_version):
... return '.'.join(bit.lstrip('0') for bit in padded_version.split('.'))
...
>>> normalize_version('00010')
'10'
>>> normalize_version('00002.00001.00010')
'2.1.10'
您要找的是"natural sort"。
另一种实现方式是使用数据库排序。这是 Postgres 的示例:
class VersionRecordManager(models.Manager):
def get_queryset(self):
return super().get_queryset().extra(
select={
'natural_version':
"string_to_array( "
" regexp_replace( " # Remove everything except digits
" version, '[^\d\.]+', '', 'g' " # and dots, then split string into
" ), '.' " # an array of integers.
")::int[] "
}
)
def available_versions(self):
return self.filter(available=True).order_by('-natural_version')
def last_stable(self):
return self.available_versions().filter(stable=True).first()
class VersionRecord(models.Model):
objects = VersionRecordManager()
version = models.CharField(max_length=64, db_index=True)
available = models.BooleanField(default=False, db_index=True)
stable = models.BooleanField(default=False, db_index=True)