Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#31245 closed Bug (invalid)

CharField doesn't accept bytestrings in default parameter in Django 2.2.

Reported by: Tony S Yu Owned by: nobody
Component: Database layer (models, ORM) Version: 2.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

System details

  • Migrations generated in Django 1.11.28 + Python 2.7
  • Migrations run on Django 2.2.10 + Python 3.6
  • Database backend: django.db.backends.mysql

Summary

We recently upgraded an app from Django 1.11 to 2.2. When a developer on the team attempted to re-install the app from scratch some time later, they ran into the following exception:

django.db.utils.OperationalError: (1067, "Invalid default value for '<charfield>'")

where the <charfield> had choices containing bytestring db values and unicode string labels (i.e. choices=[(<bytes>, <str>),...])

I reproduced this in isolation using a modified version of the Students example from the docs:

class Student(models.Model):

    name = models.CharField(max_length=300)

    FRESHMAN = 'FR'
    SOPHOMORE = 'SO'
    JUNIOR = 'JR'
    SENIOR = 'SR'
    GRADUATE = 'GR'
    YEAR_IN_SCHOOL_CHOICES = [
        (FRESHMAN, u'Freshman'),
        (SOPHOMORE, u'Sophomore'),
        (JUNIOR, u'Junior'),
        (SENIOR, u'Senior'),
        (GRADUATE, u'Graduate'),
    ]
    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=FRESHMAN,
    )

The main modification is that the choices labels are explicitly unicode, but the db values are unprefixed strings (bytestrings in Python 2, unicode in Python 3). (Note that the actual code generated choices from an Enum, which when combined with Python 2 using unicode literals causes bytestring db values and unicode labels used above.)

In practice, I wasn't able to reproduce the error without splitting up the year_in_school migration from the model creation. (The name field was added to the model to create an initial migration.)

Running the year_in_school migration using Django 1.11.28 (on Python 2.7) generates the following migration:

# -*- coding: utf-8 -*-
# Generated by Django 1.11.28 on 2020-02-07 17:07
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('djangodebug', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='student',
            name='year_in_school',
            field=models.CharField(choices=[(b'FR', 'Freshman'), (b'SO', 'Sophomore'), (b'JR', 'Junior'), (b'SR', 'Senior'), (b'GR', 'Graduate')], default=b'FR', max_length=2),
        ),
    ]

Running that migration in Django 1.11 (on either Python 2 or 3) works fine. Running the migration in Django 2.2 throws the following error:

django.db.utils.OperationalError: (1067, "Invalid default value for 'year_in_school'")

Additional Notes

Workarounds

  • Downgrade to Django 1.11 and run migrations, then revert to Django 2.2
  • Manually modify migration files to use unicode strings (i.e. remove b prefix from strings)

Change History (2)

comment:1 by Mariusz Felisiak, 5 years ago

Component: UncategorizedDatabase layer (models, ORM)
Resolution: invalid
Status: newclosed
Summary: "Invalid default value for '<charfield>'" for CharField choices migration generated by Django 1.11 and run on Django 2.2CharField doesn't accept bytestrings in default parameter in Django 2.2.

Support for bytestring was removed in some places in Django 2.0 (see release notes).

comment:2 by Tony S Yu, 5 years ago

That makes sense. I was working under the (baseless) assumption that migrations would remain backwards compatible (which I recognize would be a huge burden). After your comment, I did find other notes in the docs about the need for editing old migrations files to add on_delete, so my assumption was obviously wrong. Thanks

Note: See TracTickets for help on using tickets.
Back to Top