Create multiple submodels into the main model's form - ruby-on-rails

Here is the situation :
class Activity < ApplicationRecord
has_many :activity_amenities
end
class Amenity < ApplicationRecord
has_many :activity_amenities
has_many :activities, through: :activity_amenities
end
class ActivityAmenity < ApplicationRecord
belongs_to :activity
belongs_to :amenity
end
So basically i have Activities that have Amenities. The reason for me to use a has_many :through association is because I want to create basic Amenities and each Activity' amenities will have a description proper to the Activity.
So, I want to create new Amenities straight in the creation/edition of an Activity. This image should illustrate it very well :
So when I click on Add amenity button, it should add new select/input group.
Then when I click on create, it should create all the ActivityAmenities and associate them with the Activity model.
Any idea How this should be done (in the Controller and in the View) ? Couldn't find anything really...
ps : note that I'm using simple-form gem for the forms

For all your has_many associations, include the inverse_of option. Then when adding the ActivityAmenities by ID, pass them all in at once as an array. e.g when you make activity_params, pass in activity_amenity_ids[]. Rails will use the automatically generated #activity_amenity_ids= method to handle the creation for you.
Model:
class Activity < ApplicationRecord
has_many :activity_amenities, inverse_of: activity
end
class Amenity < ApplicationRecord
has_many :activity_amenities, inverse_of: amenity
has_many :activities, through: :activity_amenities
end
class ActivityAmenity < ApplicationRecord
belongs_to :activity
belongs_to :amenity
end
Controller:
params.require(:activity).permit(activity_amenity_ids[], :whatever_else)
View: Whatever the name of your input for your form
name="activity[activity_amenity_ids][]"
If you want an explanation of why you you pass in activity_amenity_ids, is basically tl;dr it creates the object if it doesn't exist, and does nothing if it does.
E.g try
Activity.create(name: "Running or whatever", activity_amenity_ids: [1, 2, 3])
And see the result. Notice how a new activity is created along with its associated activity amenities in a single transaction?

Related

Rails, validate has_many through association with attribute value of children

I have three models:
class User < ApplicationRecord
has_many :game_accounts
has_many :favorite_game_accounts, through: :game_account_favorites, source: :game_account
end
class GameAccount < ApplicationRecord
belongs_to :user
has_many :favorite_users, through: :game_account_favorites, source: :user
end
class GameAccountFavorite < ApplicationRecord
belongs_to :user
belongs_to :game_account
validates_presence_of :user, :game_account
validates_uniqueness_of :user, scope: :game_account_id
end
This means that User can have his own GameAccounts and other Users can add them to favorites.
I have added scope in order to prevent one user to have multiple favorites of the same GameAccount. However, there is one problem. User can add to favorite his own GameAccount. How to prevent user adding his own GameAccount to favorites?
I'm not sure that there is any built-in Rails validation for you case, so I'd suggest writing your own custom one.
In your particular case, you can verify on GameAccountFavorite instance, that game_account.user_id isn't equal to user.id.
There's plenty of ways of performing custom validation in Rails

Is: grandparent.parents.children association chaining not correct in Rails 4?

I'm having trouble figuring out the proper way of retrieving all children of multiple parents through association chaining.
To simplify I have three models:
class Customer < ActiveRecord::Base
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
After creating a few objects I tired to use the example from rails guides (association basics: 4.3.3.4 includes):
Customer.first.invoices.line_items
It returns:
undefined method `line_items' for #<Customer::ActiveRecord_Associations_CollectionProxy
Is grandparent.parents.children not usable?
EDIT
I'm not searching for the grandparent.parents.first.children, but all children of all parents in the collection, rails guides state:
If you frequently retrieve line items directly from customers (#customer.orders.line_items),
As a valid operation, I would like to know if that is a mistake.
FINAL As stated in the comments of the selected answer: in ActiveRecord: scopes are chainable but associations are not.
The customer.invoices.line_items cannot work the way you want to, since the has_many always is linked to a single record. but you can achieve what you want (if I understand correctly) using has_many through
as follows:
class Customer < ActiveRecord::Base
has_many :invoices
has_many :line_items, through: :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
and now you can write:
customer.line_items
and it will return all line_items which are connected to a customer's invoices.
Customer.first.invoices.first.line_items
Or if you want all of the data together, you can do something like:
results = Customer.first.invoices.includes(:line_items)
Then you may access data with no DB call, by looping results. For first data ex: results.first.line_items
Hope it helps!
Customer.first.invoices will return an collection (like an array) of invoices. The line_items method isn't defined for a collection, but its defined for an invoice. Try Customer.first.invoices.first.line_items
EDIT - If you always want the orders to include the line items, you can just do:
class Customer < ActiveRecord::Base
has_many :orders, -> { includes :line_items }
end
class Order < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
end

add a many_to_many participation model to two existing table with one_to_many association

ok, this is my situation, i have two existing models, User and Event, which have a one_to_many association:
class User < ActiveRecord::Base
has_many :events
...
end
and in my event class i have:
class Event < ActiveRecord::Base
belongs_to :user
...
end
i did this in order to associate a user_id with an event as its creator.
so now i want to add a third table called event_participation, what is the best way to do that?
and how can i add a fourth model called event_invitation?
Use source
class User < ActiveRecord::Base
has_many :events
has_many :my_events, through: :event_participation, source: event
end

has_and_belongs_to_many STI, restrict to be unique by types

I'm using STI models with has_and_belongs_to_many relations.
So I have User with many Templates of different types, like MainTemplate < Template; NotSoMainTemplate < Template; etc.
Is there a way to limit each user to have only one MainTemplate and only one NotSoMainTemplate, and so on for all types?
Let me reiterate the problem statement as I have understood it.
You want User to have at most one kind of each template. i.e.
1 MainTemplate, 1 NotSoMainTemplate, etc.
You don't need a direct relation with Template (parent table)
Each template may be used by more than one user
Based on the above assumption, I would suggest you to do the following:
Remove existing habtm association between User and Template
Add migrations to add main_template_id, not_so_main_template_id to User
Add the following associations:
class MainTemplate < Template
has_many :users
end
class NotSoMainTemplate < Templete
has_many :users
end
class class User < ActiveRecord::Base
belongs_to :main_template
belongs_to :not_so_main_template
end
Since you are already using STI, you can try has_one.
class Template < ActiveRecord::Base
has_many :users, through: template_choice
...
end
class MainTemplate < Template
...
end
class TemplateChoice < ActiveRecord::Base
belongs_to :template_choice
belongs_to :user
end
class User < ActiveRecord::Base
has_one :main_template, through: :template_choice
has_one :not_so_main_template, through: :template_choice
...
end

Recording the date an object is added to a has_many collection

Users on my site each have one list, which consists of a different type of users. I'm using a has_many through relationship to do this as follows:
List.rb:
class List < ActiveRecord::Base
belongs_to :company
has_many :list_applicants
has_many :applicants, through: :list_applicants
end
Applicant.rb:
class Applicant < ActiveRecord::Base
has_many :list_applicants
has_many :lists, through: :list_applicants
end
ListApplicant.rb
class ListApplicant < ActiveRecord::Base
attr_accessible :applicant_id, :list_id
belongs_to :applicant
belongs_to :list
end
Company.rb:
class Company < ActiveRecord::Base
has_one :list
end
When a user adds another user to their list, I'd like to record the date the user is added so they can sort their list of users by date added. What is the best way to do this?
You can use the created_at field of the ListApplicant model if it has one. If not, you may add manually a similar field.
UPDATE:
You can access the field by specifying both applicant and list like this:
#applicant.list_applicants.where(list_id: #list.id).first.created_at

Resources