Ticket #302 (closed defect: invalid)

Opened 5 years ago

Last modified 5 months ago

ValueError: You cannot add a null=False column without a default value.

Reported by: Tom <toabctl@…> Owned by: andrew
Priority: major Milestone: 0.6.3
Component: databaseapi Version: 0.6.1
Keywords: Cc:

Description

i have a model looks like this:

#vim poject/models.py

class Project(models.Model):
    """ 
    A Model which describe a Project 
    """
    auth_group = models.ForeignKey('auth.Group')
    name = models.CharField(max_length=200)
    timezone = models.CharField(max_length=200, choices = TIMEZONES, blank=True, null=True)
    longitude = models.FloatField(help_text=_('Longitude in &deg; East'), blank=True, null=True)
    latitude = models.FloatField(help_text=_('Latitude in &deg; North'), blank=True, null=True)
    altitude = models.FloatField(help_text=_('Altitude in m'), blank=True, null=True)
    comment = models.TextField(blank=True, null=True)
    class Meta:
        ordering = ['name']
    def __unicode__(self):
        return self.name

the initial migration looks like this:

# vim project/migrations/0001_initial.py

from south.db import db
from django.db import models
from ammonitor.project.models import *

class Migration:
    
    def forwards(self, orm):
        
        # Adding model 'Project'
        db.create_table('project_project', (
            ('id', orm['project.Project:id']),
            ('auth_group', orm['project.Project:auth_group']),
            ('name', orm['project.Project:name']),
            ('timezone', orm['project.Project:timezone']),
            ('longitude', orm['project.Project:longitude']),
            ('latitude', orm['project.Project:latitude']),
            ('altitude', orm['project.Project:altitude']),
            ('comment', orm['project.Project:comment']),
        ))
        db.send_create_signal('project', ['Project'])
        
    
    
    def backwards(self, orm):
        
        # Deleting model 'Project'
        db.delete_table('project_project')
        
    
    
    models = {
        'auth.group': {
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
        },
        'auth.permission': {
            'Meta': {'unique_together': "(('content_type', 'codename'),)"},
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
        },
        'contenttypes.contenttype': {
            'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
        },
        'project.project': {
            'altitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
            'auth_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
            'longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
            'timezone': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
        }
    }
    
    complete_apps = ['project']

now, i add a field to the models.py:

# vim project/models.py

...
name2 = models.CharField(max_length=200)
...

and now i create a migration with:

./manage.py startmigration project wonderful_migration --auto --settings=develsettings

the new migration looks like:

# vim project/migrations/0002_wonderful_migration.py

from south.db import db
from django.db import models
from ammonitor.project.models import *

class Migration:
    
    def forwards(self, orm):
        
        # Adding field 'Project.name2'
        db.add_column('project_project', 'name2', orm['project.project:name2'])
        
    
    
    def backwards(self, orm):
        
        # Deleting field 'Project.name2'
        db.delete_column('project_project', 'name2')
        
    
    
    models = {
        'auth.group': {
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
        },
        'auth.permission': {
            'Meta': {'unique_together': "(('content_type', 'codename'),)"},
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
        },
        'contenttypes.contenttype': {
            'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
        },
        'project.project': {
            'altitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
            'auth_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
            'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
            'longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
            'name2': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
            'timezone': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
        }
    }
    
    complete_apps = ['project']

when i want to apply the migration, i got the following error:

./manage.py migrate --settings=develsettings
/usr/lib/pymodules/python2.6/registration/models.py:4: DeprecationWarning: the sha module is deprecated; use the hashlib module instead
  import sha
Running migrations for project:
 - Migrating forwards to 0002_wonderful_migration.
 > project: 0002_wonderful_migration
Traceback (most recent call last):
  File "./manage.py", line 15, in <module>
    execute_manager(ammonitorsettings)
  File "/usr/lib/pymodules/python2.6/django/core/management/__init__.py", line 362, in execute_manager
    utility.execute()
  File "/usr/lib/pymodules/python2.6/django/core/management/__init__.py", line 303, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/lib/pymodules/python2.6/django/core/management/base.py", line 195, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/lib/pymodules/python2.6/django/core/management/base.py", line 222, in execute
    output = self.handle(*args, **options)
  File "/usr/lib/pymodules/python2.6/south/management/commands/migrate.py", line 91, in handle
    skip = skip,
  File "/usr/lib/pymodules/python2.6/south/migration.py", line 578, in migrate_app
    result = run_forwards(mapp, [mname], fake=fake, db_dry_run=db_dry_run, verbosity=verbosity)
  File "/usr/lib/pymodules/python2.6/south/migration.py", line 385, in run_forwards
    verbosity = verbosity,
  File "/usr/lib/pymodules/python2.6/south/migration.py", line 326, in run_migrations
    runfunc(orm)
  File "/home/tom/devel/ammonitor/django-ammonitor/src/ammonitor/../ammonitor/project/migrations/0002_wonderful_migration.py", line 11, in forwards
    db.add_column('project_project', 'name2', orm['project.project:name2'])
  File "/usr/lib/pymodules/python2.6/south/db/sqlite3.py", line 20, in add_column
    raise ValueError("You cannot add a null=False column without a default value.")
ValueError: You cannot add a null=False column without a default value.

i don't understand why i got this error because i have the same field (called 'name') in the database and this field makes no problems.

Change History

comment:1 Changed 5 years ago by andrew

  • Status changed from new to closed
  • Resolution set to invalid
  • Milestone set to 0.6.3

Well, you certainly do need a default value, since the database needs something to put in your new column (since you've said the field is NOT NULL, so the database can't just put nothing in there).

I'm not sure how you've managed to add the column in the database so far, but it's probably nullable, not NOT NULL. Future south versions will raise this error even earlier (at startmigration time), but with a helpful message and a prompt asking what to set the default to.

Closing as invalid, since this is an intentional error.

comment:2 Changed 5 years ago by wes@…

Hello,

I don't think this should be the expected behaviour. If you don't supply a DEFAULT clause for a NOT NULL constraint then this is perfectly valid for a column, it just means that INSERTs and UPDATEs will fail if no value is provided, this is perfectly valid in pretty much every SQL based DB under the sun, and it is the default behaviour for a Django model field without any params, e.g.:

my_field = models.IntegerField()

Would be:

`my_field` INTEGER NOT NULL;

This current incorrect behaviour is currently causing any migration under a django-cms install to fail, as it involves various IntegerField? and DecimalField? ALTERs.

comment:3 follow-up: ↓ 4 Changed 5 years ago by andrew

Yes, that's fine, but the issue here is because you're adding a column *to an existing table, with existing data*.

There are already rows in that table, and they need a value in the new column, and it can't be NULL.

See the dev docs here: http://south.aeracode.org/docs/tutorial/part2.html#defaults

comment:4 in reply to: ↑ 3 Changed 5 years ago by anonymous

Replying to andrew:

Yes, that's fine, but the issue here is because you're adding a column *to an existing table, with existing data*.

There are already rows in that table, and they need a value in the new column, and it can't be NULL.

See the dev docs here: http://south.aeracode.org/docs/tutorial/part2.html#defaults

Cheers.

The actual issue on my side was a case of PEBKAC on my part causing me to think this was the same issue. ;-)

comment:5 Changed 21 months ago by Chris Wilson <chris+south@…>

I just ran into this issue again. South allowed me to create a migration that can't be applied because it creates a database error. Shouldn't South detect this before creating the migration?

comment:6 Changed 21 months ago by andrew

South detects this particular error - adding a not-null column without a default - but we can't detect all errors (like conflicting constraints, bad data, etc), as there's no way to do this without looking at the live database, at which point you may as well try the change and if it fails roll back - what South does.

comment:7 Changed 21 months ago by Chris Wilson <chris+south@…>

I don't think it detected it in my case. I don't think there are any conflicting constraints or bad data in this case.

comment:8 Changed 5 months ago by artem.rizhov@…

Same problem for me, no data in the tables.

Note: See TracTickets for help on using tickets.