I'm starting out with the whole (wonderful) idea of database associations in Rails but I'm having problems because I'm working with an existing database that does not conform to Rails standards and cannot figure out how to name the associations. There are a couple of similar posts, but I can't wrap my head around the naming for my particular situation which is as follows:
table book with book.formatId looks up values in book_format.id
So foreign key book.formatId
My models are named: Book and BookFormat (I read that you use camelCase when your tables are separated by underscore).
Under the Book model I have this:
has_one :bookFormat, :foreign_key => 'book_format.id' # not sure if this format table.field is correct or I have to use something else here. Also not sure about the bookFormat, should it be BookFormat or bookformat?
The BookFormat model has this:
belongs_to :book
But when I try to do
book = Book.first
book.bookFormat.empty?
I get an error of method not found for bookFormat. So obviously something's wrong, but I can't figure out where.
A second part of the question is the use of many to many relationships. Example:
Tables
book, book_subjects, book_subjects2title
book_subjects.id => book_subjects2title.pId
book.id => book_subjects2title.bookId
I'm reading the Beginning Rails 3 book from Apress (which is a great book) but it's not very clear on all this or I'm just not getting it.
Thanks.
Since the book stores the formatId on it, you should use belongs_to, and change the foreign key as such:
belongs_to :book_format, :class_name => 'BookFormat', :foreign_key => 'formatId'
For the table name, i did some quick searching, and found the following method: set_table_name
So you should be able to add it at the top of your model, like so:
class Book < ActiveRecord::Base
set_table_name 'book'
# the rest of book model code
end
class BookFormat < ActiveRecord::Base
set_table_name 'book_format'
# rest of book_format model code
end
Normally rails uses plural table names, so hence why you need to specify it there.
agmcleod put me on the right track, so here's the full answer in the hopes that this helps other people with similar problems:
I created the model with a different name for easier reading. So model Books will have:
class Books < ActiveRecord::Base
set_table_name 'bookpedia'
belongs_to :format, :foreign_key => 'formatId' # we need to specify the foreign key because it is not the rails default naming
has_many :author2title, :foreign_key => 'bookId'
has_many :author, :through => :author2title
end
model Format:
class Format < ActiveRecord::Base
set_table_name 'book_format'
has_many :books
end
Then an instance of the class will have the format method:
#book = Books.first
#book.format # Hardcover
For many to many relationships I'll paste the syntax here as well since that took me a while to figure out:
class Author < ActiveRecord::Base
set_table_name 'book_author'
has_many :author2title, :foreign_key => 'pId' # junction table
has_many :books, :through => :author2title # establishing the relationship through the junction table
end
This is the actual junction table:
class Author2title < ActiveRecord::Base
set_table_name 'book_author2title'
belongs_to :author, :foreign_key => 'pId' # foreign key needs to be here as well
belongs_to :books, :foreign_key => 'bookId'
end
The book model above has the necessary entries for this many to many relationship already in it.
If anyone needs clarification on this I'd be happy to oblige since I struggled for more than a day to figure this out so would be glad to be of help.
Cheers.
Related
I have two associated table in my application and I cannot figure it how to join them together in rails, below is my Model:
class Lead < ApplicationRecord
has_many :employee_leads
has_many :employees, :through => :employee_leads
end
class EmployeeLead < ApplicationRecord
belongs_to :employee
belongs_to :lead
end
class Employee < ApplicationRecord
has_many :employee_leads
has_many :leads, :through => employee_leads
has_many :emp_stores
has_many :stores, :through => emp_stores
end
class EmpStore < ApplicationRecord
belongs_to :store
belongs_to :employee
end
class Store < ApplicationRecord
has_many :emp_stores
has_many :employees, :through => :emp_stores
end
My application required me to find out which store each lead belongs to. I know how to join the lead to the employee, which is:
Lead.joins(employee_leads: :employee)
and I also know how to join the employee to the store
Employee.joins(emp_stores: :store)
Those are working for me without issue. When I try to get the lead join to store, I used:
Lead.joins(employee_leads: :employee { emp_stores: :store })
This gave me a syntax error, I refer to the link of Active Record regarding Joining Nested Associations (Multiple Level) and I still can't figure it out. I'm very new to this, please someone take some time to explain and help me out. Thank you.
Try this:
Lead.joins(employee_leeds: [employee: [amp_stores: :store] ])
I believe these variants should also work:
Lead.joins(employee_leeds: [{employee: [{amp_stores: :store}] }])
Lead.joins(employee_leeds: {employee: {amp_stores: :store} })
Although, depending on how you're using this you might want to consider using includes rather than joins. Joins are lazy-loaded whereas includes are eager-loaded.
As an explanation to why your syntax was wrong ... :something is a symbol. And in the old (pre ruby 1.9) world, hash key/value pairs were written like so: :key => :value. However, from Ruby 1.9 the newer syntax of key: :value was provided.
In your attempt to get this working you had a key/value pair followed (without a separator) by another hash. In order to fix it, you need to provide just one value to the first key:
# Old syntax
:employee_leads => [ :employee => [ :emp_stores => :store ] ]
Which when you consider the new syntax for hashes, hopefully makes this more logical to understand:
# New syntax
employee_leads: [ employee: [ emp_stores: :store ] ]
My question is more related to naming conventions than programmation I guess.
Let's assume an application where users can create new articles (so they are the owner of these articles) and where you can add article "editors", who can only update the article content.
class User
include Mongoid::Document
has_many :articles # as owner of the articles
has_and_belongs_to_many :articles # as editor of the articles
end
class Article
include Mongoid::Document
belongs_to :user
has_and_belongs_to_many :editors, :class_name => 'User'
end
What I want to know is how I should call the articles association in my User model. I mean, an article has an author and editors, which seems strong naming conventions to me, but a user has articles he created and articles he is the editor. How would you call/name/declare the last 2 associations?
I would call them as :edited_articles, and :authored_articles or :owned_articles, or something similarly straightforward names. Just dont forget to add the :class_name and :foreign_key or :through qualifiers to them.
Update:
For has_and_belongs_to_many relation you need a connection table, which is by default, is named of the two joined table. E.g. articles_users in your case. In this table you will propably have two ids, user_id and article_id. This way rails connects your models automatically.
has_and_belongs_to_many :editors, :class_name => 'User', :foreign_id => 'user_id'
Of course if you call it editor_id in the join table then use that. And the opposite, on the user side should work too.
I am a rails newbie and I am trying to create a database schema that looks like the following:
There are many matches. Each match has 2 teams.
A team has many matches.
The team model and match model are joined together through a competition table.
I have that competition model with a match_id and a team1_id and a team2_id.
But I don't know how to make this work or if it's even the best way to go about it. I don't know how to make certain teams team1 and others team2.... two foreign keys? Is that possible?
The match table also needs to hold additional data like team1_points and team2_points, winner and loser, etc.
You can have as many foreign keys as you want in a table. I wrote an application that involved scheduling teams playing in games.
The way that I handled this in the Game class with the following:
class Game < ActiveRecord::Base
belongs_to :home_team, :class_name => 'Team', :foreign_key => 'team1_id'
belongs_to :visitor_team, :class_name => 'Team', :foreign_key => 'team2_id'
You can add appropriate fields for team1_points, team2_points, etc. You'll need to set up your Team model with something like:
class Team < ActiveRecord::Base
has_many :home_games, :class_name => 'Game', :foreign_key => 'team1_id'
has_many :visitor_games, :class_name => 'Game', :foreign_key => 'team2_id'
def games
home_games + visitor_games
end
#important other logic missing
end
Note that some of my naming conventions were the result of having to work with a legacy database.
I faced a similar problem, and extending the previous answer, what I did was:
class Game < ActiveRecord::Base
def self.played_by(team)
where('team1_id = ? OR team2_id = ?', team.id, team.id)
end
end
class Team < ActiveRecord::Base
def games
#games ||= Game.played_by(self)
end
end
This way, Team#games returns an ActiveRecord::Relation instead of an Array, so you can keep chaining other scopes.
I have two tables, AppTemplate and AppTemplateMeta
AppTemplate table has column id, MetaID, name etc.
I have associated these two model like this
class AppTemplate < ActiveRecord::Base
set_table_name 'AppTemplate'
belongs_to :app_template_meta, :class_name => "AppTemplateMeta", :foreign_key => 'MetaID'
end
If we fetch data using AppTemplate.all, I want associated meta details also. But currently it's not returning associated meta details. It just returns AppTemplate details.
any guys can help me for this
If I understood correctly, you want something like the following:
# models
class AppTemplate < ActiveRecord::Base
# table names usually looks like this: app_template.
# the same for columns names. so you should have 'meta_id' as foreign key
set_table_name 'AppTemplate'
belongs_to :app_template_meta, :class_name => "AppTemplateMeta",
:foreign_key => 'MetaID'
end
class AppTemplateMeta < ActiveRecord::Base
has_one :app_template # or has_many
end
# controller
# get all app templates and load the associated app_template_meta for each one
#app_templates = AppTemplate.all(:include => :app_template_meta)
# get associated app_template_meta for the first app_template
#app_templates.first.app_template_meta
I have the following setup:
class Publication < ActiveRecord::Base
has_and_belongs_to_many :authors, :class_name=>'Person', :join_table => 'authors_publications'
has_and_belongs_to_many :editors, :class_name=>'Person', :join_table => 'editors_publications'
end
class Person < ActiveRecord::Base
has_and_belongs_to_many :publications
end
With this setup I can do stuff like Publication.first.authors. But if I want to list all publications in which a person is involved Person.first.publications, an error about a missing join table people_publications it thrown. How could I fix that?
Should I maybe switch to separate models for authors and editors? It would however introduce some redundancy to the database, since a person can be an author of one publication and an editor of another.
The other end of your associations should probably be called something like authored_publications and edited_publications with an extra read-only publications accessor that returns the union of the two.
Otherwise, you'll run in to sticky situations if you try to do stuff like
person.publications << Publication.new
because you'll never know whether the person was an author or an editor. Not that this couldn't be solved differently by slightly changing your object model.
There's also hacks you can do in ActiveRecord to change the SQL queries or change the behavior of the association, but maybe just keep it simple?
I believe you should have another association on person model
class Person < ActiveRecord::Base
# I'm assuming you're using this names for your foreign keys
has_and_belongs_to_many :author_publications, :foreign_key => :author_id
has_and_belongs_to_many :editor_publications, :foreign_key => :editor_id
end