Cannot create an new interface when it belongs to project - ruby-on-rails

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.

Related

Custom route param and form_with not using the param

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| %>
...

Rails keeps showing "UnkownAttributeError" while trying to insert into two tables from one controller

What I wanted to do is get the
last_insert_id
of a table and insert it into another table but I get this error while doing it this way(I don't know if this is the right way to do such things)
ActiveModel::UnknownAttributeError (unknown attribute 'content' for MessagePicture.): # This is copied from the console
unknown attribute 'content' for MessagePicture. # Copied from the Browser and it had this number highlighted
#message = current_user.messages.build.message_pictures.build(message_params)
This is the
new and create methods of the Message_controller class
def new
#message = current_user.messages.build if logged_in?
#reciever = User.find_by(params[:id])
end
def create
##msg = Message.new(user_params)
#message = current_user.messages.build.message_pictures.build(message_params)
if #message.save
if #message[:picture].present?
# Send this to the message model for insertion with the #message_id and from the picture model, insert it to the
add_msg_pic(msg = [#message.id,#message[:msg_img_url]])
end
flash[:success] = "Message sent"
redirect_to messages_path
else
flash.now[:danger] = "Message not sent"
render 'new'
end
end
This is the
message_params method
def message_params
params.require(:message).permit(:content, :receiver, :sender, :archive, message_pictures_attributes:[:msg_img_url, :message_id])
end
This is the
Message class(model) add_msg_pic method
def add_msg_pic(msg)
msg.message_pictures.each.do |m|
m.message.id = nil
message_pictures << m
end
This is the view page
<h1>Compose a new message</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#message, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: #message %>
<%= f.label :content %>
<%= f.hidden_field :receiver, value: #reciever %>
<%= f.hidden_field :sender, value: current_user.id %>
<%= f.text_area :content, size:"20x15" %>
<%= f.submit "Send message", class: "btn btn-primary" %>
<span class="picture">
<%= f.fields_for :message_pictures do |mp| %>
<%= mp.file_field :msg_img_url, accept: 'image/jpeg,image/gif,image/png' %>
<% end %>
</span>
<% end %>
<%= button_to "Save message", archive_messages_path %>
</div>
</div>
Assuming Message looks like this
class Message < ApplicationRecord
has_many :message_pictures
accepts_nested_attributes_for :message_pictures
end
Then this:
#message = current_user.messages.build.message_pictures.build(message_params)
should actually be
#message = current_user.messages.build(message_params)
Right now you are passing all of those attributes to a new MessagePicture but really you should be passing some to the Message and some to the MessagePicture.
The accepts_nested_attributes_for will slice off the ones for message_pictures via message_pictures_attributes and build both items for you.

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>

First or create method , creating duplicates

Hi i'm trying to use the find or create method to update a skill if it already exists or create it if it doesn't. I can create a new skill fine but when i try update a skill that already exists it does update the skill but also creates a duplicate skill with the same data.
def create
#project = Project.find params[:project_id]
#skills_required = #project.skills_requireds.new(skills_required_params)
skills = SkillsRequired.where(skills_required_params.slice(:skill_id)).first_or_create
skills.update(skills_required_params)
respond_to do |format|
if #skills_required.save
format.html{ redirect_to #project, notice: "Skills required added for #{#project.projectName}" }
else
format.html{ redirect_to #project, notice: "Something went wrong, unable to update required skills " }
end
end
end
Form:
<div class="section">
<div class="top-border left"></div>
<div class="top-border right"></div>
<h3> Skill Required</h3>
<%= form_for([#project, SkillsRequired.new]) do |f| %>
<div class="field">
<%= f.label :skill_id %><br>
<%= f.collection_select :skill_id, Skill.all, :id, :skillType, :prompt => "Select Skill" %>
</div>
<div class="field">
<%= f.label :numWorkers %><br>
<%= f.number_field :numWorkers %>
</div>
<div class="field">
<%= f.label :skillLevel %><br>
<%= f.text_field :skillLevel %>
</div>
<%=f.submit "Add Skill" %>
<%end%>
</div>
I've tried adding a skills_required destroy to my controller but this doesn't allow me to add a new skill. Any help would be appreciated
You're approaching the problem wrong. You need a model level validation which enforces what skill requirements are valid:
# Your model names should be nouns - not adjectives
class SkillRequirement < ApplicationRecord
validates_uniqueness_of :skill_id, scope: :project_id'
end
If you want users to be able to specify the same skill but at different levels you can do it like so:
# Your model names should be nouns - not adjectives
class SkillRequirement < ApplicationRecord
validates_uniqueness_of :skill_id, scope: [:project_id, :level]
end
You should also combine this with a database index to avoid race conditions.
add_index(:skill_requirements, [:project_id, :skill_id], unique: true, name: 'by_skill_and_project')
Since updating existing skill requirements should be handled by a seperate update action you don't need all that bloat in your controller:
class SkillRequirementsController
before_action :set_project
def create
#skill_requirement = #project.skill_requirements.new(skill_requirement_params)
if #skill_requirement.save
redirect_to #project
else
render :new
end
end
def update
#skill_requirement = #project.skill_requirements.find(params[:id])
if #skill_requirement.update(skill_requirement_params)
redirect_to #project
else
render :edit
end
end
private
def set_project
#project = Project.find(params[:project_id])
end
def skill_requirement_params
params.require(:skill_requirement).permit(:)
end
end

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