I ran through railscast #37 and can't seem to even get my search form to display on my index page. Any ideas whats wrong with my code? thanks in advance! any help is greatly appreciated.
Here is the index page where the form is located:
<h1>All users</h1>
<% form_tag users_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
Heres the controller:
class UsersController < ApplicationController
...
def index
#title = "All users"
#users = User.paginate(:page => params[:page])
#usersearch = User.search(params[:search])
end
...
Lastly, the user.rb file:
...
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
all
end
end
I didn't look further, but there must be an equal sign in form_tag in the recent Rails versions. You should also get an error message on that in development mode.
Try <%= form_tag users_path, :method => 'get' do %>
I saw your previous question. It was deleted while I was submitting my answer. This looks similar to that question, so here is my answer to the previous question and this one.
I searched the users based on their country. So this is what I did for that:
First I created a new column country based on what I'm going to search. The command is
$ rails generate migration add_country_to_users country:string
$ rake db:migrate
Add this string to the attr_accessible in the user model as follows:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation, :country
validates_presence_of :country
..........
attr_accessible lets you do a mass assignment. Don't forget to add the new column here.
Once this is done you are now ready to write a controller function. I named mine network because I want to display users based on country. My search word is going to be country and I will display all the users belonging to one particular country. The function definition is as follows:
class UsersController < ApplicationController
def network
#title = "All users"
#users = User.find_all_by_country(User.find(params[:id]).country)
end
end
For your question if you want to display users by name make it
User.find_all_by_name(User.find(params[:id]).name)
or something like that based on our search word.
Now coming to the display. I created a new page under views/users/ as network.html.erb as I want to display a network of users belonging to a country. First have one form from where you will give the input, i.e where you invoke the search. For mine I have a link in the header of my form. The link is as follows:
<li><%= link_to "Network", network_user_path(current_user) %></li>
Once user clicks this the following form will be displayed:
<h1>All users</h1>
<ul class="users">
<% #users.each do |user| %>
<li>
<%= gravatar_for user, :size => 30 %>
<%= link_to user.name, user %>
</li>
<% end %>
</ul>
So far, so good. Now the final step is to connect them by adding them in the routes.rb file. Add the following in that file:
MyApp::Application.routes.draw do
resources :users do
member do
get :following, :followers, :network, :news
end
end
end
So this is what I did for my application and it worked. I hope it helps.
Related
I'm building a rails app, and I'm having some trouble getting the search function and filter option to work in unison.
#app/models/raffle.rb
class Raffle < ApplicationRecord
has_many :tickets
has_many :users, through: :tickets
def self.filter(filter)
if filter
raffle = Raffle.where(category: filter)
else
Raffle.all
end
end
def self.search(key)
if key.nil?
raffle = Raffle.where(product_name: search)
else
keys = key.split('+')
Raffle.where((['product_name LIKE ?'] * keys.size).join(' OR '), *keys.map{ |key| "%#{key}%" })
end
end
end
#app/controllers/raffles_controller.rb
def index
#raffles = Raffle.filter(params[:filter]) #something for search functionality here
end
#app/views/raffles/index.html.erb
<%= form_tag(raffles_path, method: :get) do %>
<%= text_field :search, params[:search]%>
<%= submit_tag ("Search") %>
<% end %>
<%= form_tag(raffles_path, method: 'get') do %>
<%= select_tag(:filter, options_for_select(Raffle.pluck(:category).uniq, params[:filter])) %>
<%= submit_tag ("Filter") %>
<% end %>
I'm certain the issue lies in the controller, since I've tried #raffles = Raffle.filter(params[:filter]).search(params[:search]), but I get an error 'wrong number of arguments (given 0, expected 1)' being triggered by the line def self.search(key)
Any help greatly appreciated!
Here's an excerpt from your code.
def self.search(key)
if key.nil?
raffle = Raffle.where(product_name: search)
Once execution reaches the end of it, it calls product_name: search. search is the name of the method you've defined that requires one argument and you're passing none. I assume you wanted to call something else here.
If you would have looked at the stack trace you would have gotten it yourself.
I don't even know what to search for on this, so I'll just explain what I'm trying to do. On my user index page I'm printing out a list of the latest users with the immediate info (stuff from the users table). How do I go about pulling the latest post by that user, then? I don't know what to do in the controller that will enable me to have access to each result separately.
Here is a simplified view:
<% #users.each do |user| %>
<%= link_to user.username, user_path(user) %><br />
<%= user.email %>
# this is where I would show the user's latest post
<% end %>
controller:
def index
#users = User.order('created_at DESC').paginate(:page => params[:page], :per_page => 24)
#user_latest_post = ???
end
You should set up User so that it has an association with Post. This allows you to access all Posts belonging to that User (like user.posts). This isn't totally necessary for your question, but is probably something you want (or have already done).
Then you need to create a second, scoped association that gets the latest Post. This way you can use includes in your controller and avoid an N+1 issue (otherwise it will do a new query for the latest post every time you iterate through a user with that each block).
Lastly, you need to choose what you want to display about the Post. Convention would have you make a shared "partial" view for Post that you can reuse. This means you can just tell Rails to render user.latest_post and it will know what to do (assuming you've defined this partial).
I give code examples below to explain what I mean:
# models/user.rb
class User < ApplicationRecord
has_many :posts
has_one :latest_post, class_name: "Post", -> { order(created_at: :desc).limit(1) }
end
# controllers/users_controller.rb
def index
#users = User.includes(:latest_post).order(created_at: :desc).paginate(:page => params[:page], :per_page => 24)
end
# views/users/index.erb
<% #users.each do |user| %>
<%= link_to user.username, user_path(user) %><br />
<%= user.email %>
<%= render user.latest_post %>
<% end %>
# views/posts/_post.erb
<%= post.text %>
# or whatever you want here
I am relatively new to Rails, I am trying to create a cookbook app/site from scratch, I'm not following any tutorials or anything like that. Anyway... I am still in the early stages of the site but I am now to the part where I am wanting to display a index list of all the different kinds of recipes. But I want to filter the list, for example:
If I click on the 'vegetables' button on the navigation bar I would like to be take to an index page that only displays the different vegetable recipes.
I've gone ahead and added a string attribute to Recipes, called "category" thus I will be able to differentiate between Meat, Seafood, Poultry, Appetizers and Vegetable recipes. My goal is to only need the one controller 'recipes' and in the index action be able to conditionally filter on a param. Thus filtering the list by 'category' of food. But I am unsure how to go about doing so.
Here is my RecipesController:
class RecipesController < ApplicationController
def index
#recipes = Recipe.all
end
def show
#recipe = Recipe.find(params[:id])
end
def new
end
def edit
end
end
Here is my Routes file:
Rails.application.routes.draw do
resources :recipes
get 'vegetables' => 'recipes#vegetables'
get 'poultry' => 'recipes#poultry'
get 'meat' => 'recipes#meat'
get 'seafood' => 'recipes#seafood'
get 'appetizers' => 'recipes#appetizers'
devise_for :users
get 'about' => 'welcome#about'
root to: 'welcome#index'
end
Here is the application layout file that contains the navigation bar:
<!DOCTYPE html>
<html>
<head>
<title>Mrs. P's Cookbook</title>
<link href='https://fonts.googleapis.com/css?family=Mate+SC' rel='stylesheet' type='text/css'>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<div class="top-banner">
<h1 class="banner-logo"> <%= link_to "Mrs. P's Cookbook", root_path %></h1>
<nav>
<%= link_to "POULTRY", poultry_path, class: "nav-link" %> |
<%= link_to "MEAT", meat_path, class: "nav-link" %> |
<%= link_to "SEAFOOD", seafood_path, class: "nav-link" %> |
<%= link_to "APPETIZERS", appetizers_path, class: "nav-link" %> |
<%= link_to "VEGETABLES", vegetables_path, class: "nav-link" %> |
<%= link_to "ABOUT", about_path, class: "nav-link" %>
</nav>
<% if current_user %>
Hello <%= current_user.email %>! <%= link_to "Sign out", destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to "Sign In", new_user_session_path %> or
<%= link_to "Sign Up", new_user_registration_path %>
<% end %>
</div>
<div class="white-field">
<%= yield %>
</div>
</body>
</html>
Here is my Recipe.rb model file:
class Recipe < ActiveRecord::Base
has_many :comments
end
Here is my Recipe table:
create_table "recipes", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "category"
end
I've got the different category views, Vegetables, Meat, Poultry, Seafood and Appetizers all inside the Recipe view folder. All of the views are empty except for some text that just says "Vegetable recipes will be listed here.", "Meat recipes will be listed here.", "Seafood recipes will be listed here.", etc..
I know what I am asking might be a tall order, so any help you guys can provide I will be extremely grateful for. If you need anymore information please let me know.
I came up with two possible solutions (among many! :) )
Solution 1
One solution could be using a query string. The routes file would be
Rails.application.routes.draw do
resources :recipes
devise_for :users
get 'about' => 'welcome#about'
root to: 'welcome#index'
end
Now create the links with the category as argument. In your view:
...
<nav>
['poultry','meat','seafood','appetizers','vegetables'].each do |category|
<%= link_to category.upcase,
recipes_path(category: category),
class: "nav-link" %> |
end
<%= link_to "ABOUT", about_path, class: "nav-link" %>
</nav>
...
And in the controller you can capture the category, querying the model as already showed in the other answers. For example
class RecipesController < ApplicationController
def index
#recipes = params[:category].blank? : Recipe.all :
Recipe.where(category: params[:category])
end
...
end
But isn't that elegant if you want to customize the view of a single category.
Solution 2
Add the following route, (before the resources :recipes )
Rails.application.routes.draw do
get 'recipes/:category' => 'recipes#category', as: :recipes_category
resources :recipes
...
end
Note that with the as: :recipes_category you can reference to this path using the recipes_category_path helper
Now we need to add the category action to the RecipesController
class RecipesController < ApplicationController
...
def category
#recipes = Recipe.where(category: params[:category])
end
...
end
And the the path in the link_to will change to
...
<%= link_to category.upcase,
recipes_category_path(category: category),
class: "nav-link" %> |
...
IMHO that's cleaner if you want to customize your view without caring the presence of the param and the url looks prettier :).
Looking forward
As already suggested by #denys281, the category field smells like a separate entity, related with the recipes with a N:1 relation.
The two model will have the associations
class Recipe < ActiveRecord::Base
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :recipes
end
In that way, for example, you can easily have all the categories by calling
Category.all
or the recipes for a category with
#category = Category.find(2)
#recipes = #category.recipes
It really depends on what you want to achieve. I hope I gave you some ideas on the argument.
Regarding the other suggestion of using Slim, I disagree. It's just syntactic sugar, it's not standard and it will confuse you: the most of the code you will find around will have the standard syntax.
Get your hands dirty before using some other tools, it will give you a better idea of the mechanisms behind, a better understanding if the tool will solve your needs and how you can customize it.
I suggest you to read the official rails guide, the countless blogs and articles and try to read code written by others ( thanks Open Source! ).
Use a named scope like this
class Recipe < ActiveRecord::Base
scope :by_category, lambda {|cat|
where(category: cat)
}
...
end
Then you can call
Recipe.by_category("Vegetables")
You can also do:
scope :vegetables, lambda {
where(category: "Vegetables")
}
and then call
Recipe.vegetables
I think you need use some tutorial, in this way you will see best practices and so on. Also I think you should read some good book about relational databases.
Use slim ;
Create Category table. Recipe belongs_to :category. Category has_many :recipes. Check association basics ;
How to get recipes you can look at Active Record Query Interface (Something like Recipe.where(category_id: params[:id])) ;
Also check about partials
Your routes should coincide with a function in your controller so when you have a route to 'recipes#meat', your controller would be looking for a 'meat' function and a 'meat' view
I would remove those routes and have one called
'get recipe/:recipe'
and pass that parameter(the string) to the show function.
This way you could access a specific recipe like so:
#recipes = Recipe.where("category = ?", params[:recipe])
I use Devise gem for authentication.
In database I have users table and posts table in my database schema (and Post controller).
In post controller I want to find all posts assigned to specific user. I have user_id in posts table.
How to get all user's posts or how to check if specific post is assigned for SIGNED IN user.
I thought about something like this (of course is only pseudocode:
current_user.id == Post.where(params:[post_id]).user_id
So how to get current user id in Devise and how to check the current user id is the same like eg. user_id assigned to viewing post (I want to add 'edit' function when current user is post owner) and how to find all post which current user is owner.
Associations
Firstly, your user_id column in your posts table is what's known as a foreign_key
Foreign keys are used in relational database systems to give you the ability to call associative data from a single record. Simply, it means that you'll be able to use the ActiveRecord associations to call the data you require, rather than having to call it individually:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
end
#app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
This will give you the ability to use the following call:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
#posts = current_user.posts
end
end
You'll be best served looking up the has_many association:
Fix
In regards to showing your posts for your users, you need to be sure that you have the correct "flow" set up. What I mean is you need some condition to know whether your user is signed in & that #posts is set:
#app/views/posts/index.html.erb
<% if #posts.present? %>
<% #posts.each do |post| %>
<%= post.title %>
<% end %>
<% end %>
Maybe this is the first time you use Devise. You can access current_user inside controllers or views. I imagine you could do something like this
In controller (posts_controller.rb):
#posts = current_user.posts
In view (posts/show.html.erb, I guess):
if current_user.id = #post.current_user
#render something here
end
Get all post which current user is owner.
#posts = Post.where(:user_id => current_user.id)
and on your view
<%-# commented : checking if #posts is empty -%>
<% if #posts.empty? %>
<span>Sorry, post is empty </span>
<% else %>
<%= #posts.each do |p| %>
<% if p.user_id == current_user.id %>
<% link_to "edit", edit_path(p) %>
<% end %>
<% end %>
<% end %>
There are many ways you could get current_user posts. I'll go the long way.
we need
an action
an action view and a partial
a route
a link_to
* action *
def my_posts
#posts = current_user.posts.all.order(created_at: 'DESC')
end
* view *
my_posts.html.erb
<% if #posts.present? %>
<%= render 'posts' posts: #posts %>
<% else %>
<h1>You don't have any posts yet! create one</h1>
<% end %>
_posts.html.erb
<%posts.each do |post| %>
<%= post.title %>
<% end %>
index.html.erb
<%= render 'posts' posts: #posts %>
route
get 'post' => 'posts#my_posts', as: :my_posts
link_to
<%= link_to 'My posts', my_posts_path %>
I may be late but someone can find it useful :)
So I have two models, Users that belong_to Organization (which has_many users). I'm using a partial to display all the users that belong to any one particular organization, and this is working fine if I output just the name (it correctly names all the users). However when I change the partial code to provide link_to user.name, the returned links are all links to the parent Organization rather than the individual child objects. What am I doing wrong?
Relevant code:
Organizations Controller
def show
#organization = Organization.find(params[:id])
#users_index = User.joins(:organization).where(organizations: {id: #organization.id})
end
Organization.show.html.erb
<% provide(:title, #organization.organization_name) %>
<h1><%= #organization.organization_name %></h1>
<h2>Your Organization's Users:</h2>
<%= render partial: "users_index", collection: #users_index %>
_users_index.html.erb code:
<p class="users"><%= link_to users_index.name %></p>
If you set up your relationship properly then you can use:
#users_index = #organization.users
And then you need to loop through #users_index and pass that to your partial.
#users_index.each do |user|
<%= render "users_index", :user => user %>
end
And in your partial, change to:
<p class="users"><%= link_to user.name, user %></p>
link_to
I think the core of the issue, as alluded to in the other answer, is your link_to:
<%= link_to users_index.name %>
The link_to helper method basically works like this:
<%= link_to "Your Link Text", link_path %>
I don't see how your application will be able to load the correct link path without your definition, irrespective of whether you added it to the helper or not.
I presume that your rendered link will point to "#", not the "parent object" as you alluded.
--
Fix
I'd do this:
#app/views/organization/show.html.erb
<%= render partial: "users_index", collection: #users_index, as: :user %>
#app/views/organization/_users_index.html.erb
<p class="users"><%= link_to user.name, user %></p>
This should set the correct link_to helper method for you
--
Models
Further, I would also address the associations in your models
You're currently calling users in the most inefficient way. ActiveRecord associations work to remedy this:
#app/models/user.rb
Class User < ActiveRecord::Base
belongs_to :organization
end
#app/models/organization.rb
Class Organization < ActiveRecord::Base
has_many :users
end
This will allow you to call the following:
#app/controllers/organizations_controller.rb
Class OrganizationsController < ApplicationController
def show
#organization = Organization.find params[:id]
#users_index = #organization.users
end
end