i have a Book model looking like this:
class Book < ActiveRecord::Base
belongs_to :author, class_name: 'User', touch: true
belongs_to :translator, class_name: 'User'
end
How can i search for books with specific author name? i join the tables first:
Book.joins(:author).joins(:translator)
But i cant chain
.where('authors.name = "Tolkien"')
because there is no "authors" table in the database. is
.where('users.name = "Tolkien"')
a safe approach here? is there no risk concerning both translators and authors being users in fact?
(select() method is not an option, i need activerecord::relation here)
You need to pass table name in query
Book.joins(:author).where(users: {name: 'Tolkien'})
is .where('users.name = "Tolkien"') a safe approach here?
If you are concerned about two associations using same table you might need to add a field to distinguish them
For ex. a boolean field author and use condition with association which will solve the problem
class Book < ActiveRecord::Base
belongs_to :author, -> { where(author: true) }, class_name: 'User', touch: true
belongs_to :translator, -> { where(author: false) }, class_name: 'User'
end
Related
I have models with variables (many model classes : polymorphic relation), and constraints between variables (variables are not necessarily in the same model).
I try to make a query to find all constraints associated to a list of models (with all vars associated to the models in list), and I really don't know how to do it.
My models looks like this.
class Model1 < ApplicationRecord
has_many :vars, as: :model
end
class Model2 < ApplicationRecord
has_many :vars, as: :model
end
class Var < ApplicationRecord
belongs_to :model, polymorphic: true
# model_type and model_id in vars table
has_many :cns_vars
has_many :constraints, through: :cns_vars
end
class_CnsVar < ApplicationRecord
belongs_to :var
belongs_to :constraint
end
class Constraint < ApplicationRecord
has_many :cns_vars
has_many :vars, through: :cns_vars
end
To find constraints related to one model I have this query :
Constraint.includes(:vars).where(active: true, vars: {model_id: model.id, model_type: model.class.to_s})
This query give me the constraints that have at least one var associated to my model.
I need constraints with all vars associated to a list of models.
Is there a way to make the same query, but with all vars associated to the model ?
Is there a way to make the same query, but with all vars associated to a list of models ?
Constraint.includes(:vars).where(active: true, vars: {*[var.model_type, var.model_id] in my models list*})
Is there a solution to do this with one query ?
Or do I have to do it another way ?
Thanks for your help.
(ruby : 2.6.0 / rails : 5.2.3)
EDIT :
To give better explanation, look at this function that returns what I need, but this make too much queries !
def constraints_for_models_list(models)
all_vars = models.flat_map(&:vars)
all_constraints = all_vars.flat_map(&:constraints)
all_constraints.uniq!
constraints = []
all_constraints.each do |constraint|
next unless constraint.vars.included_in?(all_vars)
constraints << constraint
end
return constraints
end
Constraint.includes(:vars).where(active: true).where.not(vars: { model: nil })
of course if I correctly get the point of what you're trying.
for what you asked in comment:
Constraint.includes(:vars).where(active: true).where('vars.model_type IN
?', ['Model1',Model2'])
I have model course.rb with pre_courses is an array.
class Course < ActiveRecord::Base
serialize :pre_courses, Array
end
Now I want to check if an exist course is pre_course of any course by Activerecord or raw SQL (I am using MySQL), such as Course.where("pre_courses INCLUDEs self.id").
Is there any way to do this?
A serialized array is just a string in the db, so try using LIKE, for example:
Course.where("pre_courses LIKE ?", "% #{self.id}\n%")
Notice that a serialzed array adds a space before each item and a new line after, thus the added space before the interpolated string and the \n at the end.
It sounds like a pre_course is actually a regular course, a course can have many pre_courses and a pre_course can belong to many courses. A self referential has_many through relationship is possible and may give you more flexibility to work with your data than serializing it as an array.
You'll need a join model I'll call CoursePreCourse. It will have the columns course_id and pre_course_id. pre_course_id will be a foreign key for records on the courses table.
class CreateCoursePreCourses < ActiveRecord::Migration[5.1]
def change
create_table :course_pre_courses do |t|
t.references :course, foreign_key: true
t.references :pre_course, foreign_key: { to_table: :courses }
t.timestamps
end
end
end
class CoursePreCourse < ApplicationRecord
belongs_to :course
belongs_to :pre_course, class_name: 'Course'
end
class Course < ApplicationRecord
# A straight-forward has_many :through association for a course that has_many :pre_courses
has_many :course_pre_courses
has_many :pre_courses, through: :course_pre_courses
# A little coercion is necessary to set up the association as a pre_course that has_many :courses
has_many :pre_course_courses, class_name: 'CoursePreCourse', foreign_key: :pre_course_id
has_many :courses, through: :pre_course_courses
end
Now you can retrieve the courses that are pre_courses of any course with course.pre_courses. If you want to see if a course is a pre_course of other courses it's pre_course.courses. Similarly you can add a course to another course's pre_courses with either course.pre_courses << pre_course or pre_course.courses << course.
Do you ever say a word so many times it loses its meaning?
I am developing a rails app where users can add tasks they wish to do to a customised list of their own. Each task can also belong to 0 or more categories. So far I've tried this:
user.rb
has_one :user_list
has_many :tasks, through: :user_list
user_list.rb
belongs_to :user
has_many :tasks
tasks.rb
has_and_belongs_to_many :categories
[timestamp}_migration.rb
create_table :user_lists do |t|
t.integer :user_id
t.integer :task_id
t.timestamps null: false
end
The issue I am having is in the console I try to run User.find(1).tasks it cannot find the column tasks.user_list_id when using the following query:
SELECT "tasks".* FROM "tasks" INNER JOIN "user_lists" ON "tasks"."user_list_id" = "user_lists"."id" WHERE "user_lists"."user_id" = ? [["user_id", 1]]
This query should be joining the tasks id from the tasks table with the tasks id on the user_lists table. Are the associations correct and if so what can I do to change the query?
To allow tasks to be placed on many lists you need a M2M join table which joins the user_lists and tasks table.
class User < ActiveRecord::Base
has_many :user_lists
has_many :tasks, through: :user_lists
end
class Task < ActiveRecord::Base
has_many :user_list_items
has_many :user_lists, through: :user_list_items
end
class UserListItem < ActiveRecord::Base
belongs_to :task
belongs_to :user_list
has_one :user, through: :user_list
# optional
validates_uniqueness_of :task_id, scope: :user_list_id
end
class UserList < ActiveRecord::Base
belongs_to :user
has_many :user_list_items
has_many :tasks, through: :user_list_items
end
You can create the join model and migration with:
rails g model UserListItem user_list:belongs_to task:belongs_to
You may also want to open up the migration and add a compound index:
add_index :user_list_items, [:user_list_id, :task_id], unique: true
Setting it as unique is optional - but in most cases you want join table entries to be unique for table A and B.
See:
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
http://guides.rubyonrails.org/active_record_migrations.html
Your use case calls for a task to be assigned to more than one user and a user has only one task list. This sounds like a HABM association between users and tasks.
The simplest way to express that would be:
class User
has_and_belongs_to_many: :tasks
...
end
class Task
has_and_belongs_to_many: :users
...
end
and a migration to create the join table:
create_join_table :users, :tasks, do |t|
t.index :user_id
t.index.task_id
end
You don't need to create a TaskUser model to match the join table until you need to track additional attributes. Rails will take care of that automatically.
If a user needs more than one task list, you'll need that TaskList model. Let me know and I'll update my answer.
Here's the documentation on HABM and join migration
I have a list model which has_and_belongs_to_many postings and vice versa.
I have a join table:
create_table :postings_lists do |t|
t.integer :posting_id
t.integer :list_id
end
Users have many lists.
I already validate the list uniqueness for the user with:
validates :name, presence: true, uniqueness: {scope: :user_id}
In the join table, how can I validate the uniqueness of both the :posting_id and the :list_id so that a posting can't belong to a list more than once?
I have tried adding, uniq: true to both has_and_belongs_to_manys in the models but it messes things up and I have tried added custom validations in the list model but it wasn't working.
I think the simplest thing would be just to validate both ids in the join table but I don't know if I can do that without creating a model?
I would use a has_many :through instead of HABTM.
class List < ActiveRecord::Base
has_many :postings
has_many :posts, through: :postings
end
class Posting < ActiveRecord::Base
belongs_to :list
belongs_to :post
validate :post_id, uniqueness: {scope: :list_id}
end
class Post < ActiveRecord::Base
has_many :postings
has_many :lists, through: postings
end
I ended up adding uniq: true
Lists
has_and_belongs_to_many :postings, uniq: true
Postings
has_and_belongs_to_many :lists, uniq: true
This causes some issues if you use things like pluck on postings because things have to be "distinct" before doing other things like "order_by" to them.
But you can work around this issue by using different queries like 'map' instead.
I have the following setup:
One matchdays table with a column called home_team_id and one called visitor_team_id
and a team table.
My Match model looks like this:
class Match < ActiveRecord::Base
belongs_to :home_team, class_name: "Team", foreign_key: :home_team_id
belongs_to :visitor_team, class_name: "Team", foreign_key: :visitor_team_id
belongs_to :matchday
validates :home_team, presence: true
validates :visitor_team, presence: true
end
And the Team model like that:
class Team < ActiveRecord::Base
has_many :matches
has_many :player
end
Now it's getting tricky (at least for me). I'd like to be able to call team.matches and get all of the matches for the team. Since every team has home games and also games on the road.
Currently I'm getting a ActiveRecord::StatementInvalid Error because it's looking for the team_id column in the matches table.
So if I understand correctly, what you need is just a method that returns all games where the current team is playing. This should do the trick:
class Team < ActiveRecord::Base
has_many :player
def matches
Team.where(home_team_id => self.id, foreign_key => self.id)
# This line will also work if you want to try it out.
# Team.where("home_team_id = ?", self.id).where("foreign_key = ?", self.id)
end
end
Happy coding!