Render unto Caesar…
Let the database do it’s job. For a while, Rails development focused on pushing logic into models that would be better suited being put in as a table constraint. Articles like this one are great at outlining the proper approach. The problem with this is that it’s not built into the Rails tools, leaving you writing ugly code in your migrations. Not very ruby-esque.
Databases have a built in way for managing data integrity between 2 tables. The Foreign Key constraint. This ensures the value of a foreign key exists in the referenced table.
Since Rails was created, it relied on SQL, and foreign keys, but never had a way of confirming the
id being referenced existed, shy of adding a model validation.
Exiting news! Rails 4.2 shipped with foreign key support. You can create migrations that delegate the responsibility directly to the database if your database supports it.
We’re going to model an app that lets you teach and sign up for courses.
1 2 3 User -> Can be both a teacher and a student Course -> Belongs to a user by a teacher_id Enrollment -> Links a user by a student_id, and a course
1 2 3 4 5 rails new learn_stuff -d postgresql && cd learn_stuff rails g model user name rails g model course topic teacher:belongs_to rails g model enrollment student:belongs_to course:belongs_to rake db:create
This sets us up, but when we run
rake db:migrate fireworks start.
1 PG::UndefinedTable: ERROR: relation "teachers" does not exist
This makes sense. We don’t have a
teachers table. We need to pass the course table some more options.
Here’s our migration as it stands.
1 2 3 4 5 6 7 8 9 10 class CreateCourses < ActiveRecord::Migration def change create_table :courses do |t| t.string :topic t.belongs_to :teacher, index: true, foreign_key: true t.timestamps null: false end end end
The Rails source shows that we can’t switch the name of table. What would be great is to be able to say something like: “OK RAILS, I’M GOING TO GIVE YOU A COLUMN NAME BUT IT’S NOT THE TABLE NAME FOR THE CONSTRAINT. I’LL GIVE YOU MORE INFO LATER. KTHXBAI”
We have to first create the column, THEN add the constraint.
1 2 3 4 5 6 7 8 9 10 11 class CreateCourses < ActiveRecord::Migration def change create_table :courses do |t| t.string :topic t.belongs_to :teacher, index: true t.timestamps null: false end add_foreign_key :courses, :users, column: :teacher_id end end
The rest of the source code, including the associations for all of this stuff can be found here
I wish this was a bit smarter, off to work on a pull request.comments powered by Disqus