2009-12-07 4 views
22

는 다음과 같은 상황을 생각해 보자. 명확성을 위해 ASCII 문자 인 만 있습니다.고유 모델 필드와 대소 문자 구분 (포스트 그레스)

미국에서는 사용자가 "텍사스"라고하는 상태를 만들 수 있습니다. 이 응용 프로그램 가 내부적으로 사용하는 경우, 그것은 이 경우 "텍사스"또는 "텍사스"또는 "텍사스"

그러나 중요한 것은 시스템이 "텍사스"의 생성을 방지한다 철자 경우의 사용자가 신경 쓰지 않는 가정 해 봅시다 "Texas"가 이미 데이터베이스에 있습니다.

모델이 같은 경우 다음

class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

대소 문자를 구분 포스트 그레스에있을 것 고유성; 즉, 포스트 그레스 은 사용자가이 고유하다고 간주하므로 "텍사스"와 "텍사스"를 모두 만들 수 있습니다.

이러한 상황을 방지하기 위해 수행 할 수있는 작업은 다음과 같습니다. 지금 나는 대소 를 구분 중복 생성을 방지하기 위해 다음과 같은 일을 해요

장고와 insenstitive 고유성과 포스트 그레스를 소문자 제공에 대해 한 가지를 수행하는 방법.

class CreateStateForm(forms.ModelForm): 
    def clean_name(self): 
     name = self.cleaned_data['name'] 
     try: 
      State.objects.get(name__iexact=name) 
     except ObjectDoesNotExist: 
      return name 
     raise forms.ValidationError('State already exists.') 

    class Meta: 
     model = State 

나는이 검사를해야 할 경우가 많으며 비슷한 iexact 검사를 사방에 작성하지 않아도되는 경우가 많습니다.

더 좋은 방법으로 내장 또는 이 있는지 궁금하십니까? 아마도 db_type이 도움이 될까요? 어쩌면 다른 해결책이 있을까요?

+0

을 당신이 이미 바로 그 일을하고 생각합니다. 적어도 그것이 태그를 사용하는 방식이므로 내 태그 클라우드는 서로 "태그", "태그"및 "태그"로 끝나지 않습니다. –

+1

네,하지만 여기 장고의 고유 한 내장은별로 도움이되지 않습니까? 결국 나는 어디에서나 유일한 수표를해야만한다. – chefsmart

+1

문자열을 데이터베이스에 소문자로 입력하도록 앱을 코딩 할 것입니다. 그런 다음에 대해 확인하십시오. – DrBloodmoney

답변

28

models.CharField에서 파생 된 사용자 정의 모델 필드를 정의 할 수 있습니다. 이 필드는 대소 문자를 무시하고 중복 값을 확인할 수 있습니다.

사용자 정의 필드 문서는 기존 필드를 서브 클래 싱하여 사용자 정의 필드를 만드는 방법의 예를 들어 http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/files.py에서 http://docs.djangoproject.com/en/dev/howto/custom-model-fields/

봐 여기에있다.이 모듈을 사용하는 경우

당신은 사용자 정의 필드는 PostgreSQL을 데이터베이스에 대한 CITEXT으로 "에서 db_type"를 정의 할 수 PostgreSQL을 https://www.postgresql.org/docs/current/static/citext.html

의 citext 모듈을 사용할 수 있습니다.

이렇게하면 사용자 지정 필드의 고유 한 값을 대/소문자를 구분하지 않고 비교할 수 있습니다.

+0

이것은 흥미로운 해결책이며 여기에 언급 된 다른 솔루션보다 장고 - 이시 틱적인 것으로 보인다. – chefsmart

+0

명시 적 단계에 대한 내 대답은 아래를 참조하십시오. – eyaler

+0

Thanks @MichielB – Mayuresh

1

모델의 저장 방법을 덮어 쓰면됩니다 (docs 참조). 당신은 기본적으로 같은 것을 할 거라고 : 또한

class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

    def save(self, force_insert=False, force_update=False): 
     if State.objects.get(name__iexact = self.name): 
      return 
     else: 
      super(State, self).save(force_insert, force_update) 

을, 나는 이것에 대해 잘못 될 수 있지만, 곧 모델 검증의 SoC 지점은 우리가 쉽게 더 많은 일을 할 수 있습니다.

+0

이것은 본질적으로 제가 이미하고있는 것과 같습니다. 사실, 내가 지금하고있는 방식대로 저장 중에 처리하는 것보다 낫습니다. – chefsmart

+0

두 번째 읽기에서 당신 말이 맞습니다. AFAIK, 폼의 유효성 확인을 사용하면 데이터가 폼을 통해 삽입되지 않는 한 최선의 방법이 될 것입니다. –

+0

양식을 통해 데이터를 삽입 할 수 없습니다. 특히 타사 앱을 사용하는 경우 – dbn

3

저장을 재정의하기 위해 이미 언급 한 옵션 외에도 모든 텍스트를 소문자로 데이터베이스에 저장하고 표시 할 때 대문자로 사용할 수 있습니다.

class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

    def save(self, force_insert=False, force_update=False): 
     self.name = self.name.lower() 
     super(State, self).save(force_insert, force_update) 
5

사물의 Postgres 측에서 기능적 고유 색인을 사용하면 대소 문자없이 고유 한 값을 적용 할 수 있습니다. citext도 언급되었지만, 이것은 PostgreSQL의 이전 버전에서 작동 할 것이고 일반적으로 유용한 기술입니다.

예 :

# create table foo(bar text); 
CREATE TABLE 
# create unique index foo_bar on foo(lower(bar)); 
CREATE INDEX 
# insert into foo values ('Texas'); 
INSERT 0 1 
# insert into foo values ('texas'); 
ERROR: duplicate key value violates unique constraint "foo_bar" 
+0

을 참조하십시오. 시도한 결과 작동하는지 확인할 수 있습니다. 하지만 Mayuresh의 대답은 내가 장고에 살 수있게 해준다. – chefsmart

+2

글쎄, 당신은 항상 데이터베이스에 제약 조건을 적용해야합니다. –

+0

Django가 모델을 만든 후에 데이터베이스 수준에서이를 '자동으로'시행하는 좋은 방법은 무엇입니까? django의'syncdb' 다음에 수동으로 db를 편집하는 대신 스크립팅 방법을 묻습니다. – Medorator

6

는 다른 방법 필드에 대소 문자를 구분하지 룩업을 수행하는 기본 쿼리 설정 관리자를 변경할 수 있습니다. 비슷한 문제를 해결하기 위해 노력에서 나는 건너 온 : 편의를 위해 여기에 붙여

http://djangosnippets.org/snippets/305/

코드 :

from django.db.models import Manager 
from django.db.models.query import QuerySet 

class CaseInsensitiveQuerySet(QuerySet): 
    def _filter_or_exclude(self, mapper, *args, **kwargs): 
     # 'name' is a field in your Model whose lookups you want case-insensitive by default 
     if 'name' in kwargs: 
      kwargs['name__iexact'] = kwargs['name'] 
      del kwargs['name'] 
     return super(CaseInsensitiveQuerySet, self)._filter_or_exclude(mapper, *args, **kwargs) 

# custom manager that overrides the initial query set 
class TagManager(Manager): 
    def get_query_set(self): 
     return CaseInsensitiveQuerySet(self.model) 

# and the model itself 
class Tag(models.Model): 
    name = models.CharField(maxlength=50, unique=True, db_index=True) 

    objects = TagManager() 

    def __str__(self): 
     return self.name 
+0

이 접근법은 name__in = [] 또는 related__name = 같은 복합 쿼리에서는 작동하지 않습니다. – dbn

2

매우 간단한 해결책 : Mayuresh의 답변

class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

    def clean(self): 
     self.name = self.name.capitalize() 
0

해결하여 models.py 추가 기능에서

  • citext를 사용할 필요없이 나를 위해 일했고, 깨끗한 함수만으로도 꽤 쉬운 해결책이었고 대신 자본을 사용했습니다. upper(). Mayuresh의 솔루션도 작동하지만 CharField에서 TextField으로 필드를 변경했습니다.

    class State(models.Model): 
    
        name = models.CharField(max_length=50, unique=True) 
    
        def clean(self): 
         self.name = self.name.upper() 
    
  • 1

    이 같은 시리얼에 UniqueValidator에 조회 = 'iexact'를 사용할 수 있습니다

    class StateSerializer(serializers.ModelSerializer): 
        name = serializers.CharField(validators=[ 
        UniqueValidator(
         queryset=models.State.objects.all(),lookup='iexact' 
        )] 
    

    장고 버전 : 1.11.6