I currently have a DB in Ruby on Rails, however, I have been having trouble with the documentation on how to do much other than list all of the items in DB. I am still new to the this language as a whole, and wish I didn't need to ask for so much help, but here it goes. My pertinent code is as follows:
migrate/(DB name)
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps null: false
end
end
end
articles_controller.rb
class ArticlesController < ApplicationController
def index
#articles = Article.all
Article.search(params[:id])
end
def show
#article = Article.find(params[:search])
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(params.require(:article).permit(:title, :text))
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
article.rb
class Article < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
def self.search(search)
if search
#article = Article.where('name LIKE ?', "%#{search}%")
else
#article = Article.all
end
end
end
index.html.rb
<h1>Listing articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
<%= form_tag articles_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
</table>
Thanks for any help in advance!
Essentially your issue is that you're trying to set controller instance variables in a class method in your model.
class ArticlesController < ApplicationController
def index
#articles = Article.search(params[:search])
end
end
article.rb
class Article < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
def self.search(search)
if search.present?
Article.where('title LIKE ?', "%#{search}%")
else
Article.all
end
end
end
So now the class method does the search, returns the collection to the controller which assigns them to an instance variable for use in the view.
You have got your search form, made sure that is a GET one. Good. When you hit search you'll notice in your development log that there's a hit to articles#index and the browser will show the same as before. To make the search matter edit the index method in the articles controller.
def index
#articles = Article.all.search(params[:search])
end
In Article.search you have a name where you should have a title.
PS: You've got the show method a bit wrong.
Related
I am currently trying to create a search method. I have a database all setup; however, I am running into the errors:
ActiveRecord::RecordNotFound in ArticlesController#index
and:
Couldn't find Article with 'id'=all
Here is the pertinent code:
Articles_controller.rb
class ArticlesController < ApplicationController
def index
#articles = Article.all
#articles = Article.search(params[:id])
end
def show
#article = Article.find(params[:search])
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(params.require(:article).permit(:title, :text))
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
article.rb
class Article < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
def self.search(search)
if search
#article = Article.find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
#article = Article.find(:all)
end
end
end
index.rb
<h1>Listing articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
<%= form_tag articles_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
</table>
Sorry for all the code to look through. The errors I am getting are from running localhost:3000/articles, where I receive these error messages from the server. I should note that I am still very new to both Ruby and Ruby on Rails; however, I aim to learn and find seeing proper code helps me quite significantly (I am dyslexic and tend to be a visual learner).
I truly appreciate your help, thanks in advance.
I think find can not take :all. the documentation says
"Using the find method, you can retrieve the object corresponding to the specified primary key that matches any supplied options. I think this is enough
Article.where('name LIKE ?', "%#{search}%")
or if you find all the articles
Article.all
Why do you have #articles = Article.search(params[:id]) in the index method?
Also, the stack trace will tell you exactly on which line the error occurs
I set up a Rails toy to play with Twitter API, but already having challenges right from the start. My files look like this:
routes.rb
Rails.application.routes.draw do
devise_for :users
root to: 'accounts#index'
resources :accounts do
resources :posts
end
end
account.rb
class Account < ActiveRecord::Base
belongs_to :user
has_many :posts
accepts_nested_attributes_for :posts
validates :name, :description, presence: :true
end
post.rb
class Post < ActiveRecord::Base
belongs_to :account
validates :tweet, presence: true
end
accounts_controller.rb
class AccountsController < ApplicationController
before_action :authenticate_user!
def index
#accounts = Account.all
#user = current_user
end
def new
#account = Account.new
end
def create
#account = Account.new(account_params)
#account.user = current_user
if #account.save
flash[:notice] = "Account succesfully created."
redirect_to #account
else
flash.now[:alert] = "Oops, something went wrong!"
render 'new'
end
end
def show
#account = Account.find(params[:id])
end
def update
end
def destroy
end
private
def account_params
params.require(:account).permit(:name, :description, posts_attributes: [:tweet])
end
end
post_controller.rb
class PostsController < ApplicationController
before_action :set_account
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = #account.posts.build(post_params)
if #post.save
flash[:notice] = "Tweet created successfully."
redirect_to [#account, #post]
else
flash.now[:alert] = "Something went wrong."
render 'new'
end
end
def edit
#post = Post.find(params[:id])
end
def update
if #post.update
flash[:notice] = "Tweet updated."
redirect_to [#account, #post]
else
flash.now[:alert] = "Something is not right!"
render 'edit'
end
end
def destroy
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:tweet)
end
def set_account
#account = Account.find(params[:account_id])
end
end
The tricky part is here:
What I am trying to do here is on the accounts page, when a user clicks on the account name, he should be redirects to the new action of the Posts controller that will allow him to create a new post for the account in question. Somehow, I am not sure how to pass the :account_id parameter.
views/accounts/index.html.erb
<table>
<tr>
<th>Account Name</th>
<th>Description</th>
<th>Tweets</th>
</tr>
<% #user.accounts.each do |account| %>
<tr>
<td><%= link_to account.name, new_account_post_path(#account) %></td>
<td><%= account.description %></td>
<td><%= account.posts.count %></td>
</tr>
<% end %>
</table>
<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
<%= link_to "Create new account", new_account_path %>
Error in browser:
You need to change
<%= link_to account.name, new_account_post_path(#account) %>
to
<%= link_to account.name, new_account_post_path(account) %>
Use this code:
<% #user.accounts.each do |account| %>
<tr>
<td><%= link_to account.name, new_account_post_path(account) %></td>
<td><%= account.description %></td>
<td><%= account.posts.count %></td>
</tr>
<% end %>
I'm creating a website where users can post their items & services for sale (a classified ads site) and have setup the 'listing', 'category', and 'user' models. The listings and categories are associated with each other in a "has_many"-->"belongs_to" relationship, with categories owning listings.
However, even though the Listings are successfully associating with a Category upon creation of a new listing (I believe...?), the "show" page for Categories is not displaying it's associated listings; (displaying the "No listings to display" message"). What could be the problem?
-Here's my 'show' page for Categories:
<h1><%= #category.name %></h1>
<%= render partial: 'listings/list', locals: {
listings: #category.listings } %>
-and here's the controller for Categories:
class CategoriesController < ApplicationController
def show
#category = Category.find(params[:id])
end
end
-Here's the controller for Listings:
class ListingsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, except: [:create, :index, :new]
def index
#listings = Listing.all
end
def show
end
def new
#listing = Listing.new
end
def edit
end
def create
#listing = current_user.listings.build(listing_params)
if #listing.save
redirect_to #listing
flash[:success] = "Listing was successfully created."
else
render 'new'
end
end
def update
if #listing.update(listing_params)
flash[:success] = "Listing was successfully updated."
redirect_to #listing
else
render 'edit'
end
end
def destroy
#listing.destroy
flash[:success] = "Listing deleted."
redirect_to request.referrer || root_url
end
private
def listing_params
params.require(:listing).permit(:name, :description, :price, :image,
:category_id)
def correct_user
#listing = current_user.listings.find_by(id: params[:id])
redirect_to root_url if #listing.nil?
end
end
-This is listing partial referenced in the show page:
<% if #listings.nil? %>
No listings to display! Go <%= link_to 'create one', new_listing_path %>.
<% else %>
<table class="table table-striped">
<tbody>
<% #listings.each do |listing| %>
<tr>
<td><%= link_to listing.name, listing %></td>
<td class="text-right">
<% if listing.price %>
<%= number_to_currency(listing.price) %>
<% end %>
</td>
<td class="text-right">
<% #listings.each do |listing| %>
<% if listing.category %>
<%= link_to listing.category.name, listing.category %>
<% end %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
-The model file for listing:
class Listing < ActiveRecord::Base
belongs_to :user
belongs_to :category
default_scope -> { order('created_at DESC') }
validates :name, presence: true
validates :description, presence: true
validates :price, presence: true
validates :user_id, presence: true
mount_uploader :image, ImageUploader
end
-The model file for category:
class Category < ActiveRecord::Base
has_many :listings
end
<%= render partial: 'listings/list', locals: {
listings: #category.listings } %>
Makes listings avalable via listings var, not #listings, as in your template.
Just remove # sign.
Its currently listing my oldest articles at the top and I want to do the opposite. I think I need to order it as created_at somewhere but I have yet to make it work. I know this is easy but I'm still a newbie. Thanks
Currently I have
<div class="bit-75">
<h2 id="title"><%= link_to article.title, article_path(article) %></h2>
<br>
<ul id="article-links">
<div id="article-image"><%= image_tag article.image_url %></div>
<br>
<li id="article-text"><%= article.text %></li>
<br>
<%= article.created_at %>
<br>
<% if admin_signed_in? %>
<li><%= link_to 'Edit', edit_article_path(article) %></li>
<li><%= link_to 'Destroy', article_path(article),
method: :delete, data: { confirm: 'Are you sure?'} %></li>
<li><%= link_to 'New article', new_article_path %></li>
<% else %>
<li><%= link_to 'Make a Comment', article_path(article) %></li>
</ul>
<% end %>
article.rb
class Article < ActiveRecord::Base
has_many :comments, dependent: :destroy
validates :title, presence: true,
length: { minimum: 5 }
mount_uploader :image, ImageUploader
end
articles controller
def new
#article = Article.new
end
def index
#article = Article.all
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def show
#article = Article.find(params[:id])
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
In your Article model, article.rb, you can set a default_scope like this:
default_scope -> { order('created_at DESC') }
However, this method will sort the articles like this on all pages. If you only want to sort them like this on one action, say your def index, then something like this might work better.
#articles = Article.order('created_at DESC')
Like #ShamsulHaque said in his comment.
Here is a good read about default scopes.
Update
If you prefer to use scopes, like #rich says, then the syntax would look like this:
scope :recent, ->(order = 'desc') { order(created_at: order.to_sym) }
Which you would have the option, in your controller, to call either asc or desc like so:
#articles = Article.recent('asc')
#articles = Article.recent('desc') # although we defaulted to 'desc', so really only need Article.recent
To explain a bit, #rich included the to_sym to convert the string 'desc' or 'asc' to a symbol like :desc or :asc. If you did not do this, you would get an error like
Direction should be :asc or :desc
Hope this helps.
Scopes
Using default_scope is a little taboo (can cause problems) - you'll be much better using a standard scope with a condition:
#app/models/article.rb
Class Article < ActiveRecord::Base
scope :recent, (order = "desc") -> { where created_at: order.to_sym}
end
This will allow you to call:
#article = Article.recent("asc")
A nice extension to #justin's answer ;)
I'm having trouble understanding the best way to make a advanced search form. I have had a good search on the internet, looking at some ways, but I can't get them to work, as most of the suggestions are outdated. I have asked a question already, but I think I was too specific and I wasn't able to fix my problem. I am wanting to search on different text boxes and drop down boxes with one search button.
EDIT2:
projects_controller:
def index
#projects = Project.all
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #projects }
end
end
def search
#project_search = Project.search(params[:search]).order(sort_column + ' ' + sort_direction).paginate(:per_page => 2, :page => params[:page])
end
# GET /projects/1
# GET /projects/1.json
def show
#project = Project.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #project }
end
end
# GET /projects/new
# GET /projects/new.json
def new
#project = Project.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #project }
end
end
# GET /projects/1/edit
def edit
#project = Project.find(params[:id])
end
# POST /projects
# POST /projects.json
def create
#project = Project.new(params[:project])
#project.client = params[:new_client] unless params[:new_client].blank?
#project.exception_pm = params[:new_exception_pm] unless params[:new_exception_pm].blank?
#project.project_owner = params[:new_project_owner] unless params[:new_project_owner].blank?
#project.role = params[:new_role] unless params[:new_role].blank?
#project.industry = params[:new_industry] unless params[:new_industry].blank?
#project.business_div = params[:new_business_div] unless params[:new_business_div].blank?
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render json: #project, status: :created, location: #project }
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# PUT /projects/1
# PUT /projects/1.json
def update
#project = Project.find(params[:id])
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# DELETE /projects/1
# DELETE /projects/1.json
def destroy
#project = Project.find(params[:id])
#project.destroy
respond_to do |format|
format.html { redirect_to projects_url }
format.json { head :no_content }
end
end
private
helper_method :sort_column, :sort_direction
def sort_column
Project.column_names.include?(params[:sort]) ? params[:sort] : "project_name"
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
end
end
Search View:
<h1>Search</h1>
<%= form_tag search_path, method: :get do %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<%= text_field_tag :project_name, params[:project_name] %>
<%= text_field_tag :client, params[:client] %>
<%= submit_tag "Search", name: nil %>
<% end %>
<table class = "pretty">
<table border="1">
<tr>
<th><%= sortable "project_name", "Project name" %> </th>
<th><%= sortable "client", "Client" %></th>
<th>Exception pm</th>
<th>Project owner</th>
<th>Tech</th>
<th>Role</th>
<th>Industry</th>
<th>Financials</th>
<th>Business div</th>
<th>Status</th>
<th>Start date</th>
<th>End date</th>
<% if false %>
<th>Entry date</th>
<th>Edited date</th>
<th>Summary</th>
<th>Lessons learned</tStackh>
<th>Customer benifits</th>
<th>Keywords</th>
<!th></th>
<!th></th>
<!th></th>
<% end %>
</tr>
<% #project_search.each do |t| %>
<tr>
<td><%= t.project_name %></td>
<td><%= t.client %></td>
<td><%= t.exception_pm %></td>
<td><%= t.project_owner %></td>
<td><%= t.tech %></td>
<td><%= t.role %></td>
<td><%= t.industry %></td>
<td><%= t.financials %></td>
<td><%= t.business_div %></td>
<td><%= t.status %></td>
<td><%= t.start_date %></td>
<td><%= t.end_date %></td>
<% if false %>
<td><%= t.entry_date %></td>
<td><%= t.edited_date %></td>
<td><%= t.summary %></td>
<td><%= t.lessons_learned %></td>
<td><%= t.customer_benifits %></td>
<td><%= t.keywords %></td>
<% end %>
<!td><%#= link_to 'Show', project %></td>
<!td><%#= link_to 'Edit', edit_project_path(project) %></td>
<!td><%#= link_to 'Destroy', project, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
<%= will_paginate (#project_search) %>
<%= button_to "Search Again?", search_path, :method => "get" %>
<%# end %>
<%= button_to "Home", projects_path, :method => "get" %>
Project.rb
class Project < ActiveRecord::Base
attr_accessible :business_div, :client, :customer_benifits, :edited_date, :end_date, :entry_date, :exception_pm, :financials, :industry, :keywords, :lessons_learned, :project_name, :project_owner, :role, :start_date, :status, :summary, :tech
validates_presence_of :business_div, :client, :customer_benifits, :end_date, :exception_pm, :financials, :industry, :keywords, :lessons_learned, :project_name, :project_owner, :role, :start_date, :status, :summary, :tech
def self.search search_term
return scoped unless search_term.present?
where find(:all, :conditions => ['project_name OR client LIKE ?', "%#{search_term}%"])
end
end
Routes:
FinalApp::Application.routes.draw do
resources :projects
match "search" => "projects#search", :as => :search
root :to => 'projects#index'
end
As you can see, I'm still a fair bit away from having a finished application. I am trying to make a search form that will be able to search on the following fields: Project name, Client, ID, Industry, Role, Technology, Project Owner, Status, Start Date, End Date, and Keywords. The search form would have either text boxes or drop down menus depending on which field the user was searching for. I am wanting to chain each field and search on them all in one go. Before, I was only using project_name, and client as examples to make it easier for you to understand my code. Hopefully you can see now what I am trying to do.
You can create a new controller called search.
Your search form:
<%= form_tag search_index_path, method: :get do %>
<%= text_field_tag :project, params[:project] %>
<%= text_field_tag :client, params[:client] %>
<%= submit_tag "Search", name: nil %>
<% end %>
incude in your routes.rb:
get "search/index"
your search controller:
def index
#store all the projects that match the name searched
#projects = Project.where("name LIKE ? ", "%#{params[:project]}%")
#store all the clients that match the name searched
#clients = Client.where("name LIKE ? ", "%#{params[:client]}%")
end
Now you can play with #projects and #clients in the index view.
Just be careful, because these variables might became nil if there is no match for the search.
EDIT - I am assuming you have two models Project and Client - if you cannot create a new controller you can create the search action in your current controller.
def search
#store all the projects that match the name searched
#projects = Project.where("name LIKE ? ", "%#{params[:project]}%")
#store all the clients that match the name searched
#clients = Client.where("name LIKE ? ", "%#{params[:client]}%")
end
And than you can use the #projects and #clients in the search view.
If you are trying to display the results in somewhere else (for example index view), you can just move the above to the correct action.
def index
....
#store all the projects that match the name searched
#projects = Project.where("name LIKE ? ", "%#{params[:project]}%")
#store all the clients that match the name searched
#clients = Client.where("name LIKE ? ", "%#{params[:client]}%")
end
EDIT 2 - OK, you are trying to search by a combination of fields in the same model:
You and change your search method to add these two fields:
def self.search(search_project, search_client)
return scoped unless search_project.present? || search_client.present?
where(['project_name LIKE ? AND client LIKE ?', "%#{search_project}%", "%#{search_client}%"])
end
But please note the || will return scope if your search_project OR search_client are not present, you can change for AND (&&) if you prefer.
Also, the AND will return only if both match, I mean the combination of search... You can also change it to OR if you want.
Having the search form:
Your search form:
<%= form_tag search_index_path, method: :get do %>
<%= text_field_tag :project, params[:project] %>
<%= text_field_tag :client, params[:client] %>
<%= submit_tag "Search", name: nil %>
<% end %>
Then your controller must send the combination to the model:
#project_search = Project.search(params[:project], params[:client]).all
I think it will solve the problem...
I've been using MetaSearch in my application and found it quite convenient. If you've already considered it, what problems did you have?
There's also Ransack by the same author, it's a successor to MetaSearch.
A simple explanation can be found in this rails cast
Basically, we have to test if the params contain a specific field and create the filter. See the example below:
def find_products
products = Product.order(:name)
products = products.where("name like ?", "%#{keywords}%") if keywords.present?
products = products.where(category_id: category_id) if category_id.present?
products = products.where("price >= ?", min_price) if min_price.present?
products = products.where("price <= ?", max_price) if max_price.present?
products
end
An alternative is Ransack. Ransack enables the creation of both simple and advanced search forms for your Ruby on Rails application