32

How to set default charfield in lowercase? This is my model:

class User(models.Model):
    username = models.CharField(max_length=100, unique=True)
    password = models.CharField(max_length=64)
    name = models.CharField(max_length=200)
    phone = models.CharField(max_length=20)
    email = models.CharField(max_length=200)

    def __init__(self, *args, **kwargs):
        self.username = self.username.lower()

I tried the __init__ but it doesn't work. I want to make the username in lowercase every time new record saved. Thanks.

v1k45
  • 8,070
  • 2
  • 30
  • 38
Krisnadi
  • 641
  • 1
  • 10
  • 23

5 Answers5

74

While overwriting save() method is a valid solution. I found it useful to deal with this on a Field level as opposed to the Model level by overwriting get_prep_value() method.

This way if you ever want to reuse this field in a different model, you can adopt the same consistent strategy. Also the logic is separated from the save method, which you may also want to overwrite for different purposes.

For this case you would do this:

class NameField(models.CharField):
    # Ensure valid values will always be using just lowercase
    def get_prep_value(self, value):
        value = super().get_prep_value(value)
        return value if value is None else value.lower()

class User(models.Model):
    username = models.CharField(max_length=100, unique=True)
    password = models.CharField(max_length=64)
    name = NameField(max_length=200)
    phone = models.CharField(max_length=20)
    email = models.CharField(max_length=200)
Akaisteph7
  • 5,034
  • 2
  • 20
  • 43
Danil
  • 3,348
  • 2
  • 20
  • 16
  • 1
    Small typo. SnpField should be NameField. Thank you for this method. I appreciate the reuse-ability. – Anthony Petrillo Jul 07 '19 at 22:03
  • This solution is excellent. I added unique=True to SlugField and lowered it in save(), but I got IntegrityError if the same value passed but not lowercase. your solution helped me overcome it. Thanks – shahwan42 Jun 03 '20 at 15:39
  • 1
    +1, this `get_prep_value` method also used when accessing to the DB, e.g. I created an UUID field, that deletes all '-' symbols and do uppercase, then I created an object and trying to get it from the DB: `Unit.objects.get(uuid="dc048edf-a08c41c9-b8a2-10e9-7b8752a8") -> ` – Dmytro Gierman Jul 28 '20 at 07:01
  • 1
    Out of curiosity. Is `get_prep_value` the best function to override when creating `NameField`? In another answer by wiesion, he used `to_python` instead of `get_prep_value`. Any thoughts on that? https://stackoverflow.com/a/50895814/8702081 – Valachio Sep 15 '20 at 05:25
  • 1
    @Valachio according to [this answer](https://stackoverflow.com/questions/31093407/custom-field-in-django-get-prep-value-has-no-effect), "the get_prep_value() only effects what is saved to the DB, not the internal value of the Python object" – rrh Oct 05 '20 at 16:30
  • 1
    Should it be something more like: ``` def get_prep_value(self, value): value = value.lower() return super().get_prep_value(value) ``` Also, casting it to a string will cover up any non-string values which are attempted for insertion, which seems bad to me. – Shmuelt Jan 14 '22 at 22:27
  • 1
    I would first call `super` method of [`CharField`](https://github.com/django/django/blob/78470043ae4b8def7914badcd05bb1877c8a0aa4/django/db/models/fields/__init__.py#L1197) like: `value = super().get_prep_value(value)`. And than `if value: value.strip().lower()`. – NKSM Oct 18 '22 at 21:27
33

Just do it in the save method. ie, override the save method of Model class.

def save(self, *args, **kwargs):
    self.username = self.username.lower()
    return super(User, self).save(*args, **kwargs)
Avinash Raj
  • 172,303
  • 28
  • 230
  • 274
  • 8
    It's important to point out this only helps when your model updates go through save. If you write code that does an .update() on a queryset, you don't go through save, so can still end up with uppercase letters in your database field. – aggieNick02 Sep 25 '17 at 14:22
  • @aggieNick02 for this case, a pre_save signal will help. – Avinash Raj Oct 17 '17 at 11:51
  • 9
    No, it does not. The recent edit to this answer should be reverted as it is not correct. From the django docs: "Be aware that the update() method is converted directly to an SQL statement. It is a bulk operation for direct updates. It doesn’t run any save() methods on your models, or emit the pre_save or post_save signals" (source: https://docs.djangoproject.com/en/dev/topics/db/queries/#updating-multiple-objects-at-once) – aggieNick02 Oct 17 '17 at 14:49
  • 1
    Don't override `save`, it'll break other Django methods such as `get_or_create`. – sf89 Jul 24 '19 at 09:50
  • 1
    I found this solution worked fine when you were making new records. But it doesn't work when you are updating existing records. Does anyone know why? – Ross Symonds Sep 30 '20 at 14:51
  • Apologies, I had made a dumb error with my specific code - https://stackoverflow.com/questions/64143480/i-have-over-written-the-save-method-but-the-new-code-is-only-being-applied-to/64143624#64143624 – Ross Symonds Oct 01 '20 at 04:29
  • It would be better to use `to_python` on the field – alias51 Sep 04 '21 at 09:52
  • This not the best way todo this at all.. and the below from #Danil is certainly the preferred nd arguably the more sustainable and reusable – AppHandwerker Sep 05 '21 at 13:11
0

signals also works

from django.db.models.signals import pre_save

@receiver(pre_save, sender=YourModel)
def to_lower(sender, instance=None, **kwargs):
    instance.text = instance.text.lower() if  \
        isinstance(instance.text, str) else ''
sralvins
  • 189
  • 1
  • 5
0

In my case I had a recipient_name field that I needed to make all lower case when it is stored on DB

class LowerField(models.CharField):

    def get_prep_value(self, value):
        return str(value).lower()

class Recipients(models.Model):
    
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='recipients', on_delete=models.CASCADE, )
    recipient_account_number = models.IntegerField()
    recipient_name = LowerField(max_length=30)
    recipient_bank_name = models.CharField(max_length=30)
    date = models.DateTimeField(auto_now=True, verbose_name='Transaction Date')
    
    class Meta:
        ordering = ['-date']

    def __str__(self):
        return self.recipient_name

    def get_absolute_url(self):
        return reverse('recipient-detail', kwargs={'pk': self.pk})

Similarly, you can apply to another table called Transactions in your app, like this

class Transactions(models.Model):

    transaction_type = (
        ('transfer', 'Transfer'),
        )

    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='transactions', on_delete=models.CASCADE, )
    bank_name = LowerField(max_length=50)
-2
def save(self, force_insert=False, force_update=False):
        self.YourFildName = self.YourFildName.upper()
        super(YourFomrName, self).save(force_insert, force_update)
Pyro Fury
  • 5
  • 1