I try to fetch all videos except the ones with categories [20,21,22]
this is my query
#cc = Video.joins(:categories).where.not(categories: { id: [20,21,22]})
but when I do #cc.find(113).categories I get this
#<ActiveRecord::Associations::CollectionProxy
[#<Category id: 21, title: "music">, #<Category id: 22, title: "movies">,
#<Category id: 28, title: "collage">]>
What am I doing wrong?
Try this,
#cc = Video.includes(:categories).references(:categories).where.not(categories: { id: [20,21,22]})
Refer,
https://robots.thoughtbot.com/activerecords-wherenot
Try this:
array = [21,22,23]
#cc = Video.joins(:categories).where("category.id not in (?)", array)
EDIT
Think I spot the problem. Suppose your Video model is in a has_many relationship with Category. So you should do:
class Video < ActiveRecord::Base
has_many :categories
has_many :excluded, -> (array) { where("id not in (?)", array) }, class_name: 'Category'
end
And you call it like that:
Video.find(113).excluded([21,22,23])
You are doing a wrong query.
Try with:
Video.where.not(id: Video.joins(:categories).where(categories: { id: [20,21,22]}).pluck(:id))
Related
I have three models using Active Record associations:
Book Model
has_many :checkouts
User Model
has_many :checkouts
Checkout Model
belongs_to :book
belongs_to :user
In my view, I need the book, checkout, and user names from the checkouts.
By using Book.first.checkouts I get:
#<ActiveRecord::AssociationRelation
[#<Checkout id: 30,
checkout_date: "2017-04-13",
return_date: nil,
book_id: 118,
user_id: 1,
created_at: "2017-04-13 17:43:07",
updated_at: "2017-04-13 17:43:07"
>,#<Checkout id: 50,
checkout_date: "2017-04-13",
return_date: nil,
book_id: 118,
user_id: 1,
created_at: "2017-04-14 00:33:34",
updated_at: "2017-04-14 00:33:34">
]>
But, I would like to the user name, not just the id. I've tried Book.first.checkouts.map { |c| c.user.name } but that returns only the name, and I need the rest of the checkout information. Ideally, my data (converted to json) looks something like:
{
name: "Book Name",
checkouts: [
checkout_data: "Today",
user_name: "Mary"
]
}
How can I add the user name to my checkout data?
You can try this at your controller:
render json: #books, include: { checkout: { only: :checkout_date, include: { user: { only: :name } } }}
You should preload the data to prevent (N+1) query problem,
For Display possible checkouts for particular book:
book_id = <given book id>
book = Book.find(book_id)
checkouts = book.checkouts.includes(:user)
return_hash = {name: book.name, checkouts: []}
checkouts.each do |checkout|
return_hash[:checkouts] << { checkout_data: checkout.checkout_date,
user_name: checkout.user.name
}
end
To include other solutions, I found this worked pretty well:
checkouts = book.checkouts.unreturned.map do |checkout|
checkout.attributes.merge({ user_name: checkout.user.name })
end
{ checkouts: checkouts, available: book.available?, book_id: book.id }
attributes.merge did the trick.
Hi I am new to Ruby on Rails development. I have two queries with different model. My first_query is get from question model and second query is get from favourite model. I want to map with a column user_favourite from second query result to first query result.
this is my controller queries
def index
#first_query = Question.order('created_at DESC').page(params[:page]).per( (ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i )
#second_query=Favourite.with_user_favourite(#user)
#combined_queries = #first_query + #second_query
end
favourite.rb
scope :with_user_favourite, -> (user) {
joins(:user).
where(favourites: {user_id: user})
}
index.json.builder
json.questions #combined_events
json for the result is
{
questions: [ #this is first query result
{
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{ #this is second query result
id: 1,
user_id: 2,
question_id: 84,
created_at: "2016-05-12T06:51:54.555-04:00",
updated_at: "2016-05-12T06:51:54.555-04:00"
},
{
id: 2,
user_id: 2,
question_id: 81,
created_at: "2016-05-12T07:23:47.770-04:00",
updated_at: "2016-05-12T07:23:47.770-04:00"
}
]
}
i want response like
{
questions: [
{ #first query result
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: { #corresponding result from second query result
id: 1,
user_id: 2,
question_id: 88
}
},
{ #first query result
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: {} #corresponding result from second query result if there is no result for particular question in favourite table
},
]
}
The model relationships are:
class Question
belongs_to :user
has_many :favourite
end
class Favourite
belongs_to :user
belongs_to :question
end
class User
has_many :questions
has_many :favourite
end
You should modify your jBuilder template to support nesting.Since your model association is like one question has_many favorite so it will be an array and you can easily nest one object inside another.
json.array! #questions do |question|
json.user_id question.user_id
json.content question.content
json.user_favorites question.favorites do |json,favorite|
json.id question.favorite.id
json.user_id question.favorite.user.id
json.question_id question.id
end
end
Here is a link that you can refer to for more clarity.
Generate a nested JSON array in JBuilder
Using JBuilder to create nested JSON output in rails
Hope it helps!.
You can add an association between user_favourite and question so that you can select all user favourites on one question.
Question.rb:
has_many :user_favourites
UserFavourite.rb:
belongs_to :question
Then, as your web action:
def index
#questions = Question.all.order('created_at DESC').page(params[:page]).per((ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i)
end
And finally, in index.json.builder:
json.questions #questions do |question|
json.user_favourites question.user_favourites
end
including whatever other fields you want.
class Event < ActiveRecord::Base
has_many :meetings
end
class Meeting < ActiveRecord::Base
belongs_to :event
end
How to write mysql query to search all events group_by meeting DATE(start_at)?
Event.inludes(:meetings).group ...
As a result I want to get a Hash:
{"2014-01-24"=>[#<Event id: , title: "First", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">, #<Event id: 2, title: "Second", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">], "2013-01-29"=>[#<Event id: 3, title: "Third", created_at: "2013-01-29 05:30:40", updated_at: "2014-01-29 05:30:40">], ...]}
P.S: I am using PostgreSQL
Now I get it by this way:
hash = {}
Meeting.where("extract(month from start_at) = ?", Date.today.month).pluck('DATE(start_at)').uniq.each do |date|
hash[date] = Event.includes(:meetings).where("DATE(meetings.start_at) = ?", date).references(:meetings)
end
But it produced so many queries to the database :(
Event.joins(:meetings).group('meetings.start_at') should do. But want you want is a group_by array method http://apidock.com/ruby/Enumerable/group_by so what you should do is
#events.group_by {|e| e.meeting.start_date}
In case of many to many you should be better off with
result = Hash.new
Meeting.include(:events).each {|m| result[m.start_at]||=[]; result[m.start_at] << m.events}
and with one liner you could
Meeting.includes(:events).inject(Hash.new) do |result, m|
result[m.start_at]||=[]
result[m.start_at] << w.events
result
end
This code should execute two database calls i think
class Subject
has_many :subject_attribute_types
has_many :subject_attributes
accepts_nested_attributes_for :subject_attributes
end
class SubjectAttributeType
belongs_to :subject
has_many :subject_attributes
attr_accessible :type_name
end
class SubjectAttribute
belongs_to :subject
belongs_to :subject_attribute_type
attr_accessible :value
end
For example:
s1 = Subject.create()
s2 = Subject.create()
sat1 = SubjectAttributeType.create(subject: s1, name: 'Age')
sat2 = SubjectAttributeType.create(subject: s1, name: 'Sex')
sat3 = SubjectAttributeType.create(subject: s2, type_name: 'Age')
sat5 = SubjectAttributeType.create(subject: s2, type_name: 'Username')
SubjectAttribute.create(subject: s1, subject_attribute_type: sat1, value: 20)
SubjectAttribute.create(subject: s1, subject_attribute_type: sat2, value: "male")
SubjectAttribute.create(subject: s2, subject_attribute_type: sat3, value: 21)
SubjectAttribute.create(subject: s2, subject_attribute_type: sat1, value: "user1")
Problem:
What's the best practice to make a search on exact subject_attributes.
If i want to find all Subjects with age >= 18 and nickname like %user%
currently i am using ransack gem, but i can't think out how to make a search on nested_attributes
I see there is a problem in business logic of your app. Why would you need your AttributeType to know about any of subject?
class Subject < ActiveRecord::Base
has_many :subject_attributes
has_many :attribute_types, through: :subject_attributes
end
class SubjectAttribute < ActiveRecord::Base
belongs_to :attribute_type
belongs_to :subject
attr_accessible :attribute_type_id, :subject_id, :value
end
class AttributeType < ActiveRecord::Base
attr_accessible :type_name
end
After that if you insert some data:
s1 = Subject.create
s2 = Subject.create
sat1 = AttributeType.create(type_name: "Age")
sat2 = AttributeType.create(type_name: "Sex")
sat3 = AttributeType.create(type_name: "Username")
SubjectAttribute.create(subject:s1, attribute_type:sat1, value: 20)
SubjectAttribute.create(subject:s1, attribute_type:sat2, value:"male")
SubjectAttribute.create(subject:s2, attribute_type:sat1, value:21)
SubjectAttribute.create(subject:s2, attribute_type:sat3, value:"user1")
you will be able to make selects.
In your example you use several attributes, so you have to make several requests:
that way you'll find subject with value name:
names = Subject.joins(:attribute_types).where("attribute_types.type_name = 'Username'
and value like '%user%'")
=> [#<Subject id: 2, created_at: "2013-05-29 11:11:51", updated_at: "2013-05-29 11:11:51">]
that way you'll find subject with value age
ages = Subject.joins(:attribute_types).where("attribute_types.type_name = 'Age'
and value >= 18")
=> [#<Subject id: 1, created_at: "2013-05-29 11:11:42", updated_at: "2013-05-29 11:11:42">,
#<Subject id: 2, created_at: "2013-05-29 11:11:51", updated_at: "2013-05-29 11:11:51">]
That way you'll find intersected subjects
subjects = (names&ages)
=> [#<Subject id: 2, created_at: "2013-05-29 11:11:51", updated_at: "2013-05-29 11:11:51">]
Using dynamic attribute_types makes select really hard. so if you ok with making separate request for each type-value params, use it. Otherwise maybe its really just columns of Subjects?
I'm working on a Newsfeed action. I have active friendships and i would like to find the posts only of those friendships to create the newsfeed.
This is the code i currently have:
#active_friendships = current_user.active_friendships
#posts = Post.where({ :user_id => [5,8,16] }).order("created_at DESC")
I don't know how to send the #active_friendships.user_id values to the IN in the second line. At the moment the code is only worked because it has hardcoded the user_id of my active friends (5,8,16).
(If i do a debug #active_friendships) i get the 3 objects with their ids, but i still don't know how to send them to the IN in the second line as the ids to look for.
--
- !ruby/object:Friendship
attributes:
created_at: 2010-10-06 22:27:54.620007
updated_at: 2010-10-07 00:19:10.329799
id: 182
user_id: 8
status: 1
friend_id: 5
attributes_cache: {}
changed_attributes: {}
destroyed: false
marked_for_destruction: false
new_record: false
previously_changed: {}
readonly: false
- !ruby/object:Friendship
attributes:
created_at: 2010-10-07 19:13:10.617959
updated_at: 2010-10-07 19:13:17.097514
id: 192
user_id: 16
status: 1
friend_id: 5
attributes_cache: {}
changed_attributes: {}
destroyed: false
marked_for_destruction: false
new_record: false
previously_changed: {}
readonly: false
- !ruby/object:Friendship
attributes:
created_at: 2010-10-10 04:12:48.931120
updated_at: 2010-10-10 04:12:56.960752
id: 214
user_id: 8
status: 1
friend_id: 5
attributes_cache: {}
changed_attributes: {}
destroyed: false
marked_for_destruction: false
new_record: false
previously_changed: {}
readonly: false
Many thanks in advance
You can try this:
#posts = Post.where(:user_id => #active_friendships.map(&:friend_id)
).order("created_at DESC"
But better way of implementing this is to add an association
class User
has_many :active_friendships, :class_name => "Friendship", :conditions = {...}
has_many :active_friend_posts, :through => :active_friendships,
:source => :friend_posts
end
class Friendship
belongs_to :user
belongs_to :friend, :class_name => "User"
has_many :friend_posts, :class_name => "Post",
:primary_key => :freind_id, :foreign_key => :post_id
end
class Post
belongs_to :user
end
Now you can do the following:
current_user.active_friend_posts.order("created_at DESC")
I'm new at this, but could you collect all of the Friendship.user_id's into an array and then pass that in the where clause?
#friends_userids = current_user.active_friendships.collect { |x| x.user_id }
#posts = Post.where({ :user_id => #friends_userids }).order("created_at DESC")
I had a similar issue. This is how I implemented it:
ids = #active_friendships.map { |x| x.user_id }
ids = ids.join(",")
#posts = Post.all(:conditions => ["posts.user_id in (#{ids})"]).order("created_at DESC")
This creates an array of all user IDs from #active_friendships and then joins them into a string with a comma as a separator. Then I simply passed this to the :conditions parameter.
It's not as elegant as I like but it works.
Hope this helps.
Rails has a special method for getting an array of associated ids. You should be able to do this in one line something like this:
#posts = Post.where(:user_id => current_user.active_friendship_ids).order("created_at DESC")
Whenever you're dealing with has_many and has_many :through relationships, you have access to the association_ids method, which won't return the full objects. Check out the full api here:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html