We implemented a LowerCaseCharField. We would be happy to hear better implementation suggestions.
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)
In fact we love to have is:
> modelinstance.field_name="TEST"
> print modelinstance.field_name
'test'
current implementation only converts to lowercase when it is saved.
We'll then convert one string to all lowercase and the other string to all uppercase. This will give you a general idea of how template filters work. So the general format to convert a string to all lowercase is, {{ value|lower }}.
null = True. Means there is no constraint of database for the field to be filled, so you can have an object with null value for the filled that has this option. blank = True. Means there is no constraint of validation in django forms.
null. Field. null. If True , Django will store empty values as NULL in the database. Default is False .
You may wish to override to_python
, which will allow you to compare non-lowercase strings when doing database lookups. The actual method is get_prep_value
, but as that calls to_python
for CharField
, it's more convenient to override that:
def to_python(self, value):
value = super(LowerCaseCharField, self).to_python(value)
if isinstance(value, basestring):
return value.lower()
return value
Now you can do queries like:
MyModel.objects.filter(lccf="MiXeD")
Edit:
Rereading your question, it looks like you want the lowering to take effect immediately. To do this, you'll need to create a descriptor (a new-style python object with __get__
and __set__
methods, see the python docs and the django code for related models) and override contribute_to_class
in the field to set the model's field to your descriptor.
Here is a full example off the top of my head, which should be reusable for all fields that want to modify the value on setting.
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))
Another way to do it would be to add a pre_save hook to your model, and then just convert all the fields you want to be lowercase to lowercase there using lower(). This is discussed here in a slightly different fashion. This way, any time the object is saved its field is converted to lower case.
Example:
@receiver(pre_save, sender=YourModel)
def convert_to_lowercase(sender, instance, *args, **kwargs):
instance.lowercase_field = instance.lowercase_field.lower()
Using Python properties is not straightforward because of the way django.models.Models magically set their instance attributes based on class attributes. There is an open bug out for this.
I ended up doing exactly what the original poster did.
It means you have to do:
>>> modelinstance.field_name="TEST"
>>> modelinstance.save() # Extra step
>>> print modelinstance.field_name
'test'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With