使用django中的regex按指定字段获取已排序的查询集

时间:2018-06-01 14:03:59

标签: python regex django django-models django-queryset

我在经理中有以下内容:

class NullIf(Func):
template = "NULLIF(%(expressions)s, '')"

class MySiteManager(models.Manager):

def get_queryset(self):
    qws = MySiteQuerySet(self.model, using=self._db).filter(
        some_id=settings.BASE_SOME_ID).annotate(
            # This is made for sorting by short labels as by numeric values
            short_label_numeric=Cast(
                NullIf(Func(
                    F('short_label'),
                    Value('^(\D+)|(\w+)'),
                    Value(''),
                    Value('g'),
                    function='regexp_replace')),
                models.BigIntegerField())
            ).order_by('short_label_numeric', 'short_label')

    for q in qws:
        print(q.short_label, end='\n')

    return qws

打印值的输出如下:

1 10 100 101 102 103 104 105 106 107 108 109 11 110 111 112 113 114 115 116 117 118 119 12 120 121 122 123 124 125 126 127 128 129 13 130 131 132 133 134 135 136 137 138 139 14 140 141 142 143 144 145 146 147 148 149 15 150 151 152 153 154 155 156 157 158 159 16 17 18 19 20 200C 21 22 23 24 25 26 260 261 262 263 264 265fs 266fs 267C 268C 269C 27 273C 274C 275C 276C 28 29 2C 三十 302 31 32C 33C 34 35C 36 37 38 3C 4 五 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 524 6 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 7 701 702 703 704 705 706 707 708 709 710 8 801 802 803 804 805 806 807 808 809 810 9 901 902 S1 S10 S11 S12 S13 S14 S15 S16 S17 S18 S19 S2 S20 S3 S4 S5 S6 S7 S8 S9

我的问题是: 如何使用输出构建查询集,例如1 2 3 3c 4 5 6 6c ... 264 265fs 266fs 267c 268c 269c ... S1 S2 S3 S4 ???有人有任何假设吗?

2 个答案:

答案 0 :(得分:1)

主要思想是按数字排序,然后按标签的char部分,我无法重现和测试,但解决方案可能看起来像:

首先是sql:

SELECT 
  (regexp_matches(short_label, '^\d+'))[1]::numeric AS ln,
  regexp_matches(short_label, '^\D+') as ls,
  short_label
FROM YOUR_APP_TABLENAME ORDER BY 1, 2, 3;
orm中的注释:

对于第一个sql条件我创建custom Func

In [1]: from myapp.models import *

In [2]: from django.db.models import F, Func, Value
   ...: 
   ...: class StartNumeric(Func):
   ...:     function = 'REGEXP_MATCHES'
   ...:     template = "(%(function)s(%(expressions)s, '^\d+'))[1]::int"
   ...: 
   ...: qs = Ingredient.objects.annotate(
   ...:     ln=StartNumeric('short_label'),
   ...:     ls=Func('short_label', Value('^\D+'), function='regexp_matches'),
   ...:     ).values('ln').order_by('ln', 'ls', 'short_label')
   ...: 
   ...:     

In [3]: print(qs.query)
SELECT (REGEXP_MATCHES("myapp_ingredient"."short_label", '^\d+'))[1]::int AS "ln" FROM "myapp_ingredient" ORDER BY "ln" ASC, regexp_matches("myapp_ingredient"."short_label", ^\D+) ASC, "myapp_ingredient"."short_label" ASC

In [4]: data = qs.values_list('short_label', flat=True)
   ...: print(list(data))
   ...: 
   ...: 
['1', '2c', '3c', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32c', '33c', '34', '35c', '36', '37', '38', '100', '101', '102', '103', '104', '105', '106', '107', '108', '109', '110', '111', '112', '113', '114', '115', '116', '117', '118', '119', '120', '121', '122', '123', '124', '125', '126', '127', '128', '129', '130', '131', '132', '133', '134', '135', '136', '137', '138', '139', '140', '141', '142', '143', '144', '145', '146', '147', '148', '149', '150', '151', '152', '153', '154', '155', '156', '157', '158', '159', '200c', '260', '261', '262', '263', '264', '265fs', '266fs', '267c', '268c', '269c', '273c', '274c', '275c', '276c', '302', '501', '502', '503', '504', '505', '506', '507', '508', '509', '510', '511', '512', '513', '514', '515', '516', '517', '518', '519', '520', '521', '522', '524', '601', '602', '603', '604', '605', '606', '607', '608', '609', '610', '611', '612', '613', '614', '615', '616', '617', '618', '619', '620', '621', '622', '623', '701', '702', '703', '703', '704', '705', '706', '707', '708', '709', '710', '801', '802', '803', '804', '805', '806', '807', '808', '809', '810', '901', '902', 'aaaa', 'ddd', 'ddeee', 'rrrrr', 'S1', 'S10', 'S11', 'S12', 'S13', 'S14', 'S15', 'S16', 'S17', 'S18', 'S19', 'S2', 'S20', 'S3', 'S4', 'S5', 'S6', 'S7', 'S8', 'S9', 'vvvv', 'zzzz']

希望有所帮助

答案 1 :(得分:1)

如何使用natural sort

对输出进行排序
import re
_nsre = re.compile(r'(\d+)')
def natural_sort_key(s):
    return [int(text) if text.isdigit() else text.lower()
            for text in re.split(_nsre, s)]

s = "1 10 2 100 101 102 103 104 105 106 107 108 109 11 110 111 112 113 114 115 116 117 118 119 12 120 121 122 123 124 125 126 127 128 129 13 130 131 132 133 134 135 136 137 138 139 14 140 141 142 143 144 145 146 147 148 149 15 150 151 152 153 154 155 156 157 158 159 16 17 18 19 20 200c 21 22 23 24 25 26 260 261 262 263 264 265fs 266fs 267c 268c 269c 27 273c 274c 275c 276c 28 29 2c 30 302 31 32c 33c 34 35c 36 37 38 3c 4 5 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 524 6 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 7 701 702 703 704 705 706 707 708 709 710 8 801 802 803 804 805 806 807 808 809 810 9 901 902 S1 S10 S11 S12 S13 S14 S15 S16 S17 S18 S19 S2 S20 S3 S4 S5 S6 S7 S8 S9"
list1 = s.split(' ')
list1.sort(key=natural_sort_key)

输出list1

['1',  '2',  '2c',  '3c',  '4',  '5',  '6',  '7',  '8',  '9',  '10',  '11',  '12',  '13',  '14',  '15',  '16',  '17',  '18',  '19',  '20',  '21',  '22',  '23',  '24',  '25',  '26',  '27',  '28',  '29',  '30',  '31',  '32c',  '33c',  '34',  '35c',  '36',  '37',  '38',  '100',  '101',  '102',  '103',  '104',  '105',  '106',  '107',  '108',  '109',  '110',  '111',  '112',  '113',  '114',  '115',  '116',  '117',  '118',  '119',  '120',  '121',  '122',  '123',  '124',  '125',  '126',  '127',  '128',  '129',  '130',  '131',  '132',  '133',  '134',  '135',  '136',  '137',  '138',  '139',  '140',  '141',  '142',  '143',  '144',  '145',  '146',  '147',  '148',  '149',  '150',  '151',  '152',  '153',  '154',  '155',  '156',  '157',  '158',  '159',  '200c',  '260',  '261',  '262',  '263',  '264',  '265fs',  '266fs',  '267c',  '268c',  '269c',  '273c',  '274c',  '275c',  '276c',  '302',  '501',  '502',  '503',  '504',  '505',  '506',  '507',  '508',  '509',  '510',  '511',  '512',  '513',  '514',  '515',  '516',  '517',  '518',  '519',  '520',  '521',  '522',  '524',  '601',  '602',  '603',  '604',  '605',  '606',  '607',  '608',  '609',  '610',  '611',  '612',  '613',  '614',  '615',  '616',  '617',  '618',  '619',  '620',  '621',  '622',  '623',  '701',  '702',  '703',  '704',  '705',  '706',  '707',  '708',  '709',  '710',  '801',  '802',  '803',  '804',  '805',  '806',  '807',  '808',  '809',  '810',  '901',  '902',  'S1',  'S2',  'S3',  'S4',  'S5',  'S6',  'S7',  'S8',  'S9',  'S10',  'S11',  'S12',  'S13',  'S14',  'S15',  'S16',  'S17',  'S18',  'S19',  'S20']