Rails - Save client model into the database - html

Hey here! I'm kinda new to Rails and I've been trying to find some answers but no luck yet so here we go.
I've set up a basic Rails app and just trying to save a Client to my database with a validation but nothing seems to be coming together. Anyone could point me to the right direction please or let me know what I've been doing wrong in my code.
I keep getting errors like this:
NoMethodError in Clients#new
Showing /Users/******/Documents/******/*****/app/views/clients/_form.html.erb where line #1 raised:
undefined method `clients_path' for #<ActionView::Base:0x00000000064a50>
Did you mean? clients_new_path
Even if I remove #client = Client.new from the new method I can view the page but nothing gets saved.
I'm stuck really now so any help much appreciated!
Thanks!
My Routes.rb file:
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
get 'dashboard/index'
root to: "home#index"
devise_for :users, controllers: {
sessions: 'users/sessions',
passwords: 'users/passwords',
registrations: 'users/registrations'
}
get '/clients/index'
get '/clients/new'
get '/clients/edit'
get '/clients/delete'
get '/clients/:id', to: 'clients#show'
post '/clients/new', to: 'clients#create'
end
My Dashboard file:
<% if user_signed_in? %>
<nav class="subnav">
<ul>
<li><%= link_to('My Clients', clients_index_path) %></li>
<li><%= link_to('Add Client', clients_new_path) %></li>
<li><%= link_to('Edit registration', edit_user_registration_path) %></li>
<li><%= link_to('Logout', destroy_user_session_path, :method => :delete) %></li>
</ul>
</nav>
<% else %>
<%= link_to('Register', new_user_registration_path) %>
<%= link_to('Login', new_user_session_path) %>
<% end %>
My ClientsController file:
class ClientsController < ApplicationController
def index
#clients = Client.all
end
def new
#client = Client.new
end
def show
#client = Client.find(params[:id])
end
def create
#client = Client.new(client_params)
if #client.save
redirect_to #client
else
render :new
end
end
def edit
end
def delete
end
private
def client_params
params.require(:client).permit(:name, :provider)
end
end
My form:
<%= form_with model: #client do |form| %>
<div>
<%= form.label :name %><br>
<%= form.text_field :name %>
<% client.errors.full_messages_for(:name).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :provider %><br>
<%= form.text_field :provider %>
</div>
<div>
<%= form.label :business_type %><br>
<%= form.select :business_type, ["Partnership", "Sole Trader", "Limited Company"] %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
Finally my new.html.erb file:
<h1>Clients#new</h1>
<%= render 'form', client: #client %>

clients_path is generated by resources :clients, only: :index or you probably need to give your route the name you want. Try this
get '/clients/index', as: :clients
or, if you want to specify non default paths as you're doing, your index is probably called clients_index_path, but you can check that with a rake routes or rails routes, because I'm not sure.
That said, I suggest you to go with the resources method in your routes file and use the default paths as you're trying to do. Something like
resources :clients
but now you don't have a path like /clients/index no more, just /clients for the index action.
If you're in doubts with routes try to read the guide about routing

The Rails way to declare the routes to Create, Update, Read and Destroy (CRUD) a resource is just:
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
get 'dashboard/index'
root to: "home#index"
devise_for :users, controllers: {
sessions: 'users/sessions',
passwords: 'users/passwords',
registrations: 'users/registrations'
}
resources :clients
end
As you can see by the annotions above each method Rails does not add the "action" to the path except for the new and edit routes:
class ClientsController < ApplicationController
before_action :set_client, only: [:show, :edit, :update, :destroy]
# GET /clients
def index
#clients = Client.all
end
# GET /clients/1
def show
end
# GET /clients/new
def new
#client = Client.new
end
# POST /clients
def create
#client = Client.new(client_params)
if #client.save
redirect_to #client
else
render :new
end
end
# GET /clients/edit
def edit
end
# PATCH /clients/1
def update
if #client.update(client_params)
redirect_to #client
else
render :edit
end
end
# DELETE /clients/1
def delete
#client.destroy
redirect_to action: :index,
notice: 'Client deleted'
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(:name, :provider)
end
end
Thus you don't create resources with post '/clients/new' - You use POST /clients. Also when you use the "bare-bones" routing methods such as match, get, post etc Rails does not automatically add routing helper. If you actually wanted to generate the equivilent routes you would need to use:
post '/clients',
to: 'clients#create',
as: :clients
But you're much better off embracing the conventions and learning to use them to be productive.

Related

Ruby on Rails - No data in Params to save in database

My problem is similar to this question: Ruby on Rails - Data not saved. Index showing blank values
However the strong params match the answers and no data seems to come through.
If I use params.require(:banktransaction).permit(...) I get an error: param is missing or the value is empty
If i remove the require part, a row is added but with no values.
I went through the view and controller and checked my spelling, for the life of me I can't see what I have missed, can anyone point me in the right direction?
controller:
class BankAccountsController < ApplicationController
def delete
end
def destroy
end
def edit
end
def update
end
def index
#bankaccount = BankAccount.all
end
def show
end
def new
#banktransaction = BankAccount.new(:transactionDate => Time.now, :description => params[:description], :credit => params[:credit], :debit => params[:debit])
end
def create
#banktransaction = BankAccount.new(bank_account_params)
if #banktransaction.save
flash[:notice] = "transaction added successfully."
redirect_to(bank_accounts_path)
else
render('new')
end
end
private
def bank_account_params
params.require(:banktransaction).permit(:transactionDate,:description,:credit,:debit)
end
end
View:
<h1>BankAccounts#new</h1>
<p>Find me in app/views/bank_accounts/new.html.erb</p>
<div class="new transaction">
<h2>Create Transaction</h2>
<%= form_for(#banktransaction, :html => {:multipart =>true }) do |f| %>
<%= render(:partial =>'form', :locals=> {:f => f}) %>
<div class="form-buttons">
<%= f.submit("Create Transaction") %>
</div>
<% end %>
</div>
Form partial:
<%= f.label(:transactionDate) %>: <%= f.date_field(:transactionDate) %><br>
<%= f.label(:description) %>: <%= f.text_field(:description) %><br>
<%= f.label(:credit) %>: <%= f.number_field(:credit) %><br>
<%= f.label(:debit) %>: <%= f.number_field(:debit) %><br>
routes:
Rails.application.routes.draw do
resources :bank_accounts do
member do
get :delete
end
end
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
In short, you just permit wrong params key. It's bank_account and your bank_account_params should be
def bank_account_params
params.require(:bank_account).permit(:transactionDate,:description,:credit,:debit)
end
The Rails's form builder will build your params base on model name, not variable name.
Your new action assign a BankAccount instance
def new
#banktransaction = BankAccount.new(:transactionDate => Time.now, :description => params[:description], :credit => params[:credit], :debit => params[:debit])
end
so the form builder will use bank_account as the param key instead of variable name banktransaction

Ruby on Rails - hidden tag not passing into the database

Really new to RoR, and ran into an issue I can't find the answer to anywhere.
I have two tables, user and books, and I'm trying to use a join table to match up the user and the books and add a rating and review column in the join table.
The problem is everything passes into the join table EXCEPT the book_id
View
<%= form_for(current_user.user_book_collections.build) do |f| %>
<div>
<%= hidden_field_tag :book_id, current_book.id %>
<%= f.label :rating %>
<%= f.text_field :rating, class:"form-control" %>
<%= f.label :review %> (optional):
<%= f.text_area :review, size: "24x8", placeholder: "Please enter a brief review... ", class:"form-control" %>
</div>
<p></p>
<%= f.submit "Add to your collection", class:"btn btn-primary" %>
<% end %>
current_user and current_book are methods where the users and books are assigned respectively.
Controller
def create
#user_book_collection = current_user.user_book_collections.build(user_book_collection_params)
if #user_book_collection.save
flash[:success] = "Added to your collection"
redirect_to mybooks_path
else
flash[:danger] = "Add was unsuccessful"
redirect_to bookcollection_path
end
end
private
def user_book_collection_params
params.require(:user_book_collection).permit( :book_id, :user_id, :review, :rating )
end
end
This is what shows up in the console:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"85gmzUO7ldQrevh/qnKwYO9mkd9lX77sG3xxJQV8Y46xZrkl5ifk665abPr79nOT91rO3oLcMSDgYL7BtR+/XQ==", "book_id"=>"6", "user_book_collection"=>{"rating"=>"s", "review"=>"asd"}, "commit"=>"Add to your collection"}
but then checking the record also in the console i can see the book_id did not pass through even though its been assigned in the parameters:
=> #<ActiveRecord::Associations::CollectionProxy [#<UserBookCollection id: 10, user_id: 7, book_id: nil, review: "asd", rating: "s", created_at: "2016-02-07 06:36:33", updated_at: "2016-02-07 06:36:33">
Any help would be appreciated, thanks.
It is because your form is constructed in a way the book_id is passed in params[:book_id], not in params[:user_book_collection][:book_id] thus not present in user_book_collection_params
Use:
f.hidden_field :book_id, value: current_book.id
<%= f.hidden_field :book_id, current_book.id %>
When using form_for, you need to bind the input method to the form object (as above).
In addition to Rich Peck and Vasfeds excellent answers I would suggest you consider what you are doing from a REST point of view.
You want users to be able to add reviews to a book. Note that here UserBookCollections is just an ugly piece of plumbing - not really a resource which you would expose with REST. And calling it UserBookCollection is a bit misleading since each UserBookCollection is really just a link between one user and one book.
Also you want to avoid calling anything Collection in Rails since ActiveRecord uses the concept to denote a collection of linked records which may or may not be loaded - you're going to confuse others.
This is one way to solve the same problem
class User
has_many :reviews
has_many :books, through: :reviews
end
class Review
belongs_to :user
belongs_to :book
end
class Book
has_many :reviews, through: :reviews
end
Nice, now we have an object that we don't even have to explain what it does. So lets turn it into a RESTful resource:
Rails.application.routes.draw do
resources :reviews, only: [:show, :edit, :update, :destroy]
resources :books do
resources :reviews, only: [:new, :create, :index], module: :books
end
resources :users do
resources :reviews, only: [:index], module: :users
end
end
This gives us routes with context:
Prefix Verb URI Pattern Controller#Action
edit_review GET /reviews/:id/edit(.:format) reviews#edit
review GET /reviews/:id(.:format) reviews#show
PATCH /reviews/:id(.:format) reviews#update
PUT /reviews/:id(.:format) reviews#update
DELETE /reviews/:id(.:format) reviews#destroy
book_reviews GET /books/:book_id/reviews(.:format) books/reviews#index
POST /books/:book_id/reviews(.:format) books/reviews#create
new_book_review GET /books/:book_id/reviews/new(.:format) books/reviews#new
user_reviews GET /users/:user_id/reviews(.:format) users/reviews#index
Cool - now we have an API for Review with a built in context. The RESTful routes themselves tell us when we are creating a review for a certain book or looking at reviews by a certain user.
module: :books tells rails to link the nested routes to a "namespaced" controller instead of shoving everything into ReviewsController.
We setup our controllers as follows:
class ReviewsController < ApplicationController
before_action :set_review, only: [:edit, :show, :update, :destroy]
# GET /reviews/:id
def show
end
# GET /reviews
def index
#reviews = Review.all
end
# GET /reviews/:id/edit
def edit
end
# PATCH /reviews/:id
def update
#review.update(review_params)
respond_with(#review)
end
# DELETE /reviews/:id
def destroy
#review.destroy
respond_with(#review)
end
private
def set_review
#review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:rating, :body) # note that you don't need book_id in here.
end
end
# app/controllers/books/reviews_controller.rb
class Books::ReviewsController < ReviewsController
before_action :set_book
# GET /books/:book_id/reviews
def index
#reviews = Review.eager_load(:user, :book).where(book_id: params[:book_id])
end
# GET /books/:book_id/reviews/new
def new
#review = #book.find(params[:book_id]).reviews.new
end
# POST /books/:book_id/reviews
def create
#review = #book.reviews.new(review_params) do |review|
review.user = current_user
end
respond_with(#review)
end
private
def set_book
#book = Book).find(params[:book_id])
end
end
# app/controllers/users/reviews_controller.rb
class Users::ReviewsController < ApplicationController
# GET /users/:user_id/reviews
def index
#reviews = Review.joins(:user, :book).where(user_id: params[:user_id])
end
end
You might question why you would want to use 3 controllers? It allows for a very clean mechanism of both code sharing and customizations. You can have different logic and views without creating a bunch of if or switch code paths. Having many branches in a single action makes testing really messy and violates the skinny controllers paradigm.
The forms would look something like this:
# app/views/reviews/_fields.html.erb
<%= f.label :rating %>
<%= f.text_field :rating, class:"form-control" %>
<%= f.label :body %> (optional):
<%= f.text_area :body, size: "24x8", placeholder: "Please enter a brief review... ", class:"form-control" %>
# app/views/books/reviews/new.erb
<%= form_for([#book, #review]) do |f| %>
<%= render partial: 'reviews/fields', f: f %>
<% end %>
# app/views/books/edit.erb
<%= form_for(#review) do |f| %>
<%= render partial: 'reviews/fields', f: f %>
<% end %>

Ruby on Rails - basic form submission

I have been working on PHP. Presently trying to learn Ruby on Rails. I am learning Rails online, for now I am badly stuck on Sign-up or can say a form submission page. Sorry if it's too silly.
Error is:
undefined method new for nil:NilClass
Here is the code:
users_controller.rb
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user.new(params[:user])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
end
new.html.erb
<% page_title="Signup" %>
<div class="Sign_Form">
<h1>Sign up</h1>
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
<p> Username:</br> <%= f.text_field :username%> </p>
<p> Email:</br> <%= f.text_field :email%> </p>
<p> Password:</br> <%= f.password_field :password%></p>
<p> Password Confirmation:</br> <%= f.password_field :password_confirmation%> </p>
<%= f.submit :Signup %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
user.rb
class User < ActiveRecord::Base
attr_accessor :password
EMAIL_REGEX = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i
validates :username, :presence => true, :uniqueness => true, :length => { :in => 3..20 }
validates :email, :presence => true, :uniqueness => true #:format => EMAIL_REGEX
validates :password, `enter code here`:presence =>true #:confirmation => true #password_confirmation attr
validates_length_of :password, :in => 6..20, :on => :create
end
In your users_controller > create, you put capital letter on User param.
For your case, it should be all lower case params[:user].
Side note, it actually depends on your attribute name you set on the form in the first place.
Edit:
In addition of that you should put #user = User.new(params[:user])
First thing you should create new object of User class
Second pass correct params key
change first line in create method to
#user = User.new(params[:user])
So the changed code will look like this:
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
end
change #user.new(params[:user]) to #user = User.new(params[:user]) I creates #user but it is not saved to database yet. On the line below #user.save that is when it gets saved. And remove render new because it will render the template with out setting the variables that the template needs. instead use redirect_to :new that will send the user to new and also set the variables needed
I guess you need to allow the params of User model in the User controller so as to avoid the forbidden error message as mentioned here. Please note that this is Rails feature as mentioned
Rails has several security features that help you write secure applications, and you're running into one of them now. This one is called strong parameters, which requires us to tell Rails exactly which parameters are allowed into our controller actions.
Thanks

ActionController::UrlGenerationError in Images#index

newbie here stuck on nested resources. I have a image.rb model, which stores images onto the cloudinary CDN. After getting all that working, I decided I needed to categorize the images, so i created a category.rb model which just has a name really, for now anyways.
I have been slowly working through errors after adding a category model and controller/views and all was going okay, until this one. I'm stumped lol. Have been googling around for a few hours now and decided I'm at a wall... so, naturally I come here, hoping someone could enlighten me. Thanks in advance! <3 SO
Here is the error (it is currently throwing it on the "edit" link, but i think it will throw it on the "delete" as well after i get edit working, so I have been changing them there as well):
No route matches {:category_id=>nil, :id=>#<Image id: 16, title: "mario", description: "i belong to category 2!", upload_date: nil, created_at: "2014-03-15 01:13:54", updated_at: "2014-03-15 01:13:54", category_id: 2>} missing required keys: [:category_id]
<td><%= link_to "Show", [#category, image] %></td>
<% if admin? %>
<td><%= link_to "Edit", edit_category_image_path(#category, image) %></td>
<td><%= link_to "Delete", [#category, image], confirm: "Are you sure you want to delete \"#{image.title}\"?", method: :delete %></td>
<% end %>
</tr>
Here is my Image model:
class Image < ActiveRecord::Base
has_attachment :image_file, accept: [:jpg, :png, :gif]
validates :title, presence: true
belongs_to :category
end
Here is the Category model:
class Category < ActiveRecord::Base
has_many :images
end
Here are the controllers for each:
class ImagesController < ApplicationController
before_filter :authenticate_admin, except: [:index, :show]
def index
#images = Image.all
end
def show
#image = Image.find(params[:category_id])
end
def new
#category = Category.find(params[:category_id])
#image = #category.images.build(image_params)
end
def create
#category = Category.find(params[:category_id])
#image = #category.images.build(image_params)
if #image.save
render "show"
else
render "new"
end
end
def destroy
#image = Image.find(params[:id])
#image.destroy
redirect_to images_path
end
def edit
#category = Category.find(params[:category_id])
#image = #category.images.find(params[:id])
end
def update
#category = Category.find(params[:category_id])
#image = #category.images.find(params[:id])
if #image.update(image_params)
render "show"
else
render "edit"
end
end
private
def image_params
params.require(:image).permit(:title, :description, :image_file, :category_id)
end
end
and the Category controller
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def show
redirect_to category_images_path(params[:id])
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
if #category.save
redirect_to #category
else
render "new"
end
end
def destroy
#category = Category.find(params[:id])
#category.destroy
redirect_to categories_path
end
def edit
#category = Category.find(params[:id])
end
def update
#category = Category.find(params[:id])
if #category.update(category_params)
redirect_to #category
else
render "edit"
end
end
private
def category_params
params.require(:category).permit(:title)
end
end
Here is the routes file:
Portfolio::Application.routes.draw do
devise_for :users
resources :blogs
resources :categories do
resources :images
end
root "index#index"
#attachinary
mount Attachinary::Engine => "/attachinary"
end
From googling around I kept running into people having problems with it being related to form partials so here is the _form.html.erb I am using for Image just in case it's needed:
<%= form_for([#category, #image]) do |f| %>
<%= render "error" %>
<p>
<%= f.label :title %>
<br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.label "Upload image" %><br>
<%= f.attachinary_file_field :image_file %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
ImagesController
class ImagesController < ApplicationController
before_action :load_category
before_action :set_image, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_admin, except: [:index, :show]
def index
#images = #category.images.all
end
def show
end
def new
#image = #category.images.new
end
def create
#image = #category.images.new(image_params)
if #image.save
render "show"
else
render "new"
end
end
def destroy
#image.destroy
redirect_to images_path
end
def edit
end
def update
if #image.update(image_params)
render "show"
else
render "edit"
end
end
private
def load_category
#category = Category.find(params[:category_id])
end
def set_image
#image = #category.images.find(params[:id])
end
def image_params
params.require(:image).permit(:title, :description, :image_file, :category_id)
end
end
Try to use the before_action callback to set your necessary instance variables. Here I loaded the category and the images via a private method. This saves a lot time. You can even see this when you generate scaffolding.
before_filter has been aliased as before_action to make it clearer of its function.
The root path is usually defined on the second line of routes so its clear.
Your named paths doesn't seem like the problem. It is mainly your images controller
I suggest you generate a new rails app using scaffolding just to look at it's controllers.
rails g scaffolding images

Is it possible to update a session variable using a form in Rails?

I'm working on a form that should allow users to update a session variable called session[:city] that tracks their location. The variable doesn't need to be saved in the database cause it's kind of a throwaway; it only needs to exist while the user is on the site.
session[:city] is used in several places throughout the site, so I've placed the following method in the Application Controller:
class ApplicationController < ActionController::Base
before_filter :set_city
def set_city
unless session[:city].present?
session[:city] = request.location.city
end
if params[:city].present?
session[:city] = params[:city]
end
end
end
That part works correctly, and I'm able to call the variable throughout the site.
The issue I'm having is in updating the variable by a form. The only action I need it to do is update the variable, but it's not responding. I know I'm missing something here, but after trying a bunch of things I'm a bit stumped. This is my basic form currently:
<%= form_tag do %>
<%= text_field_tag :city, params[:city] %>
<%= submit_tag %>
<% end %>
Edited to working code
This will not work in production like environment where you have multiple worker process to serve the requests(unicorn or passenger). Each process will have its own memory. If the value is changed during a request processed by one worker process, other processes will not have the updated value.
You should be using session to store this information.
You can try something like this:-
<%= form_tag do %>
<%= text_field_tag :person, :city, :name => "city" %>
<%= submit_tag %>
<% end %>
class ApplicationController < ActionController::Base
before_filter :set_city
def set_city
unless session[:city].present?
session[:city] = params[:city] || request.location.city
end
end
end
The conditions needed to be split into 2 statements: an unless to set the session[:city] and an if to check if any params were being passed.
class ApplicationController < ActionController::Base
before_filter :set_city
def set_city
unless session[:city].present?
session[:city] = request.location.city
end
if params[:city].present?
session[:city] = params[:city]
end
end
end
And the working form:
<%= form_tag root_path do %>
<%= text_field_tag :city, params[:city], :placeholder => "#{session[:city]}: Change City" %>
<% end %>