Rails Nested from plus join table - ruby-on-rails

I have the following models and running rails 3.01:
# file: app/models/product.rb
class Product < ActiveRecord::Base
has_many :categories, :through => :product_categories
has_many :product_categories, :dependent => :destroy
accept_nested_attributes_for :product_categories
end
# file: app/models/category.rb
class Category < ActiveRecord::Base
has_many :products, :through => :product_categories
has_many :product_categories, :dependent => :destroy
end
# file: app/models/product_category.rb
class ProductCategory < ActiveRecord::Base
belongs_to :product
belongs_to :category
end
ProductCategory is my join table. What do I call in my products form? Do I build on the Categories table or the ProdcutCategories table? Im just really confused on how/which models Im supposed to nest in my products from. Thanks!

The model is already configured to accept attributes for the products_categories association. In your form just reference it like this:
<%= f.fields_for :products_categories do |pc| %>
# fields go here
Remember that you will need to build new objects for the products_categories association before this form will render anything:
products.products_categories.build

Related

How to get related records across a join table in Rails?

In my Rails application I have people which can have many projects and vice versa:
# app/models/person.rb
class Person < ActiveRecord::Base
has_many :people_projects
has_many :projects, :through => :people_projects
end
# app/models/people_project.rb
class PeopleProject < ActiveRecord::Base
belongs_to :person
belongs_to :project
end
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
def self.search(person_id)
if person_id
where("person_id = ?", person_id) # not working because no person_id column in projects table
else
scoped
end
end
end
How can I filter the projects by person_id in the index view of my ProjectsController, e.g. by using a URL like this: http://localhost:3000/projects?person_id=164
I can't get my head around this. Please help! Thanks...
Your association definition is not complete for Person and Project models. You also need has_many :people_projects defined.
# app/models/person.rb
class Person < ActiveRecord::Base
has_many :people_projects # <-- This line
has_many :projects, :through => :people_projects
end
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects # <-- This line
has_many :people, :through => :people_projects
end
# app/models/people_project.rb
# This is defined correctly
class PeopleProject < ActiveRecord::Base
belongs_to :person
belongs_to :project
end
Please reference The has_many :through Association for further details.
With this definition, you will be able to get all the projects of the current user using current_user.projects, just like you've already done in your ProjectsController#index.
Update:
You could use either joins or includes in your search method and apply the where condition. Something like follows:
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
def self.search(person_id)
if person_id
includes([:people_projects, :people]).where("people.id = ?", person_id)
else
scoped
end
end
end
You will not have a person_id in the projects table because its a has_many<>has_many relationship.
Simply #person.projects will perform a join btw person_projects & projects tables and returns the appropriate projects.
*I assume,current_user returns a Person object.*
Also, complete your Model definitions. Each of them should list their relation to PeopleProjects
class Person < ActiveRecord::Base
has_many :people_projects
has_many :projects, :through => :people_projects
end
class Project < ActiveRecord::Base
has_many :people_projects
has_many :people, :through => :people_projects
end

Fetching records through multiple tables

The image shows part of my data model. I would like to fetch all items that are associated with a user (through organizations and items_group). How should I change the models and write this query in the controller? Using :through => organizations I can get all items_groups but I don't how to include one more relation to query related items.
class User < ActiveRecord::Base
has_and_belongs_to_many :organizations
has_many :items_groups, :through => :organizations
end
class Organization < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :items_groups
has_many :items, :through => :items_groups
end
class ItemsGroup < ActiveRecord::Base
has_many :items, :inverse_of => :items_group
has_and_belongs_to_many :organizations
has_many :users, :through => :organizations
end
I think you might have to do it back-to-front and find items joined back to your user.
You could define method like this in your User class:
def items
Item.joins(:items_group => {:organizations => :users}).where("users.id" => self.id).select("distinct items.*")
end
The items it returns will be read-only because of the explicit select but I think you'll want that to avoid returning individual items more than once.
If you set in your models the relationships this should work:
users.organizations.item_groups.items
Though for it to work your models should contain this:
class User < ActiveRecord::Base
has_many :organizations, :through => :organization_users
end
class Organization < ActiveRecord::Base
has_many :item_groups, :through => :items_groups_organizations
end
class ItemsGroup < ActiveRecord::Base
belongs_to :item
end
Hope it works for you!

Rails Polymorphic with Lookup Table

What's the best way to do this? I want to be able to give Bands and Artists genres through polymorphism. I can do it with habtm and has_many :through but I'm trying to figure out if it's possible through polymorphism.
GenreList would be a lookup table with a list of different genres (e.g. Punk, Pop, Metal). I've reviewed Ryan Bate's screencast for Polymorphic Assoiciations but I'm still stuck. Specifically, I'm not sure how to create the polymorphic table Genre which would be fed canned genres from the GenreList model (the lookup table).
Is the following correct?
rails generate model Genre genre_list_id:integer genreable_id:integer genreable_type:string
class Artist < ActiveRecord::Base
has_many :genres, :as => :genreable
end
class Band < ActiveRecord::Base
has_many :genres, :as => :genreable
end
class Genre < ActiveRecord::Base
belongs_to :genreable, :polymorphic => true
end
class GenreList < ActiveRecord::Base
end
I think your implementation is a little bit weird. The way I would do it is to create a model Genre (which it will hold all the available genres Punk, Rock, Metal etc). Then I would do all this that you've already done but without the GenreList model:
rails g model Genre genreable_id:integer genreable_type:string genre_name:string
class Artist < ActiveRecord::Base
has_many :genres, :as => :genreable
end
class Band < ActiveRecord::Base
has_many :genres, :as => :genreable
end
class Genre < ActiveRecord::Base
belongs_to :genreable, :polymorphic => true
end
Then I would do make some nested resources in my routes as:
resources :artists do
resources :genres
end
resources :bands do
resources :genres
end
and then edit my controller to handle this nested relation. With this approach say if i want to see all the genres of the first artist I would visit:
/artists/1/genres
same holds for bands. I hope that I understood your problem. Let me know if I helped!
Ok, after 6.5 hrs, I managed to figure this out. I used the inherited_resources gem to help with the controllers. To recap, I wanted to be able to add Genres to Artists and Bands through a polymorphic relationship, i.e. Genres would be a lookup table, and Genreings would be a polymorphic model that contains genres for Artists and Bands. Below is the code that worked for me:
# Generate some scaffolding
rails generate scaffold Artist name:string
rails generate scaffold Band name:string
rails generate scaffold Genre name:string
rails generate scaffold Genreing genre_id:integer genreable_id:integer genreable_type:string
# Models
class Artist < ActiveRecord::Base
has_many :genreings, :as => :genreable
has_many :genres, :through => :genreings
end
class Band < ActiveRecord::Base
has_many :genreings, :as => :genreable
has_many :genres, :through => :genreings
end
class Genre < ActiveRecord::Base
attr_accessible :name
has_many :genreings
end
class Genreing < ActiveRecord::Base
attr_accessible :genre, :genre_id, :genreable, :genreable_type, :genreable_id
belongs_to :genre
belongs_to :genreable, :polymorphic => true
end
# Controller
class GenreingsController < InheritedResources::Base
belongs_to :genreable, :polymorphic => true
end
# Artist Form View
= simple_form_for(#artist) do |f|
.inputs
= f.input :name
= f.association :genres, :as => :check_boxes
.actions
= f.button :submit
# Band Form View
... (Similar to Artist)
It is correct. One thing that seems to be missing is the has_many relationship from GenreList to Genre
class GenreList < ActiveRecord::Base
has_many :genres
end

Rails/ActiveRecord Sub collection

I have three models: Store, Author, Books
Store has many Authors which has many Books.
What is the cleanest way to get a collection of all the books at the store?
This works:
#store.authors.collect{|a| a.books}.flatten
Is there something in Active Record that I'm missing that makes this cleaner?
Jake
This may work...
class Store < ActiveRecord::Base
has_many :authors
# I used :uniq because a book can have more than one author, and without
# the :uniq you'd have duplicated books when using #store.books
has_many :books, :through => :authors, :uniq => true
end
class Author < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :author
end
With this code you can use #store.books...
What you want is has_many through. It works like this:
# in store.rb
has_many :authors
has_many :books, :through => :authors
# in author.rb
belongs_to :store
has_many :books
# in book.rb
belongs_to :author
Now you can say #store.books and it should just work.

ActiveRecord::HasManyThroughAssociationNotFoundError in UserController#welcome

I have a many to many relationship in rails. All database tables are named accordingly and appropriately. All model files are plural and use underscore to seperate words. All naming comventions are followed by ruby and rails standards. I'm using has many through in my models like this:
has_many :users, :through => :users_posts #Post model
has_many :posts, :through => :users_posts #User model
belongs_to :users #UsersSource model
belongs_to :posts #UsersSource model
What else could this error be from?
ActiveRecord::HasManyThroughAssociationNotFoundError in UsersController#welcome
Could not find the association :users_posts in model Post
You need to define the join model as a separate association when using has_many :through:
class Post < ActiveRecord::Base
has_many :user_posts
has_many :users, :through => :user_posts
end
class User < ActiveRecord::Base
has_many :user_posts
has_many :posts, :through => :user_posts
end
class UserPost < ActiveRecord::Base
belongs_to :user # foreign_key is user_id
belongs_to :post # foreign_key is post_id
end
This works best when you need to keep data that pertains to the join model itself, or if you want to perform validations on the join separate from the other two models.
If you just want a simple join table, it's easier to use the old HABTM syntax:
class User < ActiveRecord::Base
has_and_belongs_to_many :posts
end
class Post < ActiveRecord::Base
has_and_belongs_to_many :users
end

Resources