I have a fairly simple query to return the first record in a many-to-many relation or create one if it doesn't exist.
UserCategorization.where(category_id: 3, user_id: 5).first_or_create
My model looks like:
class UserCategorization < ActiveRecord::Base
belongs_to :user
belongs_to :category
self.primary_key = [:user_id, :category_id]
end
However it generates an invalid column name in the SQL:
SQLite3::SQLException: no such column: user_categorizations.[:user_id, :category_id]:
SELECT "user_categorizations".* FROM "user_categorizations" WHERE
"user_categorizations"."category_id" = 3 AND "user_categorizations"."user_id" = 5
ORDER BY "user_categorizations"."[:user_id, :category_id]" ASC LIMIT 1
If I remove self.primary_key = [:user_id, :category_id] from the model, it can retrieve the record correctly but cannot save because it doesn't know what to use in the WHERE clause:
SQLite3::SQLException: no such column: user_categorizations.:
UPDATE "user_categorizations" SET "score" = ?
WHERE "user_categorizations"."" IS NULL
Has anyone seen this before?
I think one of these two suggestions will work:
First, try adding the following migration:
add_index :user_categorizations, [:user_id, :category_id]
Make sure to keep self.primary_key = [:user_id, :category_id] in your UserCategorization model.
If that doesn't work, destroy the UserCategorization table and run this migration:
def change
create_table :user_categorizations do |t|
t.references :user
t.references :category
t.timestamps
end
end
references are new to Rails 4. They add a foreign key and index to the specified columns.
Good Luck!
So it looks like Rails 4 ActiveRecord doesn't do composite keys very well so many-to-many models create the issues above. I fixed it by using this extension to ActiveRecord: http://compositekeys.rubyforge.org/
Related
I has two models
class Fellow < ApplicationRecord
has_and_belongs_to_many :skills
end
class Skill < ApplicationRecord
has_and_belongs_to_many :fellows
end
One fellow can have some skills, and one skill can be learned by some fellows. So I have third table
class CreateFellowsSkills < ActiveRecord::Migration[5.0]
def change
create_table :fellows_skills, id:false do |t|
t.belongs_to :skill, index: true
t.belongs_to :fellow, index: true
end
end
end
I want to use method: fellow.skills
That invoke such SQL:
SELECT "skills".* FROM "skills" INNER JOIN "fellows_skills" ON "skills"."id" = "fellows_skills"."skill_id" WHERE "fellows_skills"."fellow_id" = $1
The problem: I want to use field skill_id in table skills instead of id, so the query should be such:
SELECT "skills".* FROM "skills" INNER JOIN "fellows_skills" ON "skills"."skill_id" = "fellows_skills"."skill_id" WHERE "fellows_skills"."fellow_id" = $1
I tried to use different options in method has_and_belongs_to_many but the query is still incorrect.
http://cobwwweb.com/why-i-dont-use-has-and-belongs-to-many-in-rails
Instead of habtm, use
class Fellow < ApplicationRecord
has_many :fellows_skills
has_many :skills, through :fellows_skills
end
class Skill < ApplicationRecord
has_many :fellows_skills
has_many :fellows, through: :fellows_skills
end
class FellowsSkill < ApplicationRecord
belongs_to :fellow
belongs_to :skill
end
Furthermore I would suggest naming the third model FellowSkill (dropping the plural on fellow).
Finally I correct my db scheme and call primary key ID, so I don't need apply any changes now.
Hi I am ROR developer and using rails 3.2.13 with Postgres database.
I have two models:
Question
attr_accessible :category, :is_active, :question_text, :question_type_id, :survey_id,
:user_id
has_many :abusive_questions
And
AbusiveQuestion
attr_accessible :question_id, :user_id, :ipaddress, :posted_by
belongs_to :question
From this I want to get the AbusiveQuestion which count is greater than a particular value (ex: 5).
I did the following from my rails command
AbusiveQuestion.count(:group=>"abusive_questions.question_id")
and got
=> {1=>1, 5=>3, 3=>1}
For this result, the key is the question_id and value is the count but, I want to get the question which value is greater then a particular dynamic value (for ex:2).
Please help me.
I think you'll want to join Question on AbusiveQuestion then use a select and having to get what you want.
Something like:
AbusiveQuestion.select('abusive_questions.*, count(question.id) as question_count').
joins(:questions).
group('abusive_questions.question_id').
having('count(abusive_questions.question_id) > 5')
You can use having method.
http://guides.rubyonrails.org/active_record_querying.html#having
x = YOUR_DYNAMIC_COUNT
AbusiveQuestion.group(:question_id).having("count_all > ?", x).count
following a rails 3 tutorial, I have a scope defined to limit the orders column and a helper method to sum the results
Scope
class LineItem < ActiveRecord::Base
attr_accessible :cart_id, :product_id, :cart, :product, :quantity
belongs_to :order
belongs_to :product
belongs_to :cart
scope :order, -> {where("order != nil")}
end
Helper
module StoreHelper
def total_product_sold (product)
product.line_items.total_product.sum("quantity")
end
problem is when i call total_product_sold from my view it sums all data in orders column as opposed to only the ones where the order number is !=nil.
I also tried defining a class method as opposed to scope
def self.total_product
where(order !=nil)
end
but this gives me the same exact result. what am i doing wrong? how do i get it to add only the items whose order column are not nil?
You can't find records that are not NULL using != nil. You need to use the SQL syntax IS NOT NULL:
scope :order, -> { where("line_items.order_id IS NOT NULL") }
or:
def self.total_product
where("line_items.order_id IS NOT NULL")
end
Alternatively, you can join the :order association which effectively does the filtering for you. However, there is a performance penalty for this:
def total_product_sold (product)
product.line_items.joins(:order).sum("quantity")
end
I am a rails newbie and am trying to perform a search on a table with rails, and i'm just using my sql knowledge to do this. But this just doesn't seems like rails or ruby even...
Is there any better way to do what i'm doing below? (basically, only pass date arguments to sql if they are filled)
def search(begin_date=nil, end_date=nil)
subject = " and created_at "
if !(begin_date.nil? || end_date.nil?)
where_part = subject + "BETWEEN :begin_date AND :end_date"
else if (begin_date.nil? && end_date.nil?)
where_part = ""
else if(begin_date.nil?)
where_part = subject + " <= :end_date"
else if (end_date.nil?)
where_part = subject + " >= :begin_date"
end
end
end
end
User.joins(places: {containers: {label: :user}}).where("users.id= :user_id "+where_part, user_id: self.id, begin_date:begin_date, end_date:end_date).group(...).select(...)
end
EDIT
user.rb
has_many :containers
has_many :user_places
has_many :places, through: :user_places
has_many :labels
place.rb
has_many :containers
has_many :user_places
has_many :users, through: :user_places
container.rb
belongs_to :label
belongs_to :place
belongs_to :user
label.rb
belongs_to :user
has_many :containers
Basically, i want to get a count of the number of containers within a given user's labels or with a direct relationship, per location, and want to be able to filter it by begin and end dates.
Either of this dates may be nil, and so i would need to address this in my "query".
My question is : How can i do this the rails way? I took a look at http://guides.rubyonrails.org/active_record_querying.html and perhaps i could use the except command here somewhere...but this relationship model just seems a bit complex to do this with ActiveRecord...how may I?, i really think i should use ActiveRecord, but how?
Thank you
You can apply multiple where calls to a query so you can build your base query:
query = User.joins(...)
.group(...)
.select(...)
.where('users.id = :user_id', :user_id => self.id)
and then add another where call depending on your date interval:
if(begin_date && end_date)
query = query.where(:created_at => begin_date .. end_date)
# or where('created_at between :begin_date and :end_date', :begin_date => begin_date, :end_date => end_date)
elsif(begin_date)
query = query.where('created_at >= :begin_date', :begin_date => begin_date)
elsif(end_date)
query = query.where('created_at <= :end_date', :end_date => end_date)
end
Each where call adds another piece to your overall WHERE clause using AND so something like:
q = M.where(a).where(b).where(c)
is the same as saying WHERE a AND b AND c.
I cant think of a great reason why you would actually want to generate SQL in your code. Active record seems like a much more efficient solution for your needs, unless there is a reason why you cant use that.
Link explaining how to join tables with active record
I have a one-tomany relationship with 2 tables as follows:
Models:
class MediaType < ActiveRecord::Base
belongs_to :media
end
class Media < ActiveRecord::Base
has_many :media_types
end
SQL for simplicity sake are:
create_table :media do |t|
t.string "name", :limit => 255
t.integer "media_type_id"
end
create_table :media_types do |t|
t.string "name", :limit => 255
end
Once I insert a Media record relating to a media_type_id, how do I pull back the media_type.name value related to the media record?
I blindly tried:
media = Media.find(1)
media.media_type_id.name
But that didn't work of course. Is my SQL not Rails standards possibly?
Appreciate any help.
If you idea: media_type has many medias, but every media has only one media_type
You need another models:
class MediaType < ActiveRecord::Base
has_many :medias
end
class Media < ActiveRecord::Base
belongs_to :media_type
end
And
media = Media.find(1)
media.media_type.name
give you name
It seems that media has_many media_types.
In that case you would create media_id column in media_types table, but you did it other way around.
You will then approach each relation by
types = Media.media_types
to get the media_types that the Media has, and
media = MediaType.media
to get the media that mediatype belongs to.