rails how to save a model with has_many through association - ruby-on-rails

I am having models like
// Contains the details of Parties (Users)
class Party < ActiveRecord::Base
has_many :party_races
has_many :races, :through=>:party_races
end
// Contains the party_id and race_id mappings
class PartyRace < ActiveRecord::Base
belongs_to :party
belongs_to :race
end
// Contains list of races like Asian,American,etc..
class Race < ActiveRecord::Base
has_many :party_races
has_many :parties, :through => :party_races
end
Now, lets say I'm creating an instance of Party
party_instance = Party.new
How am I supposed to add multiple Races to party_instance and save to database ?

You could use nested attributes to make one form that allows children. There are many examples on this site. Read up on the following first:
Accepts nested attributes
Fields for
A railscast about your use case

You also can create new PartyRace for each Races that you can add:
def addRace( party_instance, new_race )
party_race = PartyRace.new( party: party_instance, race: new_race )
party_race.save
end

Related

Rails 4 Accept nested attributes with has_one association

I have a question about Rails Nested Attributes.
I'm using Rails 4 and have this model:
model Location
has_one parking_photo
has_many cod_photos
accepts_nested_attributes_for :parking_photo
accepts_nested_attributes_for :cod_photos
end
When I use for example:
Location.find(100).update(cod_photo_ids: [1,2,3]) it works.
But Location.find(100).update(parking_photo_id: 1) doesn't works.
I don't know what difference between nested attributes has_one and has_many.
Or do we have any solution for my case, when I already have child object and want to link the parent to the child and don't want to use child update.
Thank you.
The problem has nothing to do with nested attributes. In fact you're not even using nested attributes at all in these examples.
In this example:
Location.find(100).update(cod_photo_ids: [1,2,3])
This will work even if you comment out accepts_nested_attributes_for :cod_photos as the cod_photo_ids= setter is created by has_many :cod_photos.
In the other example you're using has_one where you should be using belongs_to or are just generally confused about how you should be modeling the association. has_one places the foreign key on the parking_photos table.
If you want to place the parking_photo_id on the locations table you would use belongs_to:
class Location < ActiveRecord::Base
belongs_to :parking_photo
# ...
end
class ParkingPhoto < ActiveRecord::Base
has_one :location # references locations.parking_photo_id
end
Of course you also need a migration to actually add the locations.parking_photo_id column. I would really suggest you forget about nested attributes for the moment and just figure out the basics of how assocations work in Rails.
If you really want to have the inverse relationship and put location_id on parking_photos you would set it up like so:
class Location < ActiveRecord::Base
has_one :parking_photo
# ...
end
class ParkingPhoto < ActiveRecord::Base
belongs_to :location
validates_uniqueness_of :location_id
end
And you could reassign a photo by:
Location.find(100).parking_photo.update(location_id: 1)

Create Rails scope comparing fields on two tables

I have a number of associated tables in an application
class Listing < ActiveRecord::Base
belongs_to :house
belongs_to :multiple_listing_service
end
class House < ActiveRecord::Base
has_one :zip_code
has_one :primary_mls, through: :zip_code
end
I wanted to create a scope that produces all the Listings that are related to the Primary MLS for the associated House. Put another way, the scope should produce all the Listings where the multiple_listing_service_id = primary_mls.id for the associated house.
I've tried dozens of nested joins scopes, and none seem to work. At best they just return all the Listings, and normally they fail out.
Any ideas?
If I understand correctly, I'm not sure a pure scope would be the way to go. Assuming you have:
class MultipleListingService < ActiveRecord::Base
has_many :listings
has_many :zip_codes
end
I would go for something like:
class House < ActiveRecord::Base
...
def associated_listings
primary_mls.listings
end
end
Update 1
If your goal is to just get the primary listing then I would add an is_primary field to the Listing. This would be the most efficient. The alternative is a 3 table join which can work but is hard to optimize well:
class Listing < ActiveRecord::Base
...
scope :primary, -> { joins(:houses => [:zip_codes])
.where('zip_codes.multiple_listing_service_id = listings.multiple_listing_service_id') }

Rails controllers architecture with many to many relationships and modal dialogs

I am developing a portal via RubyOnRails where pupils, teachers and parents can participate in different contests with their art works.
There are 3 entities: Contests, Categories (competitor categories / age groups) and Nominations (kinds of activity). Contest can have many Categories. Each ContestCategory can have many Nominations. Each Category can belong to several Contests. Every Nomination can belong to many ContestCategories. So I presume there is many-to-many relationship between Contests and Categories and many-to-many relationship between ContestCategories and Nominations. I've created following models: Contest (contest.rb), Category (category.rb), Nomination (nomination.rb), ContestCategory (contest_category.rb) and ContestCategoryNomination (contest_category_nomination.rb).
My models:
class Category < ActiveRecord::Base
has_many :contest_categories
has_many :contests, through: :contest_categories
end
class Contest < ActiveRecord::Base
has_many :contest_categories
has_many :categories, through: :contest_categories
has_many :nominations, through: :contest_categories
has_one :provision
end
class ContestCategory < ActiveRecord::Base
belongs_to :contest
belongs_to :category
has_many :contest_category_nominations
has_many :nominations, through: :contest_category_nominations
end
class ContestCategoryNomination < ActiveRecord::Base
belongs_to :contest_category
belongs_to :nomination
end
class Nomination < ActiveRecord::Base
has_many :contest_category_nominations
has_many :contest_categories, through: :contest_category_nominations
has_many :contests, through: :contest_categories
end
I want to create an ajax-based modal window during creation of new Contest to link it with Category and select multiple Nominations that belong to this Category.
What controllers should I create to satisfy has_many relationships
between my models?
What are the naming conventions (singular and plural) in rails to satisfy my
relationships? For example, ContestsCategoriesController or
ContestCategoryNominationsController or may be
ContestCategoryNominationsController?
What action method should I create in this controllers to invoke to
render this modal window? Should it be new_category action in
CategoriesController or new action in ContestsCategoriesController
or new action in ContestsCategoriesNominationsController?
That completely depends on how you want to manipulate your objects. If you only want to modify all attributes and relations through Contests, you'd just need the ContestsController. Using a nifty little method like accept_nested_attributes_for http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html you can provide all relevant values, even to the associated records.
Referring on your requirement you noted in the comments, I'd create a form in the modal dialogue, which represent a form object, like:
class Nomination::Category
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
# validates :your_attributes
def initialize(category, attributes = {})
#category = category
#attributes = attributes
end
def persisted?
false
end
def save
return false unless valid?
if create_objects
# after saving logic
else
false
end
end
# more business logic according to your requirements
private
def create_objects
ActiveRecord::Base.transaction do
# #category.contests = ... saving logic
#category.save!
end
rescue
false
end
end
and a representing controller:
class NominationCategoriesController < ApplicationController
def new
category = Category.find params[:category_id]
#nomination_category = Nomination::Category.new category
end
def create
category = Category.find params[:category_id]
#nomination_category = Nomination::Category.new category, params[:nomination_category]
#nomination_category.save
end
end
Please beware this is just an example/idea. The concrete implementation depends on your specific business logic requirements.
It worth reading more about the form objects approach.

How to create an association that sets join table attributes automatically?

I am totally confused about how I should go about "the rails way" of effectively using my associations.
Here is an example model configuration from a Rails 4 app:
class Film < ActiveRecord::Base
# A movie, documentary, animated short, etc
has_many :roleships
has_many :participants, :through => :roleships
has_many :roles, :through => :roleships
# has_many :writers........ ?
end
class Participant < ActiveRecord::Base
# A human involved in making a movie
has_many :roleships
end
class Role < ActiveRecord::Base
# A person's role in a film. i.e. "Writer", "Actor", "Extra" etc
has_many :roleships
end
class Roleship < ActiveRecord::Base
# The join for connecting different people
# to the different roles they have had in
# different films
belongs_to :participant
belongs_to :film
belongs_to :role
end
Given the above model configuration, the code I wish I had would allow me to add writers directly to a film and in the end have the join setup correctly.
So for example, I'd love to be able to do something like this:
## The Code I WISH I Had
Film.create!(name: "Some film", writers: [Participant.first])
I'm not sure if I'm going about thinking about this totally wrong but it seems impossible. What is the right way to accomplish this? Nested resources? A custom setter + scope? Something else? Virtual attributes? thank you!
I created a sample app based on your question.
https://github.com/szines/hodor_filmdb
I think useful to setup in Participant and in Role model a through association as well, but without this will work. It depends how would you like to use later this database. Without through this query wouldn't work: Participant.find(1).films
class Participant < ActiveRecord::Base
has_many :roleships
has_many :films, through: :roleships
end
class Role < ActiveRecord::Base
has_many :roleships
has_many :films, through: :roleships
end
Don't forget to give permit for extra fields (strong_parameters) in your films_controller.rb
def film_params
params.require(:film).permit(:title, :participant_ids, :role_ids)
end
What is strange, that if you create a new film with a participant and a role, two records will be created in the join table.
Update:
You can create a kind of virtual attribute in your model. For example:
def writers=(participant)
#writer_role = Role.find(1)
self.roles << #writer_role
self.participants << participant
end
and you can use: Film.create(title: 'The Movie', writers: [Participant.first])
If you had a normal has_and_belongs_to_many relationship i.e. beween a film and a participant, then you can create a film together with your examples.
As your joining model is more complex, you have to build the roleships separately:
writer= Roleship.create(
participant: Participant.find_by_name('Spielberg'),
role: Role.find_by_name('Director')
)
main_actor= Roleship.create(
participant: Participant.find_by_name('Willis'),
role: Role.find_by_name('Actor')
)
Film.create!(name: "Some film", roleships: [writer, main_actor])
for that, all attributes you use to build roleships and films must be mass assignable, so in a Rails 3.2 you would have to write:
class Roleship < ActiveRecord::Base
attr_accessible :participant, :role
...
end
class Film < ActiveRecord::Base
attr_accessible :name, :roleships
...
end
If you want to user roleship_ids, you have to write
class Film < ActiveRecord::Base
attr_accessible :name, :roleship_ids
...
end
Addendum:
Of cause you could write a setter method
class Film ...
def writers=(part_ids)
writer_role=Role.find_by_name('Writer')
# skiped code to delete existing writers
part_ids.each do |part_id|
self.roleships << Roleship.new(role: writer_role, participant_id: part_id)
end
end
end
but that makes your code depending on the data in your DB (contents of table roles) which is a bad idea.

How to check if object is referenced before deleting? (Many To Many Relationship)

I am quite new in the ruby on rails world. I have two classes, A and B and defining in the following way:
class AClass < ActiveRecord::Base
has_many :a_b_class
end
class ABClass < ActiveRecord::Base
validates_presence_of :attr1, :attr2
belongs_to :a_class
belongs_to :b_class
attr_accessible :attr1,:attr2,:a_class, b_class
end
class BClass < ActiveRecord::Base
validates_presence_of :attr4, :attr5
has_many :a_b_class
attr_accessible :attr4,:attr5
end
I am using activeadmin to administrate the databases data, etc.
The problem is that activeadmin allows me to delete a BClass object that is referenced by a AClass (through ABClass relationship) object so when I enter to the http://example.com/a_class the view failed because the view try to access to attr1 of a nil element. How can I add validation to the model in order to avoid delete a referenced object?
EDITED: I corrected the relationship, is a many to many
I think your association is wrong somewhere first rectifie that e.g article has many comments,so comment has article_id,and comment has validates_presence_of :article_id not article and if you delete article respective comment should be deleted there for you need 'dependent=>:destroy"
class Article <AR
has_many :comments,:dependent=>:destroy
end
class Comment <AR
belongs_to :article
attr_accessible :article_id,....
end
I am not sure this will solve the problem - but can you try adding a belongs_to attribute to BClass to mark that it belongs to AClass?
belongs_to :a_class
Alsoo, you can't do this:
validates_presence_of :b_class_id
since there could be many ids..

Resources