So i am trying to learn Ruby on Rails and i tried to code simple product creation to database but rails only executes the insert query with only created_at and updated_at values. Here is my products_controller's create action ;
def create
#product = Product.new(params[:product_params])
#product.save
redirect_to #product
end
private
def product_params
params.require(:product).permit(:title, :price)
end
Here is my new.html.erb under products folder ;
<%= form_with scope: :product ,url:products_path, local: true do |form| %>
<p>
<%= form.text_field :title %>
</p>
<p>
<%= form.text_field :price %>
</p>
<p>
<%= form.submit %>
<% end %>
And this is the SQL query executed by Rails looking to Rails Server
Started POST "/products" for 127.0.0.1 at 2018-12-04 16:44:52 +0300
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9Z4aFNpMv+uUPXx+LkHoOtYpWae/8tIOCQ3Jt47AFScp/vVHTWI+G4d6CjHqBz6t5L5UR57I7Gh7bUWog1Dqow==", "product"=>{"title"=>"brokoli", "price"=>"3434"}, "commit"=>"Save Product"}
(0.2ms) BEGIN
↳ app/controllers/products_controller.rb:8
Product Create (0.3ms) INSERT INTO products (created_at, updated_at) VALUES ('2018-12-04 13:44:52', '2018-12-04 13:44:52')
↳ app/controllers/products_controller.rb:8
(5.4ms) COMMIT
↳ app/controllers/products_controller.rb:8
Redirected to http://localhost:3000/products/12
Completed 302 Found in 25ms (ActiveRecord: 7.4ms)
You don't do
#product = Product.new(params[:product_params])
You do
#product = Product.new(product_params)
The product_params is a method that returns the white-listed params entries.
def create
#product = Product.new(product_params) #product.save
redirect_to #product
end
private
def product_params params.require(:product).permit(:title, :price)
end
Related
I am curious as to why my Rails app is rolling back transactions to DB without it being asked to.
Puma log:
Started POST "/posts" for 127.0.0.1 at 2019-01-05 00:32:32 -0500
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"1AlwhyE0VY87oSyCyjmrzgxinJ7+t1TfoYYEDXvfl0pGd4DKE842KXHroFWgtXeusOgt+ZApHmB+e40qliTPjQ==", "post"=>{"title"=>"test", "category_id"=>"4", "body"=>"test"}, "commit"=>"Create Post"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 12], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:7
(0.2ms) begin transaction
↳ app/controllers/posts_controller.rb:14
Category Load (0.2ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT ? [["id", 4], ["LIMIT", 1]]
↳ app/controllers/posts_controller.rb:14
(0.1ms) rollback transaction
↳ app/controllers/posts_controller.rb:14
Redirected to http://localhost:3000/posts
Completed 302 Found in 18ms (ActiveRecord: 0.8ms)
Post controller:
class PostsController < ApplicationController
before_action :require_user
def index
#posts = Post.all
end
def new
#post = Post.new
#categories = Category.all
end
def create
#post = Post.new(post_params)
if #post.save(post_params)
#post.author = current_user.username
flash[:success] = "Post created."
redirect_to post_path(#post.id)
else
flash[:danger] = "Post not created. Redirecting to main page....."
redirect_to posts_path
end
end
View:
<%= form_for #post do |f| %>
<%= f.label :title, "Title of Post" %>
<%= f.text_field :title, class: "form-control" %>
<%= f.label :body, "Post Text" %>
<%= f.text_area :body, class: "form-control" %>
<%= f.submit "Create Post", class: "btn btn-info"%>
<% end %>
You're likely to have validation of the presence of the author field. Since it's set incorrectly in the create method, you have the transaction rolled back:
class PostsController < ApplicationController
...
def create
#post = Post.new(post_params)
if #post.save(post_params)
#post.author = current_user.username
flash[:success] = "Post created."
redirect_to post_path(#post.id)
...
end
In order to have author assigned and saved in the db, you need to assign the author before the saving:
class PostsController < ApplicationController
...
def create
#post = Post.new(post_params)
#post.author = current_user.username
if #post.save(post_params)
flash[:success] = "Post created."
redirect_to post_path(#post.id)
...
end
Depending on the version of Rails you have, and if you are using Strong Parameters, you must whitelist your parameters in the controller.
Example from docs:
private
# Using a private method to encapsulate the permissible parameters
# is just a good pattern since you'll be able to reuse the same
# permit list between create and update. Also, you can specialize
# this method with per-user checking of permissible attributes.
def person_params
params.require(:person).permit(:name, :age)
end
I am working on a back-office app. The apps I am deploying have a model/table of user_messages. The user messages are used to display admin messages from a central app. The idea is that I can use that app to publish messages to the individual apps such as "System will be unavailable from noon to 1 on Friday".
The individual apps use their own schema in the database. for example, the research library would be rl.user_messages etc.
Since I will need to access multiple tables, I set it up so I can access external tables.
production:
adapter: sqlserver
host: server1
port: 1435
database: web
username: XX
password: xXX
schema_search_path: umc
technical_libraries:
adapter: sqlserver
host: server1
port: 1435
database: XXXX
username: XX
password: XXXXXXXX
schema_search_path: tl
The model that lets me connect to the technical library as an external model is
class TechnicalLibrary < ActiveRecord::Base
self.abstract_class = true
def self.table_name_prefix
'tl_'
end
establish_connection "technical_libraries" # TODO might want to name this to a generic
end
class UserMessage < TechnicalLibrary
self.table_name = "tl.user_messages" # for this one, as opposed to the product development, we need to specify the schema.
end
My technical Libraries controller is
class TechnicalLibrariesController < ApplicationController
def index
#user_messages= TechnicalLibrary::UserMessage.all
end
def show
#technical_library = TechnicalLibrary::UserMessage.first # TODO HARDWIRED -
end
def new
#technical_library = TechnicalLibrary::UserMessage.new
end
def edit
#technical_library = TechnicalLibrary::UserMessage.find(params[:id])
end
def create
#technical_library = TechnicalLibrary::UserMessageRl.new(technical_library_params)
respond_to do |format|
if #technical_library.save
format.html { redirect_to #technical_library, notice: 'Technical library was successfully created.' }
format.json { render :show, status: :created, location: #technical_library }
else
format.html { render :new }
format.json { render json: #technical_library.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #technical_library.update(technical_library_params)
format.html { redirect_to #technical_library, notice: 'technical library was successfully updated.' }
format.json { render :show, status: :ok, location: #technical_library }
else
format.html { render :edit }
format.json { render json: #technical_library.errors, status: :unprocessable_entity }
end
end
end
def destroy
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_technical_library
#technical_library = TechnicalLibrary.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def technical_library_params
params.require(:technical_library).permit(:message, :expires)
end
My technical Libraries form is
<%= form_for(#technical_library) do |f| %>
<% if #technical_library.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#technical_library.errors.count, "error") %> prohibited this technical_library from being saved:</h2>
<ul>
<% #technical_library.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :message %><br>
<%= f.text_field :message %>
</div>
<div class="field">
<%= f.label :expires %><br>
<%= f.datetime_select :expires %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<hr>
Controller <%= controller_name %> | <%= action_name %>
<hr>
The form looks as follows. The submit button seems to be wrong. it is pointing to another model.
If I click on the submit button, I get an error message as follows. I suspect that the problem lies in inheriting from another model.
NameError in UserMessagesController#show
uninitialized constant TechnicalLibrary::UserMessageRl
Rails.root: C:/Users/cmendla/RubymineProjects/user_message_console_3
Application Trace | Framework Trace | Full Trace
app/controllers/user_messages_controller.rb:15:in `show'
Request
Parameters:
{"id"=>"1"}
I had a UserMessage model that I'm probably not going to use since I will connect to the individual application's tables.
class UserMessage < ActiveRecord::Base
end
OK - figured it out. I had to specify the controller and action in the form_for statement.
<%= form_for #technical_library, :url => { :controller => "technical_libraries", :action => "update" }, :html => {:method => :post} do |f| %>
That seems to be doing the trick
I am trying to submit a form via remote: true in rails 3 and for some reason when I look at the response in the browser I only see the raw response instead of the JavaScript being interpreted.
Form:
<%= form_for #entry, url: contest_entries_path, remote: true, html: {id: "contest_form"} do |f| %>
Controller:
class ContestEntriesController < ApplicationController
respond_to :html, :js
def index
#entry = ContestEntry.new
#entry.build_school
respond_with #entry
end
def create
#entry = ContestEntry.new(params[:contest_entry])
respond_with #entry
end
end
Create.js.erb:
<% unless #entry.errors.any? %>
<% if #entry.parent? %>
$('body').find('#parents_message').show();
<% else %>
$('body').find('#falculty_message').show();
<% end %>
<% end %>
The response in the browser is the raw JavaScript response
I have watched the railscasts on Pagination with AJAX. I am trying to duplicate his method using a nested model. I seem to be confused because i don't have a view for the child model or a fully integrated controller.
Assume the Product has_many Comments. How can I do the same pagination using will_paginate on comments within a product view?
UPDATE:
Here is most of my attempt (my attempt actually uses kaminari but it's basically the same as will_pagenate):
view/products/comments/_index.html.erb:
<div id="comments">
<% comments.each do |comment| %>
<%= f.fields_for :comments, comments do |comment_builder| %>
<%= render 'products/comments/field', f: comment_builder %>
<% end %>
<% end %>
<%= paginate comments %>
</div>
view/products/comments/_field.html.erb:
<fieldset>
<%= f.hidden_field :_id, :value => f.object._id %>
<%= f.hidden_field :_destroy %>
<%= f.object.text %>
<%= link_to "remove", '#', class: "remove_field" %>
</fieldset>
view/products/comments/_index.js.erb:
$("#comments").html("j render("products/comments/index")) %>");
assets/javascripts/product.js
$('.pagination a').live
click: ->
$(".pagination").html("Page is loading...")
$.getScript(this.href)
false
controllers/comments_controller.rb
class CommentsController < ApplicationController
before_filter :get_product
def index
#comments = #product.comments.desc(:created_at).page(params[:page]).per(10)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #comments }
end
end
private
def get_product
#product = Product.find(params[:product_id]) if params[:product_id]
redirect_to root_path unless defined?(#product)
end
end
views/products/show.html.erb (an excerpt)
<%= render "products/comments/index", f: f, comments: #comments%>
controllers/products_controller.rb
def show
#product = Product.find(params[:id])
#comments = #product.comments.desc(:created_at).page(params[:page]).per(1)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #product }
end
end
view/products/comments/_index.js.erb never gets called. Also is there a more rails-like way of doing this?
I have a Rails app using a "has_many :through" relationship. I have 3 tables/models. They are Employee, Store, and StoreEmployee. An employee can work for one or more stores. On the Store view (show.html.erb), I want to include a form to add an employee to the store. If the employee doesn't already exist, it is added to the Employees table. If it does already exist, it is just added to the store.
Here are the model definitions:
# models
class Employee < ActiveRecord::Base
has_many :store_employees
has_many :stores, :through => :store_employees
attr_accessible :email, :name
end
class Store < ActiveRecord::Base
has_many :store_employees
has_many :employees, :through => :store_employees
attr_accessible :address, :name
end
class StoreEmployee < ActiveRecord::Base
belongs_to :store
belongs_to :employee
attr_accessible :employee_id, :store_id
end
Using the rails console, I can prove the data models are working correctly, like so:
emp = Employee.first
str = Store.first
str.employees << emp # add the employee to the store
str.employees # shows the employees that work at the store
emp.stores # shows the stores where the employee works
I have also updated my routes.rb to nest the employees resource under the stores resource like this:
resources :stores do
resources :employees
end
Edit: I also added the resources :employees as "unnested" resource. So that now looks like this:
resources :stores do
resources :employees
end
resources :employees
So, in the store controller's show action, I think it should look like this:
def show
#store = Store.find(params[:id])
#employee = #store.employees.build # creating an empty Employee for the form
respond_to do |format|
format.html # show.html.erb
format.json { render json: #store }
end
end
And here is the store's show.html.erb including the form to add an employee:
<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
<%= #store.name %>
</p>
<p>
<b>Address:</b>
<%= #store.address %>
</p>
<%= form_for([#store, #employee]) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.submit "Add Employee" %>
<% end %>
<%= link_to 'Edit', edit_store_path(#store) %> |
<%= link_to 'Back', stores_path %>
I get this error message when I click the 'Add employee' button:
NoMethodError in EmployeesController#create
undefined method `employee_url' for #
So, what I am I missing? I think it has to do with the 'form_for' but I'm not sure. I am not sure why the EmployeesController#create is getting called. Am I doing something wrong with the routes.rb?
I don't know where the logic for adding the employee should go. In the Employees controller? Store Controller?
### Update
Here is the updated create method in the EmployeesController. I included the recommendation from Mischa's answer.
def create
#store = Store.find(params[:store_id])
#employee = #store.employees.build(params[:employee])
respond_to do |format|
if #employee.save
format.html { redirect_to #store, notice: 'Employee was successfully created.' }
format.json { render json: #employee, status: :created, location: #employee }
else
format.html { render action: "new" }
format.json { render json: #employee.errors, status: :unprocessable_entity }
end
end
end
After making this change, I successfully saved the Employee but the store_employees association record is not created. I have no idea why. Here is the output in my terminal when creating a record. Note the store is SELECTed and the Employee is INSERTed but that is all. No store_employees record anywhere:
Started POST "/stores/1/employees" for 127.0.0.1 at 2012-10-10
13:06:18 -0400 Processing by EmployeesController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"LZrAXZ+3Qc08hqQT8w0MhLYsNNSG29AkgCCMkEJkOf4=",
"employee"=>{"name"=>"betty", "email"=>"betty#kitchen.com"},
"commit"=>"Add Employee", "store_id"=>"1"} Store Load (0.4ms)
SELECT "stores".* FROM "stores" WHERE "stores"."id" = ? LIMIT 1
[["id", "1"]] (0.1ms) begin transaction SQL (13.9ms) INSERT
INTO "employees" ("created_at", "email", "name", "updated_at") VALUES
(?, ?, ?, ?) [["created_at", Wed, 10 Oct 2012 17:06:18 UTC +00:00],
["email", "betty#kitchen.com"], ["name", "betty"], ["updated_at", Wed,
10 Oct 2012 17:06:18 UTC +00:00]] (1.3ms) commit transaction
Redirected to http://localhost:3000/stores/1 Completed 302 Found in
23ms (ActiveRecord: 15.6ms)
The problem is caused by this snippet from your EmployeesController#create method:
redirect_to #employee
This tries to use employee_url, but that does not exist, because you have nested the employees resource in your routes file. Solution is to also add the "unnested" resource:
resources :stores do
resources :employees
end
resources :employees
Or simply redirect to a different location. E.g. back to StoresController#show.
By the way, the default scaffolding won't do what you expect it to do: it won't create an employee that is related to a store. For that you have to rewrite it somewhat:
def create
#store = Store.find(params[:store_id])
#employee = #store.employees.build(params[:employee])
respond_to do |format|
if #employee.save
format.html { redirect_to #employee, notice: 'Employee was successfully created.' }
format.json { render json: #employee, status: :created, location: #employee }
else
format.html { render action: "new" }
format.json { render json: #employee.errors, status: :unprocessable_entity }
end
end
end