代码之家  ›  专栏  ›  技术社区  ›  chefsmart

Django中的唯一模型字段和区分大小写(postgres)

  •  25
  • chefsmart  · 技术社区  · 16 年前

    考虑以下情况:

    假设我的应用程序允许用户在他们的应用程序中创建州/省 国家。为了清楚起见,我们只考虑ASCII字符 在这里

    在美国,用户可以创建一个名为“德克萨斯”的州。如果此应用程序 是在内部使用的,假设用户不在乎它是否是 拼写为“texas”或“texas”或“texas”

    如果模型如下所示:

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

    唯一性在postgres中是区分大小写的;也就是说,博士后 被认为是独一无二的。

    在这种情况下可以做些什么来防止这种行为。怎么做 一步到位- 不直观 博士后

    现在,我正在做以下工作,以防止案件的产生-

    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
    

    只是想知道是否有一个内置的或 更好的方法?也许db_类型会有所帮助?也许还有别的解决办法?

    10 回复  |  直到 16 年前
        1
  •  31
  •   MichielB    9 年前

    可以定义从中派生的自定义模型字段 models.CharField 此字段可以检查重复值,忽略大小写。

    http://docs.djangoproject.com/en/dev/howto/custom-model-fields/

    http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/files.py 有关如何通过子类化现有字段来创建自定义字段的示例,请参见。

    https://www.postgresql.org/docs/current/static/citext.html

    如果使用此模块,自定义字段可以将“db_type”定义为PostgreSQL数据库的CITEXT。

        2
  •  8
  •   Foo    15 年前

    或者,您可以将默认查询集管理器更改为对字段执行不区分大小写的查找。在试图解决类似问题时,我遇到了:

    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
    
        3
  •  7
  •   o17t H1H' S'k    11 年前

    1. 在您的models.py中添加:

      from django.db.models import fields
      
      class CaseInsensitiveTextField(fields.TextField):
          def db_type(self, connection):
              return "citext"
      

      参考: https://github.com/zacharyvoase/django-postgres/blob/master/django_postgres/citext.py

    2. 在模型中使用:name=CaseInsensitiveTextField(unique=True)

        4
  •  6
  •   Alex Brasetvik    16 年前

    在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"
    
        5
  •  6
  •   suhailvs    12 年前

    class State(models.Model):
        name = models.CharField(max_length=50, unique=True)
    
        def clean(self):
            self.name = self.name.capitalize()
    
        6
  •  5
  •   Michal Čihař    16 年前

    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)
    
        7
  •  3
  •   Luan Francisco Lima Fernandes    8 年前

    您可以在序列化程序上的UniqueValidator中使用lookup='iexact',如下所示:

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

    django版本:1.11.6

        8
  •  2
  •   Levi Payne    7 年前

    如果不想使用postgres特定的解决方案,可以使用 upper() Field 混入,覆盖 get_lookup()

    class CaseInsensitiveFieldMixin:
        """
        Field mixin that uses case-insensitive lookup alternatives if they exist.
        """
    
        LOOKUP_CONVERSIONS = {
            'exact': 'iexact',
            'contains': 'icontains',
            'startswith': 'istartswith',
            'endswith': 'iendswith',
            'regex': 'iregex',
        }
    
        def get_lookup(self, lookup_name):
            converted = self.LOOKUP_CONVERSIONS.get(lookup_name, lookup_name)
            return super().get_lookup(converted)
    

    你是这样使用它的:

    from django.db import models
    
    
    class CICharField(CaseInsensitiveFieldMixin, models.CharField):
        pass
    
    
    class CIEmailField(CaseInsensitiveFieldMixin, models.EmailField):
        pass
    
    
    class TestModel(models.Model):
        name = CICharField(unique=True, max_length=20)
        email = CIEmailField(unique=True)
    

    您可以阅读有关此方法的更多信息 here

        9
  •  1
  •   Rishabh Manocha    16 年前

    您可以通过覆盖模型的save方法来实现这一点-请参见 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分支将使我们能够更轻松地做到这一点。

        10
  •  0
  •   nymk    11 年前

    suhail的解决方案对我来说不需要启用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()