pink0.pallino
pink0.pallino

Reputation: 65

Django migration with "--fake-initial" is not working if AddField referes to "same" column

I'm playing with django (I'm a quite new beginner) and while surfing the web I read it could be possible to keep our internal camelCase naming conventions inside the mySQL database and also for the models' name inside models.py

Well, after some days I can conclude it's better to leave things as they were designed and use the standard output generated by inspectdb without any change to its code (I removed the .lower() functions :-) )

Anyhow, just out of curiosity, I would appreciate if somebody can explain me why what follows is not working. Briefly, it seems to me that the code responsible for the migration is not checking correctly(?) if a column name is already inside the database, or at least it does its comparison in case-sensitive manner. Is that by design?

I'm using this guide from the internet https://datascience.blog.wzb.eu/2017/03/21/using-django-with-an-existinglegacy-database/

The mysql is running with the option " --lower-case-table-names=0" and the collation is case-insensitive.

Inside the models.py I have this

class City(models.Model):
    id = models.AutoField(db_column='ID', primary_key=True)
    name = models.CharField(db_column='Name', max_length=35)
    countrycode = models.ForeignKey(Country, db_column='CountryCode')
    district = models.CharField(db_column='District', max_length=20)
    population = models.IntegerField(db_column='Population', default=0)

    def __str__(self):
        return self.name

    class Meta:
        managed = True
        db_table = 'city'
        verbose_name_plural = 'Cities'
        ordering = ('name', )

if I change the reference 'db_column' to db_column='countryCode' (note the lower "c" ) and I run

./manage.py migrate --database world_data --fake-initial worlddata

I get errors saying 'django.db.utils.OperationalError: (1050, "Table 'city' already exists")'

and the problem arises only using the --fake-initial option

After analyzing "...django/db/migrations/executor.py" I found those line that check if a column is already inside the existing

column_names = [
    column.name for column in
    self.connection.introspection.get_table_description(self.co$
]
if field.column not in column_names:
    return False, project_state

here, for what I understand, there is no case sensitive comparison so the "countryCode" column is not found inside "column_names":

-> if field.column not in column_names:
(Pdb) field.column
'countryCode'
(Pdb) column_names
['ID', 'Name', 'CountryCode', 'District', 'Population']

Upvotes: 6

Views: 521

Answers (1)

Anonymous
Anonymous

Reputation: 12090

First of all I'd like to congratulate you on being so through with your first question! Many older contributors don't go into as much depth as you.


So first let's get things straight. You mention that --lower-case-table-names=0 is enabled but collation is case insensitive. From the docs I see that the option forces case sensitivity for table names. I might just be reading it wrong but it looks like you're saying everything should be case insensitive. Also collation usually refers to the data itself, not column names in case you're unaware.

That said as far as I know, all databases treat column names case-insensitively (I just tested in SQLite) so you might have just uncovered a bug in Django! I looked through the history of the file, and in the 5-odd years that code has existed I guess no one ran into this issue. It's understandable since usually people either a) just let django create the db from scratch and thus everything is in sync, or b) they use inspectdb to generate the code with the right case for the columns.

It looks like you were just playing around so I don't think you are looking for a specific solution. Perhaps the next step is to file a bug ;)? From what I see there would be no downside to adding case-insensitive comparison there, but the guys working on Django 24/7 may have a different opinion.

Upvotes: 2

Related Questions