I am modelling users and movies with "liked" and "recommeded".
The models are as follows:
class Movie < ActiveRecord::Base
attr_accessible :title, :imbd_id
has_and_belongs_to_many :liked_by, :class_name => "User",
:conditions => { "type" => "like" }
has_and_belongs_to_many :recommended_by, :class_name => "User",
:conditions => { "type" => "recommend" }
end
class User < ActiveRecord::Base
has_and_belongs_to_many :movies_liked, :class_name => "Movie",
:conditions => { "type" => "like" }
has_and_belongs_to_many :movies_recommended, :class_name => "Movie",
:conditions => { "type" => "recommend" }
end
with one table to map the relationship:
create_table "movies_users", :id => false, :force => true do |t|
t.integer "user_id"
t.integer "movie_id"
t.string "type" # this can be "like" or "recommend"
t.string "status_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Now I am getting an error when I try to do
u = User.first
m = Movie.first
u.movie_liked << m
with the following error
ruby-1.9.2-p290 :003 > u.movies_tweeted << m
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: movies.type: SELECT * FROM "movies" INNER JOIN "movies_users" ON "movies".id = "movies_users".movie_id WHERE ("movies_users".user_id = 1 AND ("movies"."type" = 'like'))
Any idea I can structure it nicely so I can just use the << operator and it will automatically assign the correct type?
Thanks!
You can create Recommendation and Like models and use them to join the Users with Movies. Or just a single model called don't know how(RecommendLike?), if you really wish to save on one table. And then:
User
has_many :recommendations
has_many :movies_recommended, :class_name => "Movie", :through => :recommendations
has_many :likes
has_many :movies_liked, :class_name => "Movie", :through => :likes
Related
I have the followings tables
break_points
id: integer
break_point_name: string
schedules
id: integer
departure: int -> break_point_id
arrival:int -> break_point_id
departure_date:date
arrival_date: date
Both departure and arrival are breakpoints .
Then Will I to create an association * to * instead?
1.9.3-p547 :004 > s=Schedule.find(1)
Schedule Load (0.1ms) SELECT "schedules".* FROM "schedules" WHERE "schedules"."id" = ? LIMIT 1 [["id", 1]]
=> #<Schedule id: 1, departure_id: 1, departure_date: "2015-01-05", departure_time: 28800, arrival_id: 11, arrival_date: "2015-01-06", arrival_departure: 3600, bus_company_id: 1, created_at: "2014-11-08 22:55:00", updated_at: "2014-11-08 22:55:00">
1.9.3-p547 :005 > s.departure_break_points
BreakPoint Load (0.3ms) SELECT "break_points".* FROM "break_points" WHERE "break_points"."departure_id" = 1
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: break_points.departure_id: SELECT "break_points".* FROM "break_points" WHERE "break_points"."departure_id" = 1
from /home/fernando/.rvm/gems/ruby-1.9.3-p547#ticket_master/gems/sqlite3-1.3.10/lib/sqlite3/database.rb:91:in `initialize'
from /home/fernando/.rvm/gems/ruby-1.9.3-p547#ticket_master/gems/sqlite3-1.3.10/lib/sqlite3/database.rb:91:in `new'
from /home/fernando/.rvm/gems/ruby-1.9.3-p547#ticket_master/gems/sqlite3-1.3.10/lib/sqlite3/database.rb:91:in `prepare'
these are my migrations that generate the tables of the database
class CreateSchedules < ActiveRecord::Migration
def change
create_table :schedules do |t|
t.integer :departure_id
t.date :departure_date
t.time :departure_time
t.integer :arrival_id
t.date :arrival_date
t.time :arrival_departure
t.integer :bus_company_id
t.timestamps
end
end
end
class CreateBreakPoints < ActiveRecord::Migration
def change
create_table :break_points do |t|
t.string :city
t.integer :province_id
t.timestamps
end
end
end
and these are my models
These are my models
class BreakPoint < ActiveRecord::Base
attr_accessible :break_point_name, :city
belongs_to :province
end
class Schedule < ActiveRecord::Base
has_many :departure_break_points,class_name: "BreakPoint", :foreign_key => 'departure_id', :dependent => :destroy
has_many :arrival_break_points, class_name: "BreakPoint", :foreign_key => 'arrival_id', :dependent => :destroy
end
Add two relations in your model , but specify the foreign key
has_many :departure_break_points,class_name: "BreakPoint", :foreign_key => 'departure' :dependent => :destroy
has_many :arrival_break_points, class_name: "BreakPoint", :foreign_key => 'arrival', :dependent => :destroy
Case:
I have a model (" ride_request ")
in ride_request there are two fields ( pickup_location , dropoff_location )
both fields (pickup and dropoff) are instances of one model (location)
Here is my migrate
create_table :ride_requests do |t|
t.integer :pickup_location
t.integer :dropoff_location
Question:
How do I create a relation to location in the fields pickup/drop when normaly you would use location_id?
you can use :class_name and :foreign_key to indicate the differences
Class RideRequest
belongs_to :pickup, :class_name => "location", :foreign_key => "pickup_location"
belongs_to :dropoff, :class_name => "location", :foreign_key => "dropoff_location"
Given User and Book models, I've created a join model, ViewedBook, that contains additional attributes. Below is the essence of what I've come up with:
create_table "users"
t.string "username"
end
create_table "books"
t.string "title"
t.integer "user_id"
t.date "authored_date"
end
create_table "books_viewings"
t.integer "book_id"
t.integer "user_id"
t.boolean "finished"
t.date "last_viewed_date"
end
class User
belongs_to :book_viewing
has_many :authored_books,
:class_name => "Book",
:source => :book
has_many :book_viewings
has_many :viewed_books :through => :book_viewings
:order => "book_viewings.last_viewed_date DESC"
has_many :finished_books :through => :book_viewings
:conditions => "book_viewings.finished = TRUE",
:order => "book_viewings.last_viewed_date DESC"
end
class Book
belongs_to :user
has_one :author, :class_name => "User"
end
class BookViewing
belongs_to :book
belongs_to :user
end
I think this works for most of the queries I need to create. However, I want each of the Book objects returned by user.viewed_books to include the finished attribute as well. Further, I will have additional queries like Book.best_sellers that I would also like to scope to a given user so that they also include the finished attribute.
From my limited exposure to ActiveRecord, it appears there's probably an elegant way to manage this and generate efficient queries, but I have yet to find an example that clarifies this scenario.
EDIT: to clarify, the other queries I'm looking for will not themselves be restricted to books that have been finished, but I need to have the finished attribute appended to each book if it exists for the given book and scoped user in book_viewings.
See my answer here https://stackoverflow.com/a/8874831/365865
Pretty much, no there isn't a specific way to do this, but you can pass a select option to your has_many association. In your case I'd do this:
has_many :books, :through => :book_viewings, :select => 'books.*, book_viewings.finished as finished'
I'm trying to figure out a complex relation between a Model.
I have a model called "Concept", which has two inheriting types called "Skill" and "Occupation". Basicly this means that each concept represents a category, but a concept can also be a skill or an occupation when going deep enough into the hierychal tree.
I'm solving this hierachy by using STI. So my schema for the Concepts table looks like this:
class CreateConcepts < ActiveRecord::Migration
def self.up
create_table :concepts do |t|
t.string :uri, :null => false, :length => 255
t.string :type, :null => true, :length => 255
t.integer :isco_code, :null => true
t.timestamps
end
end
def self.down
drop_table :concepts
end
end
The type column determins whether the Concept is a real "Concept" or a "Skill"/"Occupation".
The problem now however the following relations:
EDIT:
A Concept can belong to a single parent Concept
An Occupation can belong to a single parent Concept
A Skill can belong to multiple parent Concepts
A skill has no children
An occupation has no children
so basicly you'd have something like this:
> concept1
> concept2 concept3
> concept4 concept5 concept6 concept7 skill1
> occup1 skill2 occup2 skill5
> occup7 skill2 occup3 skill4
> occup4 skill1 occup8
I hope the picture is a bit clear what I'm trying to explain.
Currently I have created the following migration to try to solve the parent-child relation but I'm not sure how to map this with the associations...
class CreateConceptLinks < ActiveRecord::Migration
def self.up
create_table :concept_links do |t|
t.integer :parent_id, :null => false
t.integer :child_id, :null => false
t.timestamps
end
end
def self.down
drop_table :concept_links
end
end
What I want to end up with is the following posssibilities:
concepta.parents => a Concept object
conceptb.children => an array of Conept objects
Occupation.parents => a Concept object
Occupation.children => []
Skill.parents => an array of Concept objects
Skill.children => []
Hope this is even possible...
You can model hierarchical relations in rails. You've got most of the way there with your migrations. Adding the relations below should allow you to do the method calls you'd like:
def Concept < ActiveRecord::Base
has_many :child_links, :class_name => 'ConceptLink', :foreign_key => 'parent_id'
has_many :children, :through => :child_links
has_many :parent_links, :class_name => 'ConceptLink', :foreign_key => 'child_id'
has_many :parents, :through => :parent_links
end
def ConceptLink < ActiveRecord::Base
belongs_to :child, :class_name => "Concept"
belongs_to :parent, :class_name => "Concept"
end
I'd also take a look at this blog posting which does a very good of explaining parent-child mappings in rails.
At the moment, I am doing my complex queries by hand so to speak. But I keep encountering problems. For instance.
query = "SELECT histories.candidate_id
FROM histories
WHERE histories.institution_id IN (?)
GROUP BY histories.candidate_id
HAVING COUNT(*)= ?"
cand = [Code.find_by_sql([query,
params['searches'][key],
params['searches'][key].size])]
class History < ActiveRecord::Base
belongs_to :candidate
end
create_table "histories", :force => true do |t|
t.string "job_title"
t.date "start_date"
t.date "finish_date"
t.string "basic_salary"
t.string "bonus"
t.integer "institution_id"
t.integer "candidate_id"
t.datetime "created_at"
t.datetime "updated_at"
end
class Candidate < ActiveRecord::Base
# has_and_belongs_to_many :codes
has_many :codes, :through => :CandidatesCodes
has_many :histories
has_many :contacts
has_many :compensations
end
This returns a list of candidate ids.. but want I want it to return is a list of candidates how would I do that the rails way?
This is brians suggestion, and I have tried this but I get uninitialized constant History::Candidates
cand = History.find(:all,
:joins => :candidates,
:select => "candidates.*",
:conditions => [ "institution_id IN (?)", params['searches'][key] ],
:group => [ "candidate_id HAVING count(*) = ?", params['searches'][key].size ]
)
Try this:
Candidate.all(
:joins => :histories,
:conditions => {:histories=> {:institution_id => params[:searches][key]}},
:group => "candidates.id",
:having => "count(candidates.id) >= %i" % params[:searches][key].size
)
This should generate the following SQL:
SELECT candidates.*
FROM candidates AS candidates
JOIN histories AS histories ON histories.candidate_id = candidates.id
WHERE histories.institution_id IN (1,2,3)
GROUP BY candidates.id
HAVING COUNT(candidates.id) >= 3
Give this a try (building on Dinesh's approach above:
candidates = History.find(:all,
:joins => :candidates,
:select => "candidates.*"
:conditions => ["institution_id IN (?)", params['searches'][key]],
:group => ["candidate_id HAVING count(*) = ?", params['searches'][key].size]
)
Warning - untested.
Try this out.
Assuming that you have mode of History
History.find(:all ,
:conditions=>[" institution_id IN (? ) ",params['searches'][key] ],
:group => ["candidate_id HAVING count(*) = ? " ,params['searches'][key].size ]
)