I have a Slider model in my project and it has a lot of polymorphic associations with other model like Product, Manufacturer, Article and etc.
So, when I use 'show' action with one of the models I also show related Slider. It's ok. But sometimes I need to show Slider with 'index' action.
What is the best way to link some of the sliders to actions, not to other models?
UPDATE
routes
resources :products, :articles, :industries, :manufacturers, only: [:index, :show]
Product controller
class ProductsController < ApplicationController
load_resource
# GET /products
# GET /products.json
def index
#catalog = Product.by_type_and_manufacturer
end
# GET /products/1
# GET /products/1.json
def show
#page_slider = #product.slider
end
end
So in 'show' action I just use product.slider to get related Slider instance. But I want to show another slider for all products by index action.
In that case, what you're trying to do is not possible. You cannot create a relation to a controller action. What you need to do is link the relation's controller action, rather than trying to create a relation to the controller action. A model can only be related to another model (you cannot has_many index, show, delete, etc...)- In other words, call up the data for the relation, and link to that relation's controller action in the view.
example:
#Models:
class Page < ActiveRecord::Base
has_many :sliders
end
class Slider < ActiveRecord::Base
belongs_to :page
end
#Controllers
class PagesController < ApplicationController
def index
#pages = Page.all # lists all pages
end
def show
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
end
# if you would like to show a page that just has all sliders for a specific page and not the page itself...
def show_page_sliders # you will have to create a route and view for this manually
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
# note that this controller action is identical to the show action, because the data we're going to need is the same- the difference comes in the view
end
end
class SlidersController < ApplicationController
def index
#sliders = Slider.all
end
def show
#slider = Slider.find(params[:id])
end
end
# Views
# page#index
<% #pages.each do |p| %>
...
page listing code goes here. if you want to list the sliders for each page on the index...
<% p.sliders.each do |s| %>
...
individual slider info goes here
...
<% end %>
...
<% end %>
# pages#show
<%= #page.name %>
<%= #page.content %> <!-- or whatever data you have for page -->
# since here we are showing a singular page, we can just use our #page instance variable to list out the sliders
<% #page.sliders do |s| %>
...
Slider listing code goes here
...
<% end %>
# pages#show_sliders
<!-- this is identical to the page#show view, minus the actual page info, and with the addition of a link back to the parent page -->
<%= link_to "Back to page", page(s.page_id) %>
<% #page.sliders do |s| %>
...
Slider listing code goes here
<!-- you can link to any path from the slider listing -->
<%= link_to "Show", slider(s.id) %>
<%= link_to "Edit", edit_slider_path(s.id) %>
<%= link_to "Delete", delete_slider_path(s.id) %>
...
<% end %>
#######################UPDATE#############################
# to define one slider per controller action
class PagesController < ApplicationController
def index
#pages = Page.all
# you need to add a "controller_action" column to your Slider model
#slider = Slider.find_where(controller_action: "pages#index")
end
def show
#page = Page.find(params[:id])
#slider = Slider.find_where(controller_action: "pages#show")
end
# etc ...
Related
I'm new to rails, and I've already learnt how to do CRUD using scaffold and using resource, I wanna know how to Do CRUD without using resource, However what I want to do is create custom methods for CRUD in the controller that will be like the traditional CRUD model. Please help me with this.
Actually, for the action index / new / show / create / edit / update / destroy, this is a convention in Ruby On Rails
If I'm right and if you're trying to change their name using resources in routes.rb (Changing by exemple the new action into def my_restaurant), Rails will render
Unknown action The action 'new' could not be found for
RestaurantsController
Netherless, you can create some methods to perform some particular action into the index, and add them in the "private section" you want to hide them to the public. There is no routes attach to this method.
class RestaurantsController < ApplicationController
def index
#restautants = Restaurant.all
#restaurants.sort_by_name
end
private
def sort_by_name
#some action here
end
end
If you want to create your own name method, you can personnalise it but you can't use resources "shortcut" in the routes.rb
by exemple :
#reviews_controller.rb
class ReviewsController < AplicationController
def index
#reviews = Reviews.all
end
def update
#review = Review.find(review_params[:id])
end
def history
#some action
end
private
def review_params
params.require(:review).permit(:liked, :comment, :id)
end
end
Then add a view
#app/views/reviews/history.html.erb
Don't forget the routes :
Rails.application.routes.draw do
resources :reviews, only: [:index, :update] do
collection do
get :history
end
end
end
I hope this will help you and complete the previous answer.
as for your second question :
I still do have one doubt tho..... Will <%= form_for #post do |f| %>
this form be enough for new_post and edit_post, will it automatically
identify them? If that's not enough can you please tell me the what
comes at new_post.html.erb and edit_post.html.erb....Thanks again for
the help.
If the form is the same for your new_post and edit_post, i may suggest you to put into a partial file. (For this example I used simple_form gem)
#app/views/posts/_form.html.erb
<%= simple_form_for(#post) do |f| %>
<%= f.input :name, label: "post name" %>
<%= f.input :photo, as: :file %>
<%= f.submit "Save", class:"btn btn-small btn-success" %>
<% end %>
and then render the partial in your views new file.
#app/views/posts/new.html.erb
<div>
<h1>New Post</h1>
</div>
<%= render "form" %>
Well I hope I could help you with this answer.
Do not hesitate too to read ruby documention. You may find more information that you're looking for too.
My answer may be redundant but it's the better way for me to clearly explain it...
In oder to use your own custom methods you need to create them in your controller, setup the route and if needed create an view.
# PostsController
def create_post
# Do your create stuff here
end
def read_post
# Do your read stuff here
end
def update_post
# Do your update stuff here
end
def delete_post
# Do your delete stuff here
end
# routes
post '/create_post', to: 'posts#create_post'
get '/read_post/:id', to: 'posts#read_post'
put '/update_post/:id', to: 'posts#update_post'
delete 'delete_post/:id', to: 'posts#delete_post'
With the controller and routes setup you will only need a view for the read_post method. The best way to do that is create the file: views/posts/read_post.html.erb
There is 7 CRUD routes to Create, Read, Update and Delete.
Rails.application.routes.draw do
get "restaurants", to: "restaurants#index"
get "restaurants/new", to: "restaurants#new", as: :new_restaurant
post "restaurants", to: "restaurants#create"
# NB: The `show` route needs to be *after* `new` route.
get "restaurants/:id", to: "restaurants#show", as: :restaurant
get "restaurants/:id/edit", to: "restaurants#edit", as: :edit_restaurant
patch "restaurants/:id", to: "restaurants#update"
delete "restaurants/:id", to: "restaurants#destroy"
end
So once the route create, you can create in the controller, the action that you need
class RestaurantsController < ApplicationController
def index
#restaurants = Restaurant.all
end
end
and the view
app/views/restaurants/index.html.erb.
The best practice is to create only the routes that you will need and to work in silos.
1 route, 1 controller action, 1 view.
For your context: This is my first attempt to create a app. I have just started coding:-).
I am trying to get a simple CRUD setup to work.
Now i'm having two problems i can't get my head around:
My entries don't show up on my index page. it gives te following error: 'undefined method `title' for nil:NilClass'. The model contains the following columns:
string :title,text :forecast, date :review_date
If i go to decisions/edit it gives me the following error: 'Couldn't find Decision with 'id'=edit'
This is my code:
Controller:
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update]
def index
# gets all rows from decision table and puts it in #decision variable
#decisions = Decision.all
end
def show
# find only the decision entry that has the id defined in params[:id]
#decision = Decision.find(params["id"])
end
# shows the form for creating a entry
def new
#decision = Decision.new
end
# creates the entry
def create
#decision = Decision.new(decision_params)
if #decision.save
redirect_to #decision
else
render 'new'
end
end
# shows the form for editing a entry
def edit
#decision = Decision.find(params["id"])
end
# updates the entry
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find(params["id"])
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
index view
<h1>Hello World ^^</h1>
<% #decisions.each do |descision| %>
<p><%= #decision.title %></p>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :decisions
root 'decisions#index'
end
I have been working on these two all morning but i can't figure it out. I would be a great help if you guys can take a look for me.
I have just started coding
Welcome!!
My entries don't show up on my index page.
I'm sure you mean decisions, right?
If so, you have to remember that if you're calling a loop in Ruby, you'll need some conditional logic to determine if it's actually populated with any data before trying to invoke it:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |decision| %>
<%= content_tag :p, decision.title %>
<% end %>
<% end %>
This will have to be matched by the appropriate controller code:
#app/controllers/decisions_controller.rb
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update, :destroy]
def index
#decisions = Decision.all
end
def show
end
def new
#decision = Decision.new
end
def create
#decision = Decision.new decision_params
#decision.save ? redirect_to(#decision) : render('new')
end
def edit
end
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find params["id"]
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
This will give you the ability to call #decisions and #decision in your views depending on which route you're accessing.
An important point is that when you say...
decisions/edit it gives me the following error: Couldn't find Decision with 'id'=edit'
... the issue is caused by the way in which Rails routing is handled:
Because Ruby/Rails is object orientated, each set of routes corresponds to either a collection of objects, or a member object. This is why routes such as edit require an "id" to be passed - they're designed to work on member objects.
As such, when you access any "member" route (decisions/:id, decisions/:id/edit), you'll have to provide an id so that Rails can pull the appropriate record from the db:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |descision| %>
<%= link_to "Edit #{decision.title}", decision_edit_path(decision) %>
<% end %>
<% end %>
I can explain a lot more - the above should work for you for now.
I've an issue with the paths in the views and I don't know how to solve it.
I've "categories" that has_many "posts" and "posts" that belongs_to "categories".
1.- I want to show on home page the truncate last post of an specific category (the ID number "1"). Then I want that post to link to the show post path but I get this error:
"Unknow Action
The action 'index' could not be found for PostsController"
I think I've my paths wrong because I don't need the index view because I'm only going to show that specific post. So, I think that category_posts_path(#last_post) is not the right path (I don't know where to look for more info about making the route path in the views...). Actually, the browser is showing me that is looking for the "2" category when it is a post of the "1" category...? What am I doing wrong?
This is the browser route:
http://localhost:3000/en/categories/2/posts
This is my views/categories/home.html.erb file:
<div class="post_details">
<h2><%= #last_post.title %></h2>
<%= image_tag #last_post.image(:header), class: "post_image" %>
<p><%= truncate #last_post.body, length: 100 %></p>
<p class="button"><%= link_to "READ MORE", category_posts_path(#last_post) %></p>
</div>
2.- I have another path problem in the views/categories/show.html.erb file. I have a loop to show all the post of one specific category, but when I link in some post (to show it) there is the "index" error again:
"Unknow action
The action 'index' could not be found for PostsController"
This is the browser route:
http://localhost:3000/en/categories/1/posts
This is the views/categories/show.html.erb file:
<div class="post_details">
<h2><%= link_to post.title, category_posts_path(post) %></h2>
<%= image_tag post.image(:header), class: "post_image" %>
<p><%= post.body %></p>
</div>
This is the categories_controller.rb file:
class CategoriesController < ApplicationController
before_action :get_categories
def index
end
def show
#category = Category.find(params[:id])
end
def home
if params[:set_locale]
redirect_to root_url(locale: params[:set_locale])
else
#category = Category.find_by_id(1)
#last_post = #category.posts.order("created_at desc").first
end
end
def get_categories
#categories = Category.all.order("rank asc, name asc")
end
end
This is my posts_controller.rb file:
class PostsController < ApplicationController
def show
#category = Category.find(params[:category_id])
#post = #category.posts.find(params[:id])
end
end
This is my route.rb file:
scope '(:locale)' do
resources :categories do
resources :posts
end
resources :contacts
root 'categories#home'
get "/contact" => "contacts#new"
# static pages
get "/investment" => "contents#investment"
get "/partner-with-us" => "contents#partner", as: "partner"
get "/our-companies" => "contents#companies", as: "companies"
get "/site-map" => "contents#sitemap", as: "sitemap"
get "/terms-and-conditions" => "contents#terms", as: "terms"
get "/privacy" => "contents#privacy"
end
When you are nesting routes you should always consider what is the parent and whats a child in given route. Since your paths don't know anything about your associations you have to explicitly define every object in the nesting.
I.e. since you nested posts in categories linking to last post in given category would look like this:
category_post_path(#category, #last_post)
(I think you have also a typo there - category_posts_paths - which links to posts index index - hence the error. Use category_post_path. instead, and give it both parent category and the post.
You can run rake routes to see exact information on paths (or go to http://localhost:3000/rails/info/routes )
I am working on a rails 4 application that currently has two models User and Status. In the user model I defined the association below. Both the status and user tables are populating with information. Statuses are loading with an associated user_id
User Model
class Status < ActiveRecord::Base
belongs_to :user
end
I have the following block in my show status view which will display the user_id and and the content of the status
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user_id%></strong>
<p> <%=status.content%></p>
I would like to display the user's first name instead. According the tutorial i'm taking I should be able to use this code since I have the association defined however it's returning the error below.
<%=#status.user.first_name%>
Error
#==>undefined method `first_name' for nil:NilClass
How can I display first_name in the controller? Do I need to define a new method for user or should the association provide?
Relevant Controller Code for Reference
class StatusesController < ApplicationController
before_action :set_status,:set_user, only: [:show, :edit, :update, :destroy]
# GET /statuses
# GET /statuses.json
def index
#statuses = Status.all
end
# GET /statuses/1
# GET /statuses/1.json
def show
puts "debug msg #{#status.inspect}"
end
# GET /statuses/new
def new
#status = Status.new
end
# GET /statuses/1/edit
def edit
end
# POST /statuses
# POST /statuses.json
...
...
...
private
# Use callbacks to share common setup or constraints between actions.
def set_status
#status = Status.find(params[:id])
puts "in set status"
end
def set_user
#status.user = User.find_by(#status.user_id)
end
# Never trust parameters from the scary internet, only allow the white list through.
def status_params
params.require(:status).permit(:content, :user_id)
end
end
Sees like there is no problem in your code. The error undefined method first_name for nil:NilClass means that the status object not associated with user or user have no field first_name. Try following code:
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user.try(:first_name) %></strong>
<p> <%=status.content%></p>
I am not sure what page you are trying to display <%=#status.user.first_name%> this on, but this should work.
You can use the will_paginate gem:
def show
#statuses = #statuses.paginate(page: params[:page])
end
add this to the view:
<%= will_paginate %>
or this should be the normal way:
def show
#statuses = #statuses.find(params[:id])
end
I have 2-column layout. Some controller has left column, some not. What is the best way to render it depend on controller? Now, it look likes that:
<% if params[:controller] != 'page' %>
<div id="navigation" class="l"><%= render "layouts/left-menu" %></div>
<% end %>
It's bad, bad monkey code.
Edit: Changed my solution, OP wanted condition to depend on action and controller.
In your base helper, define a method like this:
# app/helpers/application_helper.rb
module ApplicationHelper
def has_left_menu?
#has_left_menu.nil? ?
true : # <= default, change to preference
#has_left_menu
end
end
In your application controller:
# app/controllers/application_controller.rb
class ApplicationController
def enable_left_menu!
#has_left_menu = true
end
def disable_left_menu!
#has_left_menu = false
end
end
In your view or layout, change your check to this:
<% if has_left_menu? %>
<div id="navigation" class="l"><%= render "layouts/left-menu" %></div>
<% end %>
Now you can disable/enable the left menu in before_filters or anywhere else in your action:
class UsersController < ApplicationController
# enable left menu for "index" action in this controller
before_filter :enable_left_menu!, :only => [:index]
# disable left menu for all actions in this controller
before_filter :disable_left_menu!
def index
# dynamic left menu status based on some logic
disable_left_menu! if params[:left_menu] == 'false'
end
end
In your controller you use layout like this
#PublicController is just an example
class PublicController < ApplicationController
layout "left-menu"
end
And in the views/layouts folder you put the left-menu.html.erb
with a stylesheet_link_tag to your spesific css file
<%= stylesheet_link_tag 'left-menu' %>
You can learn more at rails guides