I have two Rails apps, one client using ActiveResource and one service. I am testing the following command via the console:
User.find(:all, :params => {:email_address => "myemail#domain.com"})
I get back all the records in my user table and not just the one specified in my email parameter.
When I go look at the log for my service app it shows as follows
Started GET "/users.json?email_address=myemail%40domain.com" for 127.0.0.1 at 2011-12-29 11:29:06 -0600
(0.4ms) SHOW search_path
Processing by UsersController#index as JSON
Parameters: {"email_address"=>"myemail#domain.com"}
User Load (0.7ms) SELECT "users".* FROM "users"
Completed 200 OK in 40ms (Views: 35.7ms | ActiveRecord: 3.3ms)
My parameter was not included in the SQL statement.
Any insight?
Based on your comment on the original question, your controller still looks like this:
class UsersController < ActionController::Base
def index
#users = User.all
respond_to do |format|
format.json { render json: #users }
end
end
#other methods down here
end
But in order to get your index view to render just the ones that match the email, you need to update the index method to:
class UsersController < ActionController::Base
def index
#users = User.find_all_by_email_address(email_address)
respond_to do |format|
format.json { render json: #users }
end
end
#other methods down here
end
Conditions are passed to ActiveRecord find method via :conditions option and not :params. So your find call should be like that:
User.find(:all, :conditions => { :email_address => "myemail#domain.com" })
or in more Rails 3 style:
User.where(:email_address => "myemail#domain.com").all
You can find full list of available parameters for find method in docs.
Related
I have a controller​ action which requires to find data from existing database. I am still learning rails and recently came to know about active record queries.
But I am not sure how to use it to fetch record from database in a controller action.
Can anybody please explain this with some example?
Thanks in advance
I found the answer. We can use active record queries to fetch the record from the database in the controller. Example is as follow:
Consider a database 'Dress' which has color attribute. Now I want to fetch all the 'dresses' which have orange color. So the query will be as:
#dresses = Dress.where(color: 'orange')
This will fetch all the record of dresses which have color = orange.
Yes you can used active record query inside controller. But better to put active record query logic inside model only. Because rails follow MVC Architecture
MVC stands for Model, View and Controller. MVC separates application into three components - Model, View and Controller.
Model: Model represents shape of the data and business logic. It maintains the data of the application. Model objects retrieve and store model state in a database. Model is a data and business logic.
View: View is a user interface. View display data using model to the user and also enables them to modify the data.
Controller: Controller handles the user request. Typically, user interact with View, which in-turn raises appropriate URL request, this request will be handled by a controller. The controller renders the appropriate view with the model data as a response.
Example:-
class RoomsController < ApplicationController
before_action :set_room, only: [:show, :edit, :update, :destroy]
# GET /rooms
# GET /rooms.json
def index
#rooms = Room.all
end
# GET /rooms/1
# GET /rooms/1.json
def show
end
# GET /rooms/new
def new
#room = Room.new
end
# GET /rooms/1/edit
def edit
end
# POST /rooms
# POST /rooms.json
def create
#room = Room.new(room_params)
respond_to do |format|
if #room.save
format.html { redirect_to #room, notice: 'Room was successfully created.' }
format.json { render :show, status: :created, location: #room }
else
format.html { render :new }
format.json { render json: #room.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /rooms/1
# PATCH/PUT /rooms/1.json
def update
respond_to do |format|
if #room.update(room_params)
format.html { redirect_to #room, notice: 'Room was successfully updated.' }
format.json { render :show, status: :ok, location: #room }
else
format.html { render :edit }
format.json { render json: #room.errors, status: :unprocessable_entity }
end
end
end
# DELETE /rooms/1
# DELETE /rooms/1.json
def destroy
#room.destroy
respond_to do |format|
format.html { redirect_to rooms_url, notice: 'Room was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_room
#room = Room.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def room_params
params.require(:room).permit(:name, :type, :user_id)
end
end
I have a small blogging app running in Rails 4.1. It lets a user log in and then create, edit, and delete basic posts with a title and body. It all runs though the user interface perfectly.
I'm trying to write a custom rake task (that will later be attached to a chron job) to automatically create posts. Right now I have this:
namespace :blog do
desc "Automatically post to all users accounts"
task auto_post: :environment do
post_title = "Automated Blog Post Title"
post_body = "Hello World!"
Post.create!({:title => post_title,
:body => post_body})
end
end
As best I know it's properly namespaced, etc. and can be run using rake blog:auto_post. The controller for Post looks like this:
class PostsController < ApplicationController
def index
#posts = current_user.posts
end
def new
#post = Post.new
end
def create
#post = Post.new post_params
if #post.save
current_user.posts << #post
flash[:notice] = "New post created!"
redirect_to posts_path
else
render 'new'
end
end
def edit
#post = Post.find params[:id]
end
.....
private
def post_params
params.require(:post).permit(:title, :body)
end
end
As I understand it, I should be able to pass my :title and :body to the Post.new action and have it work. I suspect that I'm not interacting with strong parameters properly. Can anyone help clear this up for me.
EDIT: My psql shows the posts hitting the database so I'm close. Not sure why they're not appearing in the app interface though.
Post.new creates a new object but does not persist it to the database. You need to use Post.create!({:title => post_title, :body => post_body}) instead.
Rake tasks do not call the controller and strong parameters don't come into the picture in a rake task which is run locally. They are there to protect your app from the big, bad Internets.
I'm trying to learn Rails by creating a very simple application which just creates a website where someone can create a list of authors and books with an association that the book is written by an author. I was hoping this would be simple and DRY, but I've been having an unexpected amount of trouble with it.
First looking at my models, I've set up the association and made every data point required (author.name, book.title, and book.author). I do not want to add :author or :author_id to the attr_accessible lists because I want to use the appropriate Rails conventions.
app/models/author.rb:
class Author < ActiveRecord::Base
attr_accessible :name
validates_presence_of :name
has_many :books
end
app/models/book.rb:
class Book < ActiveRecord::Base
attr_accessible :title
validates_presence_of :title
belongs_to :author
validates_associated :author
end
The books controller and view I think is exactly from the scaffolding and very uninteresting. What is interesting is the books controller. Looking at the new method, all I did was add a collector which gets the array of author names with ids to pass to the view. (Honestly, I think I would prefer to not pass the id at all.)
app/controllers/books_controller.rb
# GET /books/new
# GET /books/new.json
def new
#book = Book.new
#authors = Author.all.collect {|a| [a.name, a.id]}
respond_to do |format|
format.html # new.html.erb
format.json { render json: #book }
end
end
Now over to the views, I used the default new.html.haml, but made changes to _form.html.haml. I added a select field using the values in #authors.
app/views/books/_form.html.haml
= form_for #book do |f|
- if #book.errors.any?
#error_explanation
%h2= "#{pluralize(#book.errors.count, "error")} prohibited this book from being saved:"
%ul
- #book.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :author
= f.select(:author, #authors, {:include_blank => ""})
.actions
= f.submit 'Save'
Lastly, back to my controller for the create method. I try to save the basic parameters and create an author association from the selected author.
app/controllers/books_controller.rb
# POST /books
# POST /books.json
def create
#book = Book.new(params[:book])
#book.author = Author.find_by_id(params[:author])
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render json: #book, status: :created, location: #book }
else
format.html { render action: "new" }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
When I click 'Save' I get the following error:
Can't mass-assign protected attributes: author
I understand that this is because the value I selected was put in params[:book] instead of params[:author]. So I have two questions.
1) How do I fix my select statement to have it send in params[:author] instead of params[:book]?
2) Is there an even better way to do this that completely hides the id association?
"Can't mass-assign protected attributes: author" means that your attribute isn't listed as attr_accessible in your model
I think I've mostly figured it out, and as I suspected, I didn't need to change my model code at all.
I changed my controllers new method definition of #authors to only return author names with the following:
#authors = Author.pluck(:name)
This accomplished my goal of hiding the id though it's probably a tad slower when I need to search by name instead of id in the controller create method (below).
Next, I fixed my views to set params[:author] instead of params[:book][:author].
= label :author, :name, 'Author'
= select_tag(:author, options_for_select(#authors), {:include_blank => ""})
Finally, I changed the new methods creation of the #book:
#book = Author.find_by_name(params[:author]).books.create(params[:book])
I'm fairly happy with this. The only thing I don't really like is that the label creates an "author_name" label instead of simply "author".
I made the following models:
class Request < ActiveRecord::Base
end
class UrgentRequest < Request
has_one:note
end
class Note < ActiveRecord::Base
attr_accessible :request_id,....
belongs_to :urgent_request, :foreign_key=>'request_id', :class_name=>'Request'
end
In my controller I've set up an action to create an UrgentRequest object:
def new_scheduled_request
#request = UrgentRequest.new
#request.build_note #<-- getting error here
respond_to do |format|
format.html # new.html.erb
format.json { render json: #request }
end
end
I'm getting the following error:
ActiveRecord::UnknownAttributeError in RequestsController#new_urgent_request
unknown attribute: urgent_request_id
The line number is where I'm invoking the build_note call. The form on the page is supposed to be a nested form. What's going on here and how can I fix it?
Uh never mind I found out the issue. Apparently I had to explicitly mention in the UrgentRequests model in the has_one:note association the foreign key and class name parameters. Works fine now!
Using Rails 3.1.3 with Devise 1.5.3. My app has accounts and users. Each account can have multiple users. A user can have one role, "user" or "account_admin". Devise signup routes to accounts#new. The account and an initial account_admin user are created simultaneously. Got this working as described here (although things have evolved some since then).
An account_admin signs should be able to create additional users in that account. That's where I'm running into trouble: instead of creating new users, it's just redirecting to the user_path (users#show) page with the message "You are already signed in." (user_path is my overridden after_sign_in_path.)
This article asks a similar question. The answer suggests a separate namespace for admins, but since admins are part of my regular app I don't think that applies.
I've defined a complete UsersController. According to the log, GET "/users/new" renders from my "UsersController#new". However POST "/users" is intercepted by Devise and rendered from "Devise::RegistrationsController#create".
config/routes.rb
devise_for :users
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
end
resources :users
resources :accounts
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource # CanCan
...
def new
# CanCan: #user = User.new
end
def create
# CanCan: #user = User.new(params[:user])
#user.skip_confirmation! # confirm immediately--don't require email confirmation
if #user.save
flash[:success] = "User added and activated."
redirect_to users_path # list of all users
else
render 'new'
end
end
...
end
I've tried overriding the Devise controller, thinking I could tell it to use my users#create action if the user is already signed in. The log tells me it is using my controller ("Processing by RegistrationsController#create as HTML"), but it doesn't seem to execute its code. I've commented out my custom actions and just left in the logger lines, but I don't get my logging messages. And in spite of super being commented out, the behavior doesn't change--it still redirects with "You are already signed in."
config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
logger.info "Custom RegistrationsController: new"
super
end
def create
logger.info "Custom RegistrationsController: create"
# super unless user_signed_in?
# render "users#create"
end
def update
super
end
end
What am I missing? How can I let the account_admin user create additional users?
The main issue is that Devise was intercepting the POST "/users" (and a few other routes). Found this workaround to allow my Users controller handle those routes: change the devise_for to skip registrations, then add back in the routes for which Devise normally defines aliases:
routes.rb
devise_for :users, :skip => [:registrations]
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
get "cancel_user_registration", :to => "devise/registrations#cancel"
post "user_registration", :to => "users#create"
get "new_user_registration", :to => "accounts#new"
get "edit_user_registration", :to => "users#edit"
end
resources :users
resources :accounts
Never figured out why the Devise controller override wasn't working.
A user on this thread pointed out the devise_invitable gem which might be an interesting alternative.
This is how I ended up getting this to work with my setup (Devise tied to my User model -- no separate Editor/Admin model):
class UsermakersController < ApplicationController
before_action :authenticate_user!
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_path, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render users_path }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
userParams = params.require(:user).permit(:name, :email)
end
end
From here, when you want to add a new User as another User, simply call the Usermakers#new url in your form, e.g., I have this at the bottom of my Users index page:
<%= link_to 'Add User', new_usermaker_path, id: 'new_user_link' %>
And the Usermakers form looks like:
= simple_form_for #user, url: usermakers_path, html: {multipart: true} do |f|
=f.error_notification errors: true
= f.input :name
= f.input :email
%p
= f.button :submit, 'Create User', class: 'button'
Of course, you'd need to add a dummy new.html.erb file which simply renders _form.html.erb to use this.
Just add the new and create methods to your routes.rb file (whether by resources :usermakers, or more specific routes) and you should be good to go. Hope this helps!