This is a rails newb question. I have a Contents model that has a content_type attribute. I have a few different content_types that I would like to filter, passing the type through the URL like so: /contents?content_type=blog
I understand I can get the contents based on that parameter like so:
#contents = Content.where({:content_type => params[:content_type]})
But when the URL parameter is not present, it's not getting any contents. I would prefer that when a URL param isn't being passed, that all contents (regardless of type) would be retrieved. How do I do that?
I would define a scope, like this (inside your model)
class Content
scope :by_content_type, lambda { |contenttype|
where({:content_type => contenttype}) unless contenttype.blank?
}
end
and then you use this in your controller as follows:
#contents = Content.by_content_type(params[:content_type])
This should work:
if params[:content_type].blank?
#contents = Content.scoped
else
#contents = Content.where({:content_type => params[:content_type]})
end
Theres a reasonable pattern here using a series of chained scopes to narrow filters based on query params:
#contents = Content.scoped # Start with no filter
# Optionally narrow filter if filter param is present
type = params[:content_type]
#contents = #contents.where(:content_type => type) if type
#contents = Content.where({(:content_type => params[:content_type]} unless params[:content_type].blank?))
Related
I want to call the ActiveRecord method where with an array for a column. If the each item on the array doesn't exist, create the object. The closest method I found for this is first_or_create but this seems to be called only once, not for each time the record doesn't exist. Below is my example code-
hashtag_list = params[:message][:hashtag_primary]
#hashtags = Hashtag.where({:name => hashtag_list}).first_or_create do |hashtag|
hashtag.creator = current_user.id
end
Rails version- 4.2.1
I don't know a direct method, only a workaround
existing_tags = Hashtag.where({:name => hashtag_list}).pluck(:name)
not_existing_tags = hashtag_list - existing_tags
#hashtags = Hashtag.where({:name => existing_tags}).all
not_existing_tags.each do |tag|
#hashtags << Hashtag.new name: tag
end
#hashtags.each do |hashtag|
hashtag.creator = current_user.id
end
This is expected behavior of where + first_or_create method. Basically where(field: array) produces an SQL to find all records where field matches any item in the array. Than you have first_or_create method which takes the first record from results or creates a new one with escaped array value assigned to a field (so something like field: "[\"foo\", \"bar\"]" when used as where(field: %w(foo bar)).
If you want to create records for each hashtag from your list, you should iterate over it:
if #hashtag = Hashtag.where({:name => hashtag_list}).first
# do something if found the first one
else
hashtag_list.each do |hashtag|
# create an object
end
end
If you want to create missing hashtags even if the record is found, you can extract this to a private helper method with missing tags as the argument and re-write code as:
if #hashtags = Hashtag.where({:name => hashtag_list})
# do something if found
end
create_missing_hashtags(hashtag_list - #hashtags.pluck(:name))
I am pretty new to Rails and I have a feeling I'm approaching this from the wrong angle but here it goes... I have a list page that displays vehicles and i am trying to add filter functionality where the user can filter the results by vehicle_size, manufacturer and/or payment_options.
Using three select form fields the user can set the values of :vehicle_size, :manufacturer and/or :payment_options parameters and submit these values to the controller where i'm using a
#vehicles = Vehicle.order("vehicles.id ASC").where(:visible => true, :vehicle_size => params[:vehicle_size] )
kind of query. this works fine for individual params (the above returns results for the correct vehicle size) but I want to be able to pass in all 3 params without getting no results if one of the parameters is left blank..
Is there a way of doing this without going through the process of writing if statements that define different where statements depending on what params are set? This could become very tedious if I add more filter options.. perhaps some sort of inline if has_key solution to the effect of:
#vehicles = Vehicle.order("vehicles.id ASC").where(:visible => true, if(params.has_key?(:vehicle_size):vehicle_size => params[:vehicle_size], end if(params.has_key?(:manufacturer):manufacturer => params[:manufacturer] end )
You can do:
#vehicles = Vehicle.order('vehicles.id ASC')
if params[:vehicle_size].present?
#vehicles = #vehicles.where(vehicle_size: params[:vehicle_size])
end
Or, you can create scope in your model:
scope :vehicle_size, ->(vehicle_size) { where(vehicle_size: vehicle_size) if vehicle_size.present? }
Or, according to this answer, you can create class method:
def self.vehicle_size(vehicle_size)
if vehicle_size.present?
where(vehicle_size: vehicle_size)
else
scoped # `all` if you use Rails 4
end
end
You call both scope and class method in your controller with, for example:
#vehicles = Vehicle.order('vehicles.id ASC').vehicle_size(params[:vehicle_size])
You can do same thing with remaining parameters respectively.
The has_scope gem applies scope methods to your search queries, and by default it ignores when parameters are empty, it might be worth checking
I want to have urls like this:
www.example.com/topic1/...
www.example.com/topic2/...
www.example.com/topic3/...
And these should be served using the TopicController.
The values topic1, topic2, topic3, .. are coming from the table in the database (topics).
Is this possible?
What will my route look like then? These topics will be added ofcourse, it isn't something that is static in nature.
Try:
match '*a/' => 'topic#show' # assume the action is show
params[:a] will equal topic1 etc.
The closest solution I can think of would be to define a route such as
match "/topic/:name" => "topic#process_topic"
and the corresponding action in the TopicController
def process_topic
#topic = Topic.find_by_name(params[:name])
case #topic.name
when topic1
...
when topic2
...
end
end
Simple question - how do I get the path or full URL of the current action INCLUDING the query string?
I wish to save it to the session variable like so:
def show
#thingy = Thingy.find(params[:id])
session[:some_var] = current_url
...
end
At the moment I'm doing the following, but it seems a bit heavy-handed (especially the specifying of query string params individually):
def show
#thingy = Thingy.find(params[:id])
session[:some_var] = thingy_path(#thingy, :q1 => params[:q1], :q2 => params[:q2])
...
end
request.url is probably what you are looking for.
access params variable,it will give you query as well as controller and action.
By using request object you can dig more deeper if you want.
I have a search form on my site that submits the url in the form of this (those are Tag id's)
http://localhost:3000/tags?utf8=✓&q=2%2C12%2C44
It works for the first Tag ID but not the others
def self.search(query = {})
output = self.scoped
output = output.where(:id => query[:q])
end
TagsController:
def index
#tags = Tag.search(params)
end
ActiveRecord::Base.where accepts an array as argument when you want a query with multiple matches. To turn your query parameter into an array you can use split:
output = output.where(:id => query[:q].split(','))