2010-06-03 9 views
8

거리 주소를 저장하는 데 사용되는 CharField를 사용하여 상당히 많은 개체 목록을 출력해야하는 상황에 처해 있습니다.Django : order_by로 숫자 값 정렬

내 문제는 명백하게 데이터가 Charfield이고 예측 가능한 결과이므로 ASCII 코드로 정렬된다는 것입니다.

1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21.... 

이제 명백한 단계는, (IntegerField이의 말을하자)이 Charfield에게 적절한 필드 유형을 변경 일부 주소는 아파트가있을 수 있기 때문에 그러나 그것은 "128A"처럼 .. 작동하지 않을 수있을 것이다.

정말 내가 주문할 수 있습니다 방법을 모르는이 제대로 ..

+0

이 문제에 대한 해결책을 찾았는지 궁금합니다. 많은 감사합니다. N –

답변

15

, 당신은으로하여 extra 방법을 통해 정수로 캐스팅 데이터베이스 및 순서를 얻을 수 :

MyModel.objects.extra(
    select={'myinteger': 'CAST(mycharfield AS INTEGER)'} 
).order_by('myinteger') 
+1

모든 주소가 숫자로 시작하는 것은 아닙니다. 이 접근 방식은 "내 char 필드에 숫자가 있습니다"라는 특수한 경우에는 작동하지만 혼합 된 데이터를 정렬하지는 못합니다. –

+0

매우 흥미로운 여분의 사용, 나는 거의 그 방법으로 노는.하지만 불행히도 내 상황에서는 작동하지 않는 것 같습니다. –

+2

MYSQL 버전에서 INTEGER 대신 'SIGNED'또는 'UNSIGNED'를 사용하십시오. – Adriaan

2

당신에 대한 최대있어 문제는 파일 이름으로 정렬 할 때 파일 이름이 정렬되는 방법을 매우 유사하다. 여기서 "2 Foo.mp3"가 "12 Foo.mp3"앞에 표시되기를 원합니다.

일반적으로 숫자를 고정 된 숫자로 확장 한 다음 정규화 된 양식을 기준으로 정렬하는 것이 일반적인 방법입니다. 즉, 정렬을 위해 "2 Foo.mp3"이 "0000000002 Foo.mp3"으로 확장 될 수 있습니다.

장고는 여기서 직접 도와주지 않습니다. "정규화 된"주소를 저장하는 필드를 추가하고 데이터베이스 order_by을 갖거나 레코드 목록을 전달하기 전에 주소 레코드에 대한 사용자 지정 정렬 (보기에서 사용하는 도우미)을 수행 할 수 있습니다 템플릿에. 당신은 단지 정수 분야에서이 있는지 경우

3

위대한 팁! 그것은 나를 위해 일합니다! 안전하게 문자/텍스트 필드에 다음 코드를 사용하여 주조 오류를 방지 할 수

revisioned_objects = revisioned_objects.extra(select={'casted_object_id': 'CAST(object_id AS INTEGER)'}).extra(order_by = ['casted_object_id']) 
9

당신은 PostgreSQL을 (MySQL은 확실하지 않음)를 사용하는 경우 : : : 그건 내 코드의

MyModel.objects.extra(
    select={'myinteger': "CAST(substring(charfield FROM '^[0-9]+') AS INTEGER)"} 
).order_by('myinteger') 
+0

이것은 훌륭한 제안입니다! –

+0

숫자가있는 문자열을 ''[0-9] + ' – Ajoy

+0

으로 정렬해야하는 경우 가장 좋습니다. MariaDB의 구문은 다음과 같습니다. "CAST (REGEXP_SUBSTR (name,'^ ​​[0-9] + ') AS INTEGER) "를 참조하십시오. 감사!! –

7

장고 deprecateextra() 방법을 시도했지만 v1.10에 Cast()을 도입했습니다. SQLite는 (적어도)에서 CAST10a과 같은 값을 취할 수 및 정수 10에 캐스팅됩니다, 그래서 당신은 할 수 있습니다 :

from django.db.models import IntegerField 
from django.db.models.functions import Cast 

MyModel.objects.annotate(
    my_integer_field=Cast('my_char_field', IntegerField()) 
).order_by('my_integer_field', 'my_char_field') 
순으로 다음, 첫번째 숫자 거리 번호를 기준으로 정렬 개체를 반환합니다

, 예. 이 아닌 숫자를 허용 할 경우

class VersionRecordManager(models.Manager): 

    def get_queryset(self): 
     return super().get_queryset().extra(
      select={ 
       'natural_version': "string_to_array(version, '.')::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) 

: 경우 ...14, 15a, 15b, 16, 16a, 17...

+1

'Cast '는'10a '와 같은 값을 지원하지 않습니다. 하나를 받으면'DataError : integer에 대한 입력 구문이 유효하지 않습니다. ' (django.db.models.expressions에서 '가져 F, 가치, Func' 'queryset.annotate (my_integer_field = 주연 : 나는 모든 수없는 값의 문자 (PostgreSQL을)를 제거하여 해결 방법을 발견 FUNC (F ('my_char_field') 값 ('[^ \ D]) 값 (' ') 값 ('g ') 함수 ='REGEXP_REPLACE ') IntegerField()) )를 ' – Filly

+1

@ Filly 당신은 오류를 던진 모든 예를 들어 줄 수 있습니까? – practual

1

당신은 점으로 구분 된 여러 숫자로 구성된 버전 번호를 정렬 할 필요가있다 (예를 들어 1.9.0, 1.10.0을), 여기에 포스트 그레스 전용 솔루션입니다 문자 (예 : 0.9.0 beta, 2.0.0 stable) :

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[]        " 
     } 
    )