Мне нужно отсортировать список объектов, содержащих имена хостов.
Имена хостов представлены в следующих форматах: h1, h5, h10, h12, h12-abc, h1000, x10
Если я использую order_by('hostname'), он будет выглядеть следующим образом:
h1, h10, h1000, h12, h12-abc, h5, x10
Как мне добиться такого порядка:
h1, h5, h10, h12, h12-abc, h1000, x10
Имена хостов всегда начинаются с символа, затем 1-4 цифры и частично расширение, например «-abc».
Я думаю, мне нужно использовать Substr(), чтобы извлечь число и каким-то образом упорядочить числа, чтобы «10» не было указано перед «5».
С помощью поиска я нашел несколько старых примеров с extra(), но в документации Django говорится, что в будущем он будет устаревшим, и «используйте этот метод в крайнем случае». https://docs.djangoproject.com/en/2.1/ref/models/querysets/#extra
Что такое перспективный способ сделать это?
@DanSwain, я думаю, что ОП хочет использовать django orm
какую базу данных вы используете?
@BearBrown PostgreSQL
Вы смотрели функции БД? Я записал эту идею, но понятия не имею, сработает ли она, но может быть отправной точкой Hosts.objects.annotate( letter=Substr("hostname", 0, 1), extension=StrIndex(F("hostname"), Value("-")), ).annotate( numerical=Cast(Substr("hostname", 1, Coalesce("extension", Value(None))), IntegerField()), ).order_by("extensions", "numerical")





вы можете использовать f-выражения
from django.db.models import F, Value, TextField, IntegerField
from django.contrib.postgres.fields import ArrayField
from django.db.models.expressions import Func
sql = ordModel.objects.annotate(
num=Cast(
Func(
F('hostname'),
Value("\d+"),
function='regexp_matches',
),
output_field=ArrayField(IntegerField())
),
char=Func(
F('hostname'),
Value("\D+"),
function='regexp_matches',
output_field=ArrayField(TextField())
)
).order_by('char', 'num', ).values('hostname')
мой результат для того же списка значений:
<QuerySet [
{'hostname': 'h1'},
{'hostname': 'h5'},
{'hostname': 'h10'},
{'hostname': 'h12'},
{'hostname': 'h12-abc'},
{'hostname': 'h1000'},
{'hostname': 'x10'}]>
о функции базы данных вы можете прочитатьL regexp_match
вывод выглядит великолепно, но откуда берется function='regexp_matches'?
это функция postgres postgresql.org/docs/10/…
на второй взгляд порядок не совсем правильный. он заказывает как order_by(hostname) , чего я стараюсь избегать. это должно быть h1, h5, h10, h12, h12-abc, h1000, x10 - не h1, h10, h1000, h12 и т. д. - есть идеи, как это исправить?
@FelixK я исправил регулярное выражение, теперь результат должен быть в порядке.
Для PostgreSQL это работает, поэтому я приму это как ответ. Ниже вы найдете мое альтернативное независимое от базы данных решение, которое тоже работает. Спасибо!
Я заставил его работать с дополнительным полем normalized_hostname, которое также не зависит от базы данных. Реализовано в модели с помощью Django Signals pre_save()
https://docs.djangoproject.com/en/2.1/ref/signals/#pre-save
Код ниже преобразует имя хоста в формат, который затем можно использовать с order_by('normalized_hostname')
Examples:
hostname -> normalized_hostname
h1 -> h0001
h5 -> h0005,
h10 -> h0010
h12 -> h0012
h12-abc -> h0012-abc
h1000 -> h1000
x10 -> x0010
модели.py
from django.db.models.signals import pre_save
import re
class MyModel(models.Model):
the solution is also database independent hostname = models.CharField(max_length=64)
normalized_hostname = models.CharField(max_length=64)
def create_normalize_hostname(instance):
normalize = re.sub("\D", "", instance.hostname).zfill(4)
normalized_hostname = re.sub("(\d{1,4})", normalize, instance.hostname)
return normalized_hostname
def receiver(sender, instance, *args, **kwargs)
instance.normalized_hostname = create_normalize_hostname(instance)
pre_save.connect(receiver, sender=ModelName)
Теперь будет так:
h1, h5, h10, h12, h12-abc, h1000, x10
Похоже, эти два ответа должны вам помочь: stackoverflow.com/questions/5967500/… и stackoverflow.com/questions/4836710/….