Django LowerCaseCharField

Мы реализовали LowerCaseCharField. Мы будем рады услышать предложения по лучшей реализации.

from django.db.models.fields import CharField

class LowerCaseCharField(CharField):
    """
    Defines a charfield which automatically converts all inputs to
    lowercase and saves.
    """

    def pre_save(self, model_instance, add):
        """
        Converts the string to lowercase before saving.
        """
        current_value = getattr(model_instance, self.attname)
        setattr(model_instance, self.attname, current_value.lower())
        return getattr(model_instance, self.attname)

На самом деле мы любим иметь это:

> modelinstance.field_name="TEST"
> print modelinstance.field_name
'test'

текущая реализация преобразует в нижний регистр только при сохранении.


person Can Burak Çilingir    schedule 28.02.2010    source источник
comment
См. решение @mcastle stackoverflow.com/a/24495517/2034015.   -  person zurfyx    schedule 25.03.2016


Ответы (4)


Вы можете переопределить to_python, что позволит вам сравнивать нестрочные строки при поиске в базе данных. Фактический метод — get_prep_value, но поскольку он вызывает to_python вместо CharField, его удобнее переопределить:

def to_python(self, value):
    value = super(LowerCaseCharField, self).to_python(value)
    if isinstance(value, basestring):
        return value.lower()
    return value

Теперь вы можете выполнять такие запросы, как:

MyModel.objects.filter(lccf="MiXeD")

Изменить:

Перечитывая ваш вопрос, похоже, вы хотите, чтобы снижение вступило в силу немедленно. Для этого вам потребуется создать дескриптор (объект Python в новом стиле с методами __get__ и __set__, см. документы Python и код django для связанных моделей) и переопределите contribute_to_class в поле, чтобы установить поле модели в соответствии с вашим дескриптором.

Вот полный пример из моей головы, который должен быть повторно использован для всех полей, которые хотят изменить значение при настройке.

class ModifyingFieldDescriptor(object):
    """ Modifies a field when set using the field's (overriden) .to_python() method. """
    def __init__(self, field):  
        self.field = field  
    def __get__(self, instance, owner=None):
        if instance is None:
            raise AttributeError('Can only be accessed via an instance.')  
        return instance.__dict__[self.field.name]
    def __set__(self, instance, value):
        instance.__dict__[self.field.name] = self.field.to_python(value)

class LowerCaseCharField(CharField):
    def to_python(self, value):
        value = super(LowerCaseCharField, self).to_python(value)
        if isinstance(value, basestring):
            return value.lower()
        return value
    def contribute_to_class(self, cls, name):
        super(LowerCaseCharField, self).contribute_to_class(cls, name)
        setattr(cls, self.name, ModifyingFieldDescriptor(self))
person Will Hardy    schedule 28.02.2010
comment
Необходимость в этом возникла, когда мы храним адреса электронной почты, так что на самом деле одного to_python было бы достаточно для решения нашей проблемы. Теперь дискуссия о нормализации данных в базе данных путем преобразования их в нижний регистр, логична она или нет. RFC, указанный на en.wikipedia.org/wiki/E-mail_address#RFC_specification говорит, что не рекомендуется различать адреса электронной почты в верхнем и нижнем регистре. Путь, который я хотел бы выбрать, таков: 1. Нормализовать его, преобразовав в нижний регистр 2. Игнорировать регистр при запросе, поскольку пользовательский ввод будет нечувствительным к регистру. - person Can Burak Çilingir; 28.02.2010
comment
Приведенный выше код должен выполнять оба действия: 1. нормализуется строка в момент установки атрибута (ModifyingFieldDescriptor) 2. нормализуются все строки, используемые в запросах, поэтому по умолчанию они нечувствительны к регистру (to_python). Нормализация — хороший подход, потому что вам никогда не понадобится исходное значение с учетом регистра, и это позволит вам иметь уникальные проверки на основе базы данных и т. д. - person Will Hardy; 28.02.2010
comment
Ваш подход кажется правильным способом сделать это. Это первый раз, когда нам нужно использовать submit_to_class, так что это новое приключение. Спасибо за ваше время и, на самом деле, также рекламу stackoverflow для меня. - person Can Burak Çilingir; 28.02.2010

Другой способ сделать это — добавить pre_save. подключитесь к вашей модели, а затем просто преобразуйте все поля, которые вы хотите, чтобы они были строчными, в нижний регистр, используя lower(). Это обсуждается здесь немного по-другому. Таким образом, каждый раз, когда объект сохраняется, его поле преобразуется в нижний регистр.

Пример:

@receiver(pre_save, sender=YourModel)
def convert_to_lowercase(sender, instance, *args, **kwargs):
    instance.lowercase_field = instance.lowercase_field.lower()
person dcgoss    schedule 09.07.2015

Использование свойств Python не является простым из-за способа, которым django.models.Models волшебным образом устанавливает свои атрибуты экземпляра на основе атрибутов класса. Для этого существует открытая ошибка.

В итоге я сделал именно то, что делал оригинальный постер.

Это означает, что вы должны сделать:

>>> modelinstance.field_name="TEST"
>>> modelinstance.save() # Extra step
>>> print modelinstance.field_name
'test'
person blokeley    schedule 01.03.2010

Вы пытаетесь сделать field_name свойством и делать что угодно в геттере или сеттере.

person Kugel    schedule 28.02.2010
comment
+1: это не другой тип поля, это ограничение существующего типа. - person S.Lott; 28.02.2010
comment
Дело в том, что у нас есть это ограничение для 12 полей в 11 различных моделях, так что это решение, которое мы нашли, подчиняется DRY. - person Can Burak Çilingir; 28.02.2010