In our Rails app, there are 3 models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
And here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
We have created the calendar#create
action as follows:
def create
@calendar = current_user.calendars.build(calendar_params)
if @calendar.save
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
And the corresponding _calendar_form.html.erb
partial:
<%= form_for(@calendar) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder: "Your new calendar name" %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
This "works" since, when we create a new calendar through the form, it does show up in Rails console, when we type Calendar.all
.
However, it seems like no new @administration
is being created and the Administration table is not updated, as nothing returns when we type Administration.all
in the console.
We thought the Administration table, which is the join table between the User table and the Calendar table, and which respectively contains user_id
and calendar_id
columns, would be updated automatically when creating a new calendar.
How can we achieve this? Do we need to create a specific administration#create
action?
UPDATE: based on the comments and the answers, we implemented the following CalendarsController
:
class CalendarsController < ApplicationController
def create
@calendar = current_user.calendars.build(calendar_params)
if @calendar.save
current_user.administrations << @calendar
@calendar.administration.role = 'creator'
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
...
However, this returns the following error:
ActiveRecord::AssociationTypeMismatch in CalendarsController#create
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
end
app/controllers/calendars_controller.rb:6:in `create'
Are we missing something?
Yes, you absolutely do! You're not getting a new
Administration
object because you're not asking the system for one.If you really, truly needed to create a new
Administration
object every time aCalendar
was created, you could do something like this:Obviously this isn't a best practice, as it injects dependencies in your code. Now, no matter what, your
Calendar
class is aware that anAdministration
class not only exists, but uses itsid
column to initialize. This was just an example to answer your question.Another example would be to include an
after_initialize
hook in yourCalendar
class like so (please note theautosave: true
which is useful for ensuring the automatic saving of child objects):What's more, you could also include the above logic in an
after_create
hook. The only difference is thatafter_create
occurs after an object's creation andafter_initialize
occurs after an object is either instantiated or loaded from the database.If you use current_user.calendars.build(calendar_params), you will only get new calendar, no administration.
If you use current_user.calendars.create(calendar_params), you will get both administration and calendar saved into database.
If you want to check whether calendar is successfully saved first, I use this approach:
UPDATED:
There is an error to associate calendar to user. This should be the correct one: