Difficulty saving form information to the database - ruby-on-rails

I've been trying to implement a commenting system for a blog application I've been working on. However, I've been having a lot of difficulty getting my form to save inputted information to my database.
The code for my comment's controller is:
def create
#comment = #wad.comments.create(comment_params)
if #comment.save
flash[:sucess] = "Thanks for posting!"
redirect_to wad_comments_path(#wad)
else
flash[:error] = "Failed submission. Please try again."
render 'index'
end
end
.
.
.
private
def comment_params
params.require(:comment).permit(:content)
end
The code for my model:
class Comment < ApplicationRecord
attr_accessor :content
belongs_to :wad
belongs_to :user
end
and the code for my form:
<%= form_for([#wad, #wad.comments.create]) do |f| %>
<%= f.text_area :content %>
<%= f.submit %>
<% end %>
Where "wad" is a regular post. I've checked my server log, and in the params is a :comment hash containing :content. I'm not sure why, then, it's not saving to the database. Any thoughts?

why you are using attr_accessor :content instead use column and modify following line
<%= form_for([#wad, #wad.comments.create]) do |f| %>
as
<%= form_for([#wad, #wad.comments.build]) do |f| %>

Related

How to remember active_storage file name when validation fails in controller

In a rails 5.2.3 app, I have a model Post, which uses active_storage to attach a file and has fields for duration and place. The duration must be present.
class Post
has_one_attached :video
validates :duration, presence: true
end
Using simple_form
the fields in the form are declared as
<%= f.input :duration %>
<%= f.input :place %>
<%= f.input :video %>
The controller has the following logic for the create
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = 'Post has been saved'
redirect_to root_path
else
#title = 'New Post'
#user = current_user
render :new
end
end
private
def post_params
params.require(:post).permit(:duration, :video)
end
If the validation fails, the form shows value of place, but I lose the file name for the video. This means the user has to choose the file again. How do I fix this?
Following Thanh's suggestion, I did check this SO question, and tried changing the simple_form field to
<%= f.hidden_field :video, value: f.object.image.signed_id if f.object.video.attached? %>
<%= f.file_field :video %>
This remembered the file name, but did not display it. So I did the following work around:
<% if f.object.video.attached? %>
<span><strong>Video File Name:</strong> <%= f.object.video.blob.filename.to_s %>. To change, choose different file below:</span>
<% end %>
<%= f.hidden_field :video, value: f.object.image.signed_id if f.object.video.attached? %>
<%= f.file_field :video %>

Rails, populate database with associated models on one form

I have looked at various answers to similar questions and haven't quite cracked it.
A wine model is defined with has_one :register, :dependent => :destroy and rightly or wrongly I have added accepts_nested_attributes_for :register. A register is defined with belongs_to :wine.
The code within wines_controller.rb for create is:
def new
#wine = Wine.new
#register = Register.new
def create
#wine = Wine.new(wine_params)
#register = #wine.registers.build(register_params)
respond_to do |format|
if #wine.save
#success
else
format.json { render json: #wine.errors, status: :unprocessable_entity }
format.json { render json: #register.errors, status: :unprocessable_entity }
end
end
end
My form for creating a new wine has the following code:
<%= simple_form_for #wine do |f| %>
# various working elements
<div class="field">
<% f.fields_for :register do |r| %>
<%= r.label :short_name %>
<%= r.text_field :short_name %>
<%= r.label :barcode %>
<%= r.text_field :barcode %>
<% end %>
</div>
When this form is called up no fields are created from the f.fields_for command but this block is executed because I can add test buttons within it to prove it is accessed.
If I try to create a wine I get the following error message:
undefined method `registers' for #<Wine:0x007f1204375330> Did you mean? register register= register_id
I believe that using .build is there to ensure data integrity: I don't want to create a wine that does not have a corresponding register. I have tried thinking about it nested attributes but that seems to be considered a bad plan by many. This current approach feels correct but I think I am missing some understanding of syntax at the very least.
At a later date it will be necessary to have other models linked to register that will not be associated to wines. I was considering a similar approach but I am happy to be told to rethink!
If I understand you correctly you have 2 issues:
Firstly fields for register aren't being displayed - this is partly because #wine.register is nil.
You should change your new action to:
def new
#wine = Wine.new
#wine.register = Register.new
In addition because you are using simple_form_for you will need to use simple_fields_for instead of fields_for
Your second issue that results in the exception tells you everything... you are trying to access #wine.registers, and not #wine.register
Change in your create method to:
#register = #wine.register.build(register_params)
This will fix that issue ... however ... all you really need to do is build the #wine object from your params - your params should be configured to permit the right nested attributes - if it is set up correctly the register object will also be built when building the #wine object.
Your model is already set to accept_nested_attributes and thus will also validate and save the register object when calling #wine.save - no need to explicitly save the register object.
You should have something like:
def wine_params
params.require(:wine).permit(
:attribute1, :attribute2,
register_attributes: [:id, :short_name, :barcode])
end
Try this
Wine and Register models
class Wine < ApplicationRecord
has_one :register, inverse_of: :wine, :dependent => :destroy
accepts_nested_attributes_for :register
end
class Register < ApplicationRecord
belongs_to :wine, inverse_of: :register
validates_presence_of :wine
end
Wines Controller
class WinesController < ApplicationController
def new
#wine = Wine.new
#wine.build_register
end
def create
#wine = Wine.new(wine_params)
if #wine.save
redirect_to #wine
else
render :new
end
end
private
def wine_params
params.require(:wine).permit(:name, register_attributes: [:simple_name])
end
end
My wine_params are specific for
rails g model wine name:string
rails g model register name:string wine_id:integer
Lastly wine form should look like this
<%= form_for #wine do |f|%>
<p>
<%= f.label :name%>
<%= f.text_field :name%>
</p>
<%= f.fields_for :register do |r|%>
<p>
<%= r.label :simple_name%>
<%= r.text_field :simple_name%>
</p>
<% end %>
<%= f.submit %>
<% end %>
So you can modify wine_params and form partial for your application specifics

Rails 4.2: Nested form that selects 2 objects from a different model

I am currently building a tricky form. I am still a fairly young Rails developer so half the battle is grasping how to go about this.
I need to build a form that launches a Campaign. This Campaign will need to be named and commented. It also needs to choose a .zip file form a pre-populated dropdown box. Under that will be the ability to choose one of 5 .pdf files via a check box. Zip files and pdf files are a part of the same model Upload.
So far my Campaign model...
models/campaign.rb
class Campaign < ActiveRecord::Base
has_many :uploads
validates :name, presence: true,
uniqueness: true
accepts_nested_attributes_for :uploads
end
The Uploads model represents the table that holds zip and pdf files..
models/uploads.rb
class Upload < ActiveRecord::Base
has_one :campaign
end
Here is the Campaign controller..
controllers/campaigns_controller.rb
class CampaignsController < ApplicationController
def index
#campaigns = Campaign.all.order("created_at DESC")
end
def new
#campaign = Campaign.new
end
def create
#campaign = Campaign.new(campaign_params)
if #campaign.save
flash[:success] = "Campaign Successfully Launched!"
redirect_to #campaign
else
flash[:error] = "There was a problem launching your Campaign."
redirect_to new_campaign_path
end
end
def show
#campaign = Campaign.find(params[:id])
end
private
def campaign_params
params.require(:campaign).permit(:name, :comment)
end
end
My form looks like this...
views/campaigns/_form.html.erb
<%= form_for #campaign, url: {action: "create"} do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :comment %>
<%= f.text_area :comment %>
#....this is where I lose it..
<%= f.label :data_file %>
<% end %>
How should I got about this? Is this a case use for collection_select? Any advice or even tutorial links will be highly appreciated.

Best practice in creating belongs_to object

Let's say we have the following situation:
class User < ActiveRecord::Base
has_many :tickets
end
class Ticket < ActiveRecord::Base
belongs_to :user
end
For simplicity let's say Ticket has only some text field description and integer user_id. If we open User's views/users/show.html.erb view and inside User controller we have this code which finds correct user which is selected:
def show
#user = User.find(params[:id])
end`
Now inside that show.html.erb view we also have small code snipped which creates user's ticket. Would this be a good practice in creating it?
views/users/show.html.erb
<%= simple_form_for Ticket.new do |f| %>
<%= f.hidden_field :user_id, :value => #user.id %>
<%= f.text_area :description %>
<%= f.submit "Add" %>
<% end %>
controller/tickets_controller.rb
def create
#ticket = Ticket.new(ticket_params)
#user = User.find(ticket_params[:user_id])
#ticket.save
end
def ticket_params
params.require(:ticket).permit(:user_id, :description)
end
So, when we create a ticket for user, ticket's description and his user_id (hidden field inside view) are passed to tickets_controller.rb where new Ticket is created.
Is this a good practice in creating a new object which belongs to some other object? I am still learning so I would like to make this clear :) Thank you.
You should be able to do something like this in your form:
<%= f.association :user, :as => :hidden, :value => #user.id %>
This will pass user_id through your controller to your model and automatically make an association. You no longer need the #user= line in your controller.
Don't forget that the user could modify the form on their end and send any id they want. :)
See https://github.com/plataformatec/simple_form#associations for more info.
How about getting the user from the controller using current_user so that you protect yourself from anyone that would manipulate the value of the user_id in the form. Also I think this way is much cleaner
views/users/show.html.erb
<%= simple_form_for Ticket.new do |f| %>
<%= f.text_area :description %>
<%= f.submit "Add" %>
<% end %>
controller/tickets_controller.rb
def create
#ticket = Ticket.new(ticket_params)
#ticket.user = current_user
#ticket.save
end
def ticket_params
params.require(:ticket).permit(:user_id, :description)
end

Controller and routes issues in my rails app

I have an app where users can create courses, and each course has_one syllabus. How could I go about configuring my courses and syllabuses (I know it's Syllabi but apparently Rails doesn't) controller, and my routes, so on a course's page there is a link to create or show the course's syllabus, and a link back to the course from the show syllabus page?
In my routes I have:
resources :courses do
resources :syllabuses
member do
put :enroll #this is so users can enroll in the course
end
end
Currently , so the course_id will be saved in the syllabus table in my courses_controller, I have:
def create_syllabus
#course = Course.find(params[:id])
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
redirect_to #syllabus, notice: "Successfully created syllabus."
else
render :new
end
end
then in my courses show page I have:
<section>
<% if (current_user.courses.includes(#course) ||
current_user.coursegroups.find_by_course_id_and_role(#course.id, "admin")) %>
<%= render 'create_syllabus' %>
<% end %>
</section>
then in my create_syllabus form (in my courses views folder) I have tried starting it off with:
# I have #course = Course.find(params[:id]) defined in show in the
#courses_controller
<%= form_for #course.create_syllabus do |f| %>
<%= form_for #course.syllabus.create_syllabus do |f| %>
<%= form_for #course.syllabus.create do |f| %>
and I get an undefined method error for each of those.
If you want to create a new syllabus in your show action of a specific course, you can add this to your controllers and views:
courses_controller.rb
#course = Course.find(params[:id])
# Build a new #syllabus object, only if there is none for the current course
unless #course.syllabus
#syllabus = #course.build_syllabus
end
views/courses/show.html.erb
# Show the syllabus name if there is one, or show the form to create a new one
<% if #course.syllabus.name %>
<p>Syllabus: <%= #course.syllabus.name %></p>
<% else %>
<p>Create Syllabus:</p>
<%= form_for([#course, #syllabus]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
syllabuses_controller.rb
def create
#course = Course.find(params[:course_id])
# Build new syllabus object based on form input
#syllabus = #course.build_syllabus(params[:syllabus])
if #syllabus.save
# redirect to /course/:id
redirect_to #course, notice: 'Syllabus was successfully created.' }
end
end
course.rb
class Course < ActiveRecord::Base
attr_accessible :name
has_one :syllabus
end
syllabus.rb
class Syllabus < ActiveRecord::Base
belongs_to :course
attr_accessible :name, :course_id
end
Some things that I left out but you should still include:
validations
rerendering form if something goes wrong
pulling things out into partials
fixing bad code like if #course.syllabus.name
pull out if/else logic into a helper
…

Resources