Django 小写字符字段

14 投票
4 回答
7986 浏览
提问于 2025-04-15 19:49

我们实现了一个小写字符字段。我们很想听听更好的实现建议。

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'

目前的实现只在保存时转换为小写。

4 个回答

1

在使用Python的属性时,事情并没有那么简单,因为django.models.Models会根据类的属性神奇地设置实例的属性。对此,有一个未解决的bug

我最后做的和最开始提问的人完全一样。

这意味着你需要这样做:

>>> modelinstance.field_name="TEST"
>>> modelinstance.save() # Extra step
>>> print modelinstance.field_name
'test'
2

另一种方法是给你的模型添加一个 pre_save 钩子,然后在这里把你想要转换成小写的所有字段都用 lower() 方法转换成小写。这个方法在 这里 也有讨论,不过方式稍微不同。这样,每次保存对象的时候,它的字段都会被转换成小写。

示例:

@receiver(pre_save, sender=YourModel)
def convert_to_lowercase(sender, instance, *args, **kwargs):
    instance.lowercase_field = instance.lowercase_field.lower()
19

你可能想要重写 to_python 这个方法,这样在进行数据库查询时就可以比较不区分大小写的字符串。实际上,真正的方法是 get_prep_value,但因为它会调用 to_python 来处理 CharField,所以重写 to_python 会更方便:

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))

撰写回答