Find if value is present in a list - ruby-on-rails

I have a post controller that allows you to select a profession. I also have a work controller that has a list of professions stored seperated by commas. Eg:
Post has a row called profession, and only allows you to choose 1.
Work has a row called profession that are stored like this: business, law, accounting.
What I would like to do is once you save your post, take you to a page that shows work where Post profession is equal to Work profession. (Only relevant work show)
How would I go about doing this?

This sounds like what you're trying to do.
posts_controller.rb
def create
#post = Post.create(post_params)
if #post.save
redirect_to some_other_path(post_id: #post.id)
else
# handle error
end
end
some_controller.rb
def some_action
#post = Post.find(params[:post_id])
#works = Work.where("profession like ?", "%#{#post.profession}%")
end
Since you're using Postgres, you can use like which will match patterns within a field. This is more of a search functionality than a way of associating records. So if you have a Post that has a profession of "market", the like would match "market" and "marketing".
It might prove to be cleaner and easier to have Profession be a separate model. Post could belong_to, and Work could have_many.

Related

Filter by multiple topics in rails

I’ve had a look through the form and have been unsuccessful in finding a solution for this. I currently have a page that displays a bunch of posts, with a simple #post = Post.all
I’m trying to create form that can filter by one of the columns, topics that Post have.
So basically on the left side it will have all the posts,
The right side will have all the unique topics of posts. The user can select one or more of the topics, then click submit, and the posts With the selected topics will be shown.
I’m able to show the unique topic, but unsure how to organise it so it filters after submitting. My current thought process is to create a form with all the topics. When the user presses a few topics, then submits, it filters by the selected topics. But I’m unsure on how to do the filtering in the controller as the amount of topics selected is dynamic. For example if it was just one post. It’s a simple #post = Post.where(:topic params[:chosen]) but I’m unsure how to filter it dynamically on different amounts of topics. Like if 2 or more topics are chosen.
Any help would be greatly appreciated.
You can implement the index method of controller something similar to this (Note: The code is not tested)
class PostsController < ApplicationController
before_action :construct_filters, only:[:index]
def index
#posts = Post.where(#query) # => Posts.where({"topic" => ["topic1","topic2"]}) or Post.where({}) in the case of no params passed(return all posts)
respond_to do |format|
format.html
format.js # In case of remote true submit, respond with index.js.erb and update the listing
end
end
private
# Generic method to construct query for listing
def construct_filters
#query = {}
# Pass the chosen from the form as comma separated string inside filter hash.
# Example params received: filters: {"topic" => "topic1,topic2"}
if params["filters"].present?
params["filters"].each do |k,v|
# You can modify the below line to suit your needs if you are not passing as comma seperated
filter_value = filter_value.split(',')
#query[k] = filter_value if filter_value.present?
end
end
# #query = {"topic" => ["topic1","topic2"]}
end
end
Having a generic filter method and a constructing parameter as filter hash will allow you to add more filters in future.
You can implement checkbox or multi select dropdown for selecting topics in your views, may be with input name as filters[topic]. Hope this helps!

Indexing method that gets parameters in Rails 4

I have a store application with a Product scaffold and I want to enable categories and pages that show each category of products.
My product model has a "category" attribute and I use the link_to helper to create links to each category.
In my products controller I added a method called index_by_category(cat):
def index_by_category(cat)
#products_by_category = Product.where(category: cat)
end
I'm trying to iterate #products_by_category in a view I created with the corresponding name (product/index_by_category.html.erb) just like the regular index method do. For some reason it render me the regular index method of products which shows ALL of them, even though the URL is:
http://localhost:3000/products?index_by_category=Food
This is what I did in my route.rb file:
get 'products/index_by_category'
I'm newbie to Rails development so if I did something which is wrong from the roots and the rails approach to the problem should be entirely different I also be happy to know for the sake of learning.
You are doing things a bit wrong. Try to write your controller like this:
def index_by_category
#products_by_category = Product.where(category: params[:category])
end
And update your route
get 'products/category/:category', to: 'products#index_by_category
Then visit
http://localhost:3000/products/category/Food
UPDATE
if you really want to use index method for both cases you could do that by modifying it to something like this
def index
if params[:category]
#products = Product.where(category: params[:category])
else
#products = Product.all
end
end
and then just visit
http://localhost:3000/products?category=Food

How to display an specific item of database

My rails app has a database set.
def index
#clubs = Club.all
end
This is my controller.
If i type in my Index.html.erb
<% #clubs.each do |club| %>
<%= club.name %>
<% end %>
I get all the names of my database show in my index view.
What if I just want to pick one or just a couple?
Thru the rails console i can by typing c=Club.find(1) 1 by default takes id=1.
So how can i just display several ID's and not all one the database in the same index.html.erb.
thanks anyway!
Try this:
Let us consider that params[:ids] contains all the ids that belong to the records you want to get.
def index
#clubs = Club.where(id: params[:ids])
end
Fix
The straightforward answer here is to recommend you look at the ActiveRecord methods you can call in your controller; specifically .where:
#app/controllers/clubs_controller.rb
Class ClubsController < ApplicationController
def index
#clubs = Club.where column: "value"
end
end
This will populate the #clubs instance variable with only the records which match that particular condition. Remember, it's your Rails app, so you can do what you want with it.
Of course, it's recommended you stick with convention, but there's nothing stopping you populating specific data into your #clubs variable
--
RESTful
As someone mentioned, you shouldn't be including "filtered" records in an index action. Although I don't agree with this idea personally, the fact remains that Rails is designed to favour convention over configuration - meaning you should really leave the index action as showing all the records
You may wish to create a collection-specific action:
#config/routes.rb
resources :clubs do
collection do
get :best #-> domain.com/clubs/best
end
end
#app/controllers/clubs_controller.rb
Class ClubsController < ApplicationController
def best
#clubs = Club.where attribute: "value"
render "index"
end
end
There are several ways to select a specific record or group of records from the database. For example, you can get a single club with:
#club = Club.find(x)
where x is the id of the club. Then in your view (the .html.erb file), you can simply access the #club object's attributes.
You can also cast a wider net:
#disco_clubs = Club.where(type: "disco") # returns an ActiveRecord Relation
#disco_clubs = Club.where(type: "disco").to_a # returns an array
And then you can iterate over them in the same manner you do in your index.html.erb. Rails has a rich interface for querying the database. Check it out here.
Also note that individual records - such as those selected with the find method - are more commonly used with the show action, which is for displaying a single record. Of course, that's for generic CRUD applications. It't not a hard rule.
change
def index
#clubs = Club.all
end
to this
def index
#clubs = Club.find(insert_a_number_that_is_the_id_of_the_club_you_want)
end
Querying your database is a complex thing and gives you a ton of options so that you can get EXACTLY what you want and put it into your #clubs variable. I suggest reading this part of the rails guide
It should also be noted that if you're only going to query your database for one record then change #clubs to #club so you know what to expect.

Update model multiple times from one controller action

I'm looking for best practices. Here's the scenario:
Customers can pay for one or more Widgets from a form. So I have a Payments model and a Widgets model. There is no association between them (Payments is associated with Customer). What's the best way to handle this?
In the Payments controller I could do:
def create
#customer = Customer.find(params[:customer_id])
if #customer.payments.create!(params[:payment])
how-many-widgets = params[:payment][:number].to_i
while how-many-widgets > 0
widget = Widgets.new
... update widget ...
widget.save!
how-many-widgets = how-many-widgets - 1
end
end
redirect_to #customer
end
Is this the best way to do this? Or is there some more elegant solution?
If you're saving and changing things, it's a good bet you should be doing this code in a model, rather than a controller. If I were to refactor your code, it would look a little like this:
def create
#customer = Customer.find(params[:customer_id])
if #customer.payments.create!(params[:payment])
params[:payment][:number].times do
Widget.create(params[:widget])
end
end
redirect_to #customer
end
If Widget.create isn't what you're looking for, come up with a custom method that takes the params in, transforms them, and then spits out the correct object. Also if the widgets should be related to either the customer or payments, don't hesitate to relate them -- like, if you look at that code and say, "I also need to pass the current user/customer/payment to the widget," that would be a good hint that the widget should be associated to that model somehow.

Constructing a Rails ActiveRecord where clause

What's the best way to construct a where clause using Rails ActiveRecord? For instance, let's say I have a controller action that returns a list of blog posts:
def index
#posts = Post.all
end
Now, let's say I want to be able to pass in a url parameter so that this controller action only returns posts by a specific author:
def index
author_id = params[:author_id]
if author_id.nil?
#posts = Post.all
else
#posts = Post.where("author = ?", author_id)
end
end
This doesn't feel very DRY to me. If I were to add ordering or pagination or worse yet, more optional URL query string params to filter by, this controller action would get very complicated.
How about:
def index
author_id = params[:author_id]
#posts = Post.scoped
#post = #post.where(:author_id => author_id) if author_id.present?
#post = #post.where(:some_other_condition => some_other_value) if some_other_value.present?
end
Post.scoped is essentially a lazy loaded equivalent to Post.all (since Post.all returns an array
immediately, while Post.scoped just returns a relation object). This query won't be executed until
you actually try to iterate over it in the view (by calling .each).
Mmmh, the best approach you want to use can be to spread this in 2 actions
def index
#post = Post.all
end
def get
#post = Post.where("author=?", params[:author_id])
end
IMHO it has more sense if you think about a RESTful API, index means to list all and get (or show) to fetch the requested one and show it!
This question is pretty old but it still comes up high in google in 2019, and also some earlier answers have been deprecated, so I thought I would share a possible solution.
In the model introduce some scopes with a test for the existence of the parameter passed:
class Post
scope :where_author_ids, ->(ids){ where(author_id: ids.split(‘,’)) if ids }
scope :where_topic_ids, ->(ids){ where(topic_id: ids.split(‘,’)) if ids }
Then in the controller you can just put as many filters in as you wish e.g:
def list
#posts = Post.where_author_ids(params[:author_ids])
.where_topic_ids(params[:topic_ids])
.where_other_condition_ids(params[:other_condition_ids])
.order(:created_at)
The parameter can then be a single value or a comma separated list of values, both work fine.
If a param doesn’t exist it simply skips that where clause and doesn’t filter for that particular criteria. If the param exists but its value is an empty string then it will ‘filter out’ everything.
This solution won’t suit every circumstance of course. If you have a view page with several filters on, but upon first opening you want to show all your data instead of no data until you press a ‘submit’ button or similar (as this controller would) then you will have to tweak it slightly.
I’ve had a go at SQL injecting this and rails seems to do a good job of keeping everything secure as far as I can see.
You should model url using nested resources. The expected url would be /authors/1/posts. Think of authors as resources. Read about nested resources in this guide: http://guides.rubyonrails.org/routing.html (scroll to 2.7 - Nested Resources).
Would something like this work?
def get
raise "Bad parameters...why are you doing this?" unless params[:filter].is_a?(Hash)
#post = Post.where(params[:filter])
end
Then you can do something like:
?filter[author_id]=1&filter[post_date]=... etc.

Resources