Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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']
like image 657
pink0.pallino Avatar asked Sep 30 '18 18:09

pink0.pallino


People also ask

What does fake migration do in Django?

--fake-initialAllows Django to skip an app's initial migration if all database tables with the names of all models created by all CreateModel operations in that migration already exist. This option is intended for use when first running migrations against a database that preexisted the use of migrations.

How do you add a new field to a model with new Django migrations?

To answer your question, with the new migration introduced in Django 1.7, in order to add a new field to a model you can simply add that field to your model and initialize migrations with ./manage.py makemigrations and then run ./manage.py migrate and the new field will be added to your DB.


1 Answers

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.

like image 58
Anonymous Avatar answered Oct 28 '22 12:10

Anonymous