Issue: migrations - field is not null but has no default
Author: binrush
State: closed
Created: 2017-05-22 11:00:34

I have a migration with the following code:

```
def migrate(migrator, database, fake=False, **kwargs):
    """Write your migrations here."""
    MyModel = migrator.orm['my_table']
    type_new = pw.CharField(null=True, constraints=[pw.Check(
        "type_new in ('type1', 'type2', 'type3')")])
    migrator.add_fields(MyModel, type_new=type_new)
    migrator.sql('UPDATE my_table SET type_new=type')
    migrator.remove_fields(MyModel, 'type')
    migrator.rename_field(MyModel, 'type_new', 'type')
    migrator.add_not_null(MyModel, 'type')
```

And when appllying it I get following error:
```
ValueError: type_new is not null but has no default
````

I'm explicitly passing `null=True`, but peewee claims this field to be non-null, how it is possible? Is it peewee bug or am I missing something?

That's odd. Thanks for the report, I'll check it out.

Thank you @coleifer . Just ping me if you need any additional information.

What database engine are you using, out of curiosity?

We are using Sqlite

Are you using an extension? I'm not familiar with some of the APIs being used:

```python
    MyModel = migrator.orm['my_table']  # XXX
    type_new = pw.CharField(null=True, constraints=[pw.Check(
        "type_new in ('type1', 'type2', 'type3')")])
    migrator.add_fields(MyModel, type_new=type_new)
    migrator.sql('UPDATE my_table SET type_new=type')  # XXX
```

Seems like we are using peewee-migrate https://github.com/klen/peewee_migrate
Is it better to file issue to that project?

I think something weird must be going on with that extension.

Running this:

```python
from peewee import *
from playhouse.migrate import *

db = SqliteDatabase('test.db')

class TestModel(Model):
    data = TextField()
    class Meta:
        database = db

TestModel.drop_table(True)
TestModel.create_table()

m = SqliteMigrator(db)
migrate(
    m.add_column(TestModel._meta.db_table, 'new_column', TextField(null=True)),
)
```

Results in:

```
∫ sqlite3 test.db
SQLite version 3.19.0 2017-04-10 12:31:15
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE IF NOT EXISTS "testmodel" ("id" INTEGER NOT NULL PRIMARY KEY, "data" TEXT NOT NULL, "new_column" TEXT);
```

Thank you. Opened new issue https://github.com/klen/peewee_migrate/issues/43

Cool, feel free to kick it back over here if need be.

@coleifer, I have a migration:

```from myapp import db
from myapp.models import Message, User
from playhouse.migrate import *
from playhouse.postgres_ext import *

migrator = PostgresqlMigrator(db.database)

def up():
	migrate(
		migrator.drop_column('message', 'user_id'),
		migrator.add_column('message', 'user_id', ForeignKeyField(User, backref='messages')),
	)
```

I get following error:
`ValueError: user_id is not null but has no default`

Where I was mistaken?

Why are you dropping and then re-adding it?

@coleifer 
I wanted make changes:
`user = ForeignKeyField(Cert, backref='messages') -> user = ForeignKeyField(User, backref='messages')`

Is there existing data in that table?

Yes

How are you migrating a foreign-key that points to "Cert" so that it points instead to "User"? Won't the existing values all be incorrect, since originally they reference ``Cert.id`` but will then reference ``User.id``?

You are right. I deleted all rows.

https://github.com/coleifer/peewee/blob/7e61d86bf6c3f256d09b2a3e1897693dfd68b48d/playhouse/migrate.py#L264-L265

My PK dont't have default value and not NULL

```
if not field.null and field.default is None:
    if not is_foreign_key:
        raise ValueError('%s is not null but has no default' % column_name)
```

Well, if you deleted all rows, can you not just drop and re-create the table entirely using the normal `Model.create_table()` API?

Another option is to add the field and make it nullable, then do a "drop_not_null()" afterwards.

```python
# Just drop and re-create:
def up():
        Message.drop_table()
        Message.create_table()

# OR:
def up():
	migrate(
		migrator.drop_column('message', 'user_id'),
		migrator.add_column('message', 'user_id', ForeignKeyField(User, null=True)),
                migrator.drop_not_null('message', 'user_id'),
	)
```