Custom route param and form_with not using the param - ruby-on-rails

I'm trying to allocate an address a custom id so it's not easy to guess the record (for people who want to add and change other people's addresses). For some reason, whilst I can create a record with the correct path being created, the edit path from form_with seems to be not using the custom resource param.
routes.rb
resources :deladdresses, param: :delid
_form.html.erb
<%= form_with model: deladdress, class: "mt-4" do |f| %>
<%= render "shared/error_messages", resource: f.object %>
.. lots removed for clarity ...
<%= f.hidden_field :country, value: "Australia" %>
<%= f.hidden_field :ordernum, name: :ordernum, value: #order %>
<%= link_to order_path(#order.ordernum) do %>
<button class="btn btn-secondary">Back</button>
<% end %>
<%= f.button "Save", id: 'submit', class: "btn btn-primary" %>
<% end %>
Which is coming from in my new.html.erb and edit.html.erb:
<%= render "form", deladdress: #deladdress %>
deladdress.rb
class Deladdress < ApplicationRecord
before_save :add_del_id
belongs_to :user
has_many :orders
def add_del_id
randchars = ("a".."z").to_a.sample(8).join
time = DateTime.now.strftime("%H%M%S")
self.delid = "#{time}-#{randchars.upcase}"
end
end
deladdress_controller.rb
class DeladdressesController < ApplicationController
before_action :find_my_order
def show
# Collect the delivery address for the order
# Do we want to collect and store these per customer?
end
def new
if current_user
#savedaddresses = Deladdress.where(user: current_user)
end
#deladdress = Deladdress.new
end
def edit
#deladdress = Deladdress.where(delid: params[:delid]).first
end
def create
#deladdress = Deladdress.create(deladdress_params)
#deladdress.user = current_user
if #deladdress
#order&.update(deladdress: #deladdress)
redirect_to order_path(#order.ordernum), notice: "Address added"
else
render :new
end
end
def update
#deladdress = Deladdress.where(delid: params[:delid]).first
if #deladdress.update(deladdress_params)
#order.update(deladdress: #deladdress)
redirect_to order_path(#order.ordernum), notice: t(".updated")
else
render :edit
end
end
private
def deladdress_params
attributes = [:first_name, :last_name, :address, :apartment, :city, :state, :country, :postcode, :ordernum, :delid]
params.require(:deladdress).permit(*attributes)
end
def find_my_order
#order = find_order
end
end
When I go to the following url http://localhost:5000/deladdresses/112750-UBTYOJGK/edit, I can see the delid is there. But, when I look at the form which it is going to try and submit I have the following. The id is 5 and not 112750-UBTYOJGK.
<form class="mt-4" action="/deladdresses/5" accept-charset="UTF-8" data-remote="true" method="post">
<input type="hidden" name="_method" value="patch">
<input type="hidden" name="authenticity_token" value="XXXXXXXXXXXX">
<button name="button" type="submit" id="submit" class="btn btn-primary">Save</button>
</form>
I am obviously missing something but I really don't know what.
Thanks in advance for any help you might be able to give to have this work.

You can pass the url for the form to the form helper like so:
for where you call the edit form:
<%= render "form", deladdress: #deladdress, url: deladress_path(#deladress.delid) %>
for where you call the new form:
<%= render "form", deladdress: #deladdress, url: deladresses_path %>
and then in the form itself:
<%= form_with model: deladdress, url: url, class: "mt-4" do |f| %>
...

Related

Cannot create an new interface when it belongs to project

I try to create a new interface object. After clicking create button, it still remains new.html.erb, it should go to project_interfaces_path(main page). Also, the data has not saved yet.
I have tried many ways such as change URL, but it does not work and it reports NoMethodError in InterfacesController#create
undefined method `interfaces' for nil:NilClass
The interface/new.html.erb
<div class="card-body">
<%= form_for #interface, url:project_interfaces_path,method: :post do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_area :name,class: 'form-control'%>
</div>
<div class="form-group">
<%= f.label :desp %>
<%= f.text_field :desp,class:'form-control'%>
</div>
<div class="form-group">
<%= f.label :request_url %>
<%= f.text_field :request_url,class:'form-control'%>
</div>
<div class="form-group">
<%= f.label :request_eg %>
<%= f.text_field :request_eg,class:'form-control'%>
</div>
<div class="form-group">
<%= f.label :response_eg %>
<%= f.text_field :response_eg,class:'form-control'%>
</div>
<%=link_to project_interfaces_path do%>
<button type="button" class="btn btn-primary">返回列表</button>
<% end %>
<%=f.submit "创建",class: 'btn btn-primary' %>
<% end %>
The interface controller:
def new
#interface = Interface.new
end
def create
#interface = #project.interfaces.new(interface_params)
if #interface.save
redirect_to project_interfaces_path
else
render :new
end
end
private
def interface_params
params.require(:interface).permit(:id, :name, :desp,:request_url,:request_eg,:response_eg)
end
The interface belongs to project:
class Interface < ApplicationRecord
belongs_to :method_type
has_many :get_fields, dependent: :destroy
has_many :put_fields, dependent: :destroy
belongs_to :project
end
You're working with nested resources, it means you can't create an interface without project_id, since Interface belongs_to :project. How it should be:
def new
#project = Project.find(params[:project_id])
#interface = #project.interfaces.new
end
def create
#project = Project.find(params[:project_id])
#interface = #project.interfaces.build(interface_params)
if #interface.save
redirect_to project_interfaces_path(#project)
else
render :new
end
end
private
def interface_params
params.require(:interface).permit(:id, :name, :desp,:request_url,:request_eg,:response_eg)
end
And remove url and method options from form, it works automagically
<%= form_for #interface do |f| %>
Indeed, you are redirecting to new instead of project_interfaces_path:
def create
#interface = Interface.new(interface_params)
if #interface.save
#redirect_to new_project_interface_path(project) <- wrong path
redirect_to project_interfaces_path # Good path
else
render :new
end
end
Also, add a space between url: and project_interfaces_path in <%= form_for #interface, url:project_interfaces_path,method: :post do |f| %>.
UPDATE: It seems you are trying to save an Interface without associate a Project to it.
You need to retrieve a project and build the interface with it:
def new
project = Project.find(params[:id]) # Assuming you are sending it
#interface = project.interfaces.build
end
def create
project = Project.find(params[:id]) # Assuming you are sending it
#interface = project.interfaces.build(interface_params)
if #interface.save
redirect_to project_interfaces_path
else
render :new
end
end
Taking a look on your routes would help.

ruby on rails nested form create

I have user model that has many reviews, and each review has many replies.
I want to allow the user to reply under a review.
In a profile page (coming from a profile controller and show action), I want to be able to create replies.
<div class="reply-box d-none" id="reply-box">
<%= form_with(model: Reply, url: new_user_review_reply_path(#user, #review)) do |reply| %>
<%= reply.hidden_field :user_id, value: #user %>
<%= reply.hidden_field :review_id, value: #review %>
<%= reply.text_field :reply_content%>
<div class="comment-box-btns mb-5">
<%= reply.submit "submit", class: 'submit-btn d-inline-block ml-2 float-right'%>
<div class="cancel-btn d-inline-block float-right">cancel</div>
</div>
<% end %>
</div>
Here is the route.rb
resources :users do
resources :reviews do
resources :replies
end
end
Here is the reply controller:
class RepliesController < ApplicationController
def new
#user = User.find(params[:user_id])
#reivew = #user.reviews.find(params[:review_id])
#reply = #reivew.replies.new
end
def create
#user = User.find(params[:user_id])
#reivew = #user.reviews.find(params[:review_id])
#reply = #reivew.replies.create!(reply_params)
respond_to do |format|
format.html {redirect_to(profile_path(param[:user_id]))}
format.js
end
end
private
def reply_params
params.require(:reply).permit(
:reply_content,
:user_id,
:review_id
)
end
end
I don't know how to set up the "form_with". So far it just says
undefined method `reply_content' for #<Class:0x007f8c7396aaa8>
reply_content is the field in reply I want to create using the text_area.
I am very confused. Any help would be greatly appreciated.
As you have already intitilize #reply = #reivew.replies.new in new action so you should use this #reply object with reply form, also i don't think that you need not to explicitly provide value: user_id and value: review_id
<div class="reply-box d-none" id="reply-box">
<%= form_for #reply, url: new_user_review_reply_path(#user, #review) do |reply| %>
<%= reply.hidden_field :user_id %>
<%= reply.hidden_field :review_id %>
<%= reply.text_field :reply_content%>
<div class="comment-box-btns mb-5">
<%= reply.submit "submit", class: 'submit-btn d-inline-block ml-2 float-right'%>
<div class="cancel-btn d-inline-block float-right">cancel</div>
</div>
<% end %>
</div>

Rails - Nested form with complex params

I need to create a campaign with given prizes. My models already are related and accepting nested attributes.
View:
<%= form_for #campaign, remote: true do |f| %>
<% 5.times do |i| %>
<%= f.fields_for :prizes do |prize_form| %>
<div class="form-group">
<%= prize_form.label "prize #{i + 1}" %>
<%= prize_form.text_field :name %>
</div>
<% end %>
<% end %>
<% end %>
Which generates:
<input id="campaign_prizes_attributes_0_name" name="campaign[prizes_attributes][0][name]" type="text">
<input id="campaign_prizes_attributes_1_name" name="campaign[prizes_attributes][1][name]" type="text">
<input id="campaign_prizes_attributes_2_name" name="campaign[prizes_attributes][2][name]" type="text">
<input id="campaign_prizes_attributes_3_name" name="campaign[prizes_attributes][3][name]" type="text">
<input id="campaign_prizes_attributes_4_name" name="campaign[prizes_attributes][4][name]" type="text">
In my controller I have this
class CampaignsController < ApplicationController
respond_to :html, :js
def index
#campaigns = Campaign.all
end
def new
#campaign = Campaign.new
#campaign.prizes.build
end
def create
#campaign = Campaign.new(campaign_params)
#campaign.prizes.build
end
def campaign_params
params.require(:campaign).permit(:name, :date_start, :date_end, :status, :rules, prizes_attributes: [name: []])
end
end
No matter what I do, I always get this error:
Unpermitted parameters: name
I need to make each campaign have a varying ammount of prizes, but I'm not able to make this work. What am I doing wrong?
Thanks.
The campaign_params method should be:
def campaign_params
params.require(:campaign).permit(:name,
:date_start,
:date_end,
:status,
:rules,
prizes_attributes: [ :name ])
end
How permit nested attributes
Your campaign_params are wrong
prizes_attributes: [name: []]
must be
prizes_attributes: [:name]

Rails 4, how to update a model field from a different controller?

I am trying to update an invoice fields, when checking out in the carts controller. These must be present when checking out, or it should fail. However, I can't get it to update, much less validate them.
Here is my code:
cart show view:
<div class = "row">
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Customer: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Seller: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<%= submit_tag 'Complete', class: 'btn btn-success' %>
<% end %>
</div>
</div>
carts controller:
class CartsController < ApplicationController
def show
#invoice = current_invoice
#invoice_products = current_invoice.invoice_products
#customers = Customer.all
#employees = Employee.all
end
def checkout
current_invoice.customer_id = params[:customer_id]
current_invoice.employee_id = params[:employee_id]
current_invoice.save
redirect_to current_invoice
end
end
current_invoice is the current session's invoice, related to the cart. It redirects correctly, but doesn't update.
in the invoices controller:
def invoice_params
params.require(:invoice).permit(:invoice_number, :customer_id, :invoice_date, :invoice_status_id, :employee_id, invoice_products_attributes: [:id, :invoice_id, :product_id, :price, :tax, :discount, :value])
end
Can anyone please help me in identifying where I am going wrong? Could it be my approach is not even valid?
Thanks in advance
The type of functionality you're after is considered "business logic" and should be implemented in the model and called from the controller.
You can define a method in a model:
class Invoice < ActiveRecord::Base
def update_invoice(cust_id, emp_id)
if self.update_attributes(:customer_id => cust_id], :employee_id = emp_id])
puts "Success!
else
puts "Failed to update record. Handle the error."
end
end
You can call my_method from carts_controller.rb like this:
def update
# all your regular update logic here
# replace the bit of code that saves the cart with something like this:
respond_to do |format|
if(current_invoice.update_invoice(params[:customer_id], params[:employee_id])
if(#cart.update(cart_params))
format.html { redirect_to #activity, notice: 'Activity was successfully updated.' }
format.json { render :show, status: :ok, location: #activity }
else
format.html { render :edit }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
Also, note the use of update_attributes rather than save. Bear in mind that update_attributes will return false if you run into any problems updating (e.g. one or more validations failed). Don't confuse update_attributes with the singular update_attribute which updates a single field and will not run validations.
Finally got it.
current_invoice.update_attributes(customer_id: params[:invoice][:customer_id], employee_id: params[:invoice][:employee_id])
Also in view, changed location of form_tag:
<div class = "row">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Cliente: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Vendedor: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= submit_tag 'Completar', class: 'btn btn-success' %>
</div>
<% end %>
</div>
Could it be my approach is not even valid
Your approach is definitely valid, it's great that you're using sessions in this way.
I'd do it slightly differently:
#config/routes.rb
resource :cart, except: [:edit, :new, :create], path_names: { update: "checkout" }
This will give you the following paths:
#url.com/cart -> carts#show (here you can invoke a cart if one doesn't exist)
#url.com/cart/checkout #-> POST to "update" method in carts controller
#url.com/cart/ (method: :delete) -> DELETE to "destroy" cart (refresh)
--
#app/controllers/carts_controller.rb
class CartsController < ApplicationController
before_action :setup_cart
def show
#cart = current_cart #-> products called from this. I don't know how you're linking them
#customers = Customer.all
#employees = Employee.all
end
def update
#invoice = Invoice.find_or_create_by(id: current_card.id)
#invoice.update update_params
redirect_to cart_path
end
def destroy
current_cart = nil
redirect_to carts_path, notice: "Cart Cleared"
end
private
def setup_cart
current_cart ||= sessions[:cart]
end
def update_params
params.require(:cart).permit(:customer_id, :employee_id)
end
end
Now, to update the cart, you'll want to take note from MarsAtomic's answer. However it must be noted that naked params are not available in the model.
If you use update_attributes, or just plain update, you'll need to do the following:
#app/models/cart.rb
class Invoice < ActiveRecord::Base
has_many :products
belongs_to :employee
belongs_to :customer
#validations here
#callbacks here (what MarsAtomic refers to as business logic)
before_save :do_something, only: :update
private
def do_something
#something here
#params appended to current instance of object
#eg self.customer_id
end
end
I'd also go more succinct in your view:
#app/views/carts/show.html.erb
<div class = "row">
<%= form_tag cart_checkout_path, method: :patch do |f| %>
<% options = [["cliente", "customer"], ["vendedor", "employee"]] %>
<% options.each do |name, type| %>
<%= content_tag :strong, "#{name.titleize}:" %>
<%= collection_select :cart, eval(":#{type}_id"), instance_variable_get("##{type.pluralize}"), :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
<% end %>
<% content_tag :div, class: "col-lg-12 text-right" do %>
<%= submit_tag 'Completar', class: 'btn btn-success' %>
<% end %>
<% end %>
</div>

How to save data in rails with no form?

I am new to rails and I am just learning the basics.
This is my code on saving data:
app/controllers/employee_controller.rb
class EmployeesController < ApplicationController
def index
render json: #employees = Employee.all
end
def show
render json: #employee = Employee.find(params[:id])
end
def new
#employee = Employee.new
end
def create
#employee = Employee.new(employee_params)
#employee.save
redirect_to #employee
end
private
def employee_params
params.require(:employee).permit(:fname, :mname, :lname, :contactno, :address, :username, :password)
end
end
app/views/employees/new.html.erb
<%= form_for #employee do |f| %>
<p>
<label>First Name</label><br>
<%= f.text_field :fname %>
</p>
<p>
<label>Middle Name</label><br>
<%= f.text_field :mname %>
</p>
<p>
<label>Last Name</label><br>
<%= f.text_field :lname %>
</p>
<p>
<label>Contact No.</label><br>
<%= f.text_field :contactno %>
</p>
<p>
<label>Address</label><br>
<%= f.text_area :address %>
</p>
<br>
<p>
<label>Username</label><br>
<%= f.text_field :username %>
</p>
<p>
<label>Password</label><br>
<%= f.text_field :password %>
</p>
<br>
<p>
<%= f.submit %>
</p>
But, my goal is to save right away without the html form. (NO INPUT) Like when I visit a certain URL and the values are automatically saved in the database.
For a start, I would like to assign a constant value in every field just to see how it works.
Example,
fname='sample name'
mname='sampleMidName'
lname='sampleLastName'
and etc...
How can I assign those values right away after a certain URL/site is visited.
You start by adding a method to your controller
def update_fname
# get the parameters
fname = params[:fname]
# get the employee ID
id = params[:id]
# find the employee
#employee = Employee.find(id)
# update the employee
employee.update_attributes(fname: fname)
redirect_to #employee
end
Then, in your route, you add:
resources :employees do
get 'update_fname'
end
And you call the route, who should be http://localhost:3000/employees/{:id}/update_fname?fname={your_fname}
In your controller try something like:
class EmployeesController < ApplicationController
def custom
#employee = Employee.create(fname: "sample name")
end
end
and define proper route in config/routes.rb:
get "/custom" => "employees#custom"
When you enter proper url in your browser, like:
localhost:3000/custom
The Employee should be saved.
Good luck!

Resources