Unique model field in Django and case sensitivity (postgres)

前端 未结 10 729
时光取名叫无心
时光取名叫无心 2020-12-13 13:37

Consider the following situation: -

Suppose my app allows users to create the states / provinces in their country. Just for clarity, we are considering only ASCII ch

相关标签:
10条回答
  • 2020-12-13 14:12

    Alternatively you can change the default Query Set Manager to do case insensitive look-ups on the field. In trying to solve a similar problem I came across:

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

    Code pasted here for convenience:

    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 讨论(0)
  • 2020-12-13 14:12

    Explicit steps for Mayuresh's answer:

    1. in postgres do: CREATE EXTENSION citext;

    2. in your models.py add:

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

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

    3. in your model use: name = CaseInsensitiveTextField(unique=True)

    0 讨论(0)
  • 2020-12-13 14:14

    If you don't want to use a postgres-specific solution, you can create a unique index on the field with upper() to enforce uniqueness at the database level, then create a custom Field mixin that overrides get_lookup() to convert case-sensitive lookups to their case-insensitive versions. The mixin looks like this:

    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)
    

    And you use it like this:

    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)
    

    You can read more about this approach here.

    0 讨论(0)
  • 2020-12-13 14:21

    a very simple solution:

    class State(models.Model):
        name = models.CharField(max_length=50, unique=True)
    
        def clean(self):
            self.name = self.name.capitalize()
    
    0 讨论(0)
  • 2020-12-13 14:22

    You could define a custom model field derived from models.CharField. This field could check for duplicate values, ignoring the case.

    Custom fields documentation is here http://docs.djangoproject.com/en/dev/howto/custom-model-fields/

    Look at http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/files.py for an example of how to create a custom field by subclassing an existing field.

    You could use the citext module of PostgreSQL https://www.postgresql.org/docs/current/static/citext.html

    If you use this module, the the custom field could define "db_type" as CITEXT for PostgreSQL databases.

    This would lead to case insensitive comparison for unique values in the custom field.

    0 讨论(0)
  • 2020-12-13 14:22

    Solution from suhail worked for me without the need to enable citext, pretty easy solution only a clean function and instead of capitalize I used upper(). Mayuresh's solution also works but changed the field from CharField to TextField.

    class State(models.Model):
    
        name = models.CharField(max_length=50, unique=True)
    
        def clean(self):
            self.name = self.name.upper()
    
    0 讨论(0)
提交回复
热议问题