Reputation: 6897
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?
Upvotes: 2
Views: 2161
Reputation: 477
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:
def create
@calendar = Calendar.build(calendar_params)
if @calendar.save
current_user.administrations << @calendar
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
UPDATED:
There is an error to associate calendar to user. This should be the correct one:
def create
@calendar = current_user.calendars.build(calendar_params)
if @calendar.save
current_user.calendars << @calendar
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
Upvotes: 3
Reputation: 2257
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 a Calendar
was created, you could do something like this:
class Calendar < ActiveRecord::Base
attr_reader :administration
def initialize(params)
@administration = Administration.new(params[:id])
end
end
class Administration < ActiveRecord::Base
def initialize(id_param_from_calendar)
calendar_id = id_param_from_calendar
end
end
Obviously this isn't a best practice, as it injects dependencies in your code. Now, no matter what, your Calendar
class is aware that an Administration
class not only exists, but uses its id
column to initialize. This was just an example to answer your question.
Another example would be to include an after_initialize
hook in your Calendar
class like so (please note the autosave: true
which is useful for ensuring the automatic saving of child objects):
class Calendar < ActiveRecord::Base
has_many :administrations, autosave: true, dependent: :destroy
after_initialize :init_admins
def init_admins
# you only wish to add admins on the newly instantiated Calendar instances
if new_record?
3.times { Administration.new self }
end
end
end
What's more, you could also include the above logic in an after_create
hook. The only difference is that after_create
occurs after an object's creation and after_initialize
occurs after an object is either instantiated or loaded from the database.
Upvotes: 2