luckyframework / luckyframework/avram
Enhancement: auto generated migrations
- Dominant language
- Crystal
- Stars
- 183
- Forks
- 67
- PR merge metrics
- No merged PRs in 30d
Description
This would take the place of manually filling in migrations. It would also make it unnecessary to have a single `change` method that handles migrate and rollback like Rails and other have.
## The problem
Right now you need to remember the Migration API, what the options are, and you then have to write the rollback by hand. This can be tedious, error-prone, and it does take some time.
## Discarded solutions
Some have tried fully automatic migrations but sometimes they get things wrong, it is hard to customize, and there is no visibility. So we shouldn't do that.
We could also do what Rails does and have a `change` method that automatically generates `rollback` behavior, but this is way to magic. Some methods don't work when rolling back and you won't know until you try rolling back, which may be too late :)
## Proposed solution
Generate migrations automatically based on the columns and tables defined in models. The file is fully editable so a person can modify it and see exactly what will happen.
### For example, let's say you create a model:
```crystal
class User < BaseModel
table do
column name : String
column age : Int32
end
end
```
You'd then run `lucky gen.migration.auto` which would create:
```crystal
class CreateUsers::VXXXXXX < Avram::Migrator::Migrator::V1
def migrate
create table_for(User) do
add name : String
add age : Int32
end
end
def rollback
drop table_for(User)
end
end
```
### Now what if we decide to make age nilable?
```crystal
class User < BaseModel
table do
column name : String
column age : Int32? # Made this nilable
end
end
```
You'd then run `lucky gen.migration.auto` which would create:
```crystal
class MakeUserAgeOptional::VXXXXXX < Avram::Migrator::Migrator::V1
def migrate
alter table_for(User) do
make_optional :age
end
end
def rollback
alter table_for(User) do
make_required :age
end
end
end
```
### Now let's add a `BlogPost` that can be written by a `User`
```crystal
class BlogPost < BaseModel
table do
column body : String
add_belongs_to author : User
end
end
```
You'd then run `lucky gen.migration.auto` which would ask you:
```
BlogPost belongs_to an author
When the author of a BlogPost is deleted what should we do?
1. Cascade - delete the associated posts when the author is deleted
2. Restrict - don't let the author be deleted if there are associated posts
3. Don’t do anything
```
Assuming we chose option 1 it would create:
```crystal
class CreateBlogPosts::VXXXXXX < Avram::Migrator::Migrator::V1
def migrate
create table_for(User) do
add body : String
add_belongs_to author : User, on_delete: :cascade
end
end
def rollback
drop table_for(User)
end
end
```
## What should we ask the user about?
Sometimes we won't know what to do, in those cases we should ask or leave a comment in the migration that we guessed something.
For example, we can infer the name of a migration when creating a single table like `CreateUsers`. But for altering tables or adding multiple tables it may not be a good idea to guess, so we should ask `What should we call this migration? (example: AddAgeToUsers)`
Or when adding a belongs to, we should ask what to do when cascading. This is nice because we can have the options explain what will happen:
```
When a User is deleted what should we do?
1. Cascade - when a User is deleted, delete the associated posts
2. Restrict - don't let the User be deleted if there are associated posts
3. Nullify - set the post's foreign key (posts.author_id) to 'null' <-- only show this if belongs_to is nilable
4. Don’t do anything
```
## What if the auto migrator is wrong?
We may not be able to figure everything out.
That is ok since the code we output is fully editable! So if we make a mistake the user can correct it in the generated file.
Contributor guide
Research direction
Start with the `lucky gen.migration.auto` entry point and the model table definitions described in the issue. Map how model changes, relationships, and migration naming would be detected and represented. Done means generating editable migrations with explicit rollback behavior and prompts for ambiguous choices.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- crystal, postgresql
- Domain
- backend, databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 18/100