adding a new column **attachment** in existing table in ruby on rails - ruby-on-rails

Ok So I have a simple rails application called ticket. here I create ticket and store into database. I generated it using scaffold.
It has 5 columns. ( I generated it using scaffold)
Name
Seat id seq
Address
Price paid
Email Address
And the application is working fine. I can create , edit, and update the ticket. I am much interested in creating the ticket rather than editing and deleting.
Now I want to add a new column in the database named attachment where a person can upload word, pdf files. I have seen many tutorials but none is explaining how I can incorporate into an existing table which already have some fields.
tickets_controller.rb
class TicketsController < ApplicationController
before_action :set_ticket, only: [:show, :edit, :update, :destroy]
def index
#tickets = Ticket.all
end
def show
end
def new
#ticket = Ticket.new
end
def edit
end
def create
#ticket = Ticket.new(ticket_params)
respond_to do |format|
if #ticket.save
format.html { redirect_to #ticket, notice: 'Ticket was successfully created.' }
format.json { render :show, status: :created, location: #ticket }
else
format.html { render :new }
format.json { render json: #ticket.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #ticket.update(ticket_params)
format.html { redirect_to #ticket, notice: 'Ticket was successfully updated.' }
format.json { render :show, status: :ok, location: #ticket }
else
format.html { render :edit }
format.json { render json: #ticket.errors, status: :unprocessable_entity }
end
end
end
def destroy
#ticket.destroy
respond_to do |format|
format.html { redirect_to tickets_url, notice: 'Ticket was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_ticket
#ticket = Ticket.find(params[:id])
end
def ticket_params
params.require(:ticket).permit(:name, :seat_id_seq, :address, :price_paid, :email_address)
end
end
tickets Model
class Ticket < ApplicationRecord
end
_form.html.erb
<%= form_for(ticket) do |f| %>
<% if ticket.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(ticket.errors.count, "error") %> prohibited this ticket from being saved:</h2>
<ul>
<% ticket.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :seat_id_seq %>
<%= f.text_field :seat_id_seq %>
</div>
<div class="field">
<%= f.label :address %>
<%= f.text_area :address %>
</div>
<div class="field">
<%= f.label :price_paid %>
<%= f.text_field :price_paid %>
</div>
<div class="field">
<%= f.label :email_address %>
<%= f.text_field :email_address %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
new.html.erb
<h1>New Ticket</h1>
<%= render 'form', ticket: #ticket %>
<%= link_to 'Back', tickets_path %>
I found some on some of the sources that to upload a document we need to create a table with three columns ( filename, content_type, data) , and I thought to add these column into the existing tickets table, but then I don't know what chnages i need to make in the new.html.erb file. On this file i am loading the form where you can enter the information, and I want to show a field to upload a file on this page.
ruby -v : 2.2.6p396
rails -v : 5.0.0.1 ( Please reply in way that I can use the solution on the rails 4.0 too)

As #Brad mentioned you can use paperclip or carrierwave to add attachment. I will use paperclip in this example. ( Note: I'm using rails 5.0.0.1 in this example, and comand rails db:migrate. in Rails 4.x it will be rake db:migrate)
Let's do some scaffolding first and then you can alter your code to suits your needs.
rails g scaffold User name:string
rails g scaffold Post title:string body:text user:references
Add has_many :posts to User model, and run rails db:migrate
Add gem file: gem "paperclip", "~> 5.0.0" and run: bundle install
Let's generate paperclip attachment: rails generate paperclip user docs and run rails db:migrate
Add attachment, validation and content type to our User model:
has_attached_file :docs
validates_attachment :docs, :content_type => {:content_type => %w(application/pdf application/msword application/vnd.openxmlformats-officedocument.wordprocessingml.document)}
Add to users_controller.rb strong params for attribute whitelisting :
def user_params
params.require(:user).permit(:name, :docs)
end
The form’s encoding must be set to multipart/form-data for uploading files html: { multipart: true } so for our attachment we need to alter our form partial app/views/users/_form.html.erb:
<%= form_for #user, html: { multipart: true } do |f| %>
And add required fields to our form:
<div class="field">
<%= f.label :docs %>
<%= f.file_field :docs %>
</div>
After restarting our server rails s, and going to url users/new we can see new field attachment. After uploading a document we can check in our console if it's uploaded: rails c and run for example User.all, which shows that our fields in DB are populated:
And the last step would be to show the document in our views app/views/users/show.html.erb so that users can download or open it in the browser:
<%= link_to "My document", #user.docs.url, target: "_blank" %>
EDIT
Let's get back to your project and do it:
First you should add Paperclip gem to your Gemfile gem "paperclip", "~> 5.0.0" and to run bundle install in command line.
Generate attachment: rails generate paperclip ticket attachment ( This will generate the migration file and add attachment field to it )
run rails db:migrate ( rake db:migrate in Rails 4 )
go to Ticket model and add:
has_attached_file :attachment
validates_attachment :attachment, :content_type => {:content_type => %w(application/pdf application/msword application/vnd.openxmlformats-officedocument.wordprocessingml.document)}
Then to tickets_controller.rb and edit strong params:
def ticket_params
params.require(:ticket).permit(:name, :seat_id_seq, :address, :price_paid, :email_address, :attachment)
end
Go to your partial _form.html.erb:
Edit this code below in your form:
<%= form_for #ticket, html: { multipart: true } do |f| %>
Add this code below to your form: (That would be new field with attachment )
<div class="field">
<%= f.label :attachment %>
<%= f.file_field :attachment %>
</div>
and the last step to your show page add:
<%= link_to "My document", #ticket.attachment.url, target: "_blank" %>
restart your server, upload the document and try if it works.

For adding attachments you can use a gem like carrierwave or paperclip. I personally use paperclip but both are good.
To do it manually:
In your terminal cd to app folder and run:
rails g migration AddAttachmentToTickets attachment:blob
run rails 4 rake db:migrate or in rails 5 rails db:migrate
in your ticket form you can now add:
<div class="field">
<%= f.label :attachment %>
<%= f.file_field :attachment %>
</div>
and make sure you permit this in your ticketsController params. It should look something like this:
def ticket_params
params.require(:ticket).permit(:ticket_number,:description, :attachement)
end
Hope this helps.

Related

Can't get a custom edit form to save

I want to have a form to just edit one field for my user's model that is separate from the scaffold generated _form.erb.
The form will show but it will not save. When I modify the def in the controller with a respond_to block, the form is bypassed and I just get the record shown.
employee_supervisor_edit.html.erb has <%= render 'employee_supervisor_form' %>
routes.rb contains match '/employee_supervisor_edit/:id' => 'users#employee_supervisor_edit' , via: [:get, :post ]
the form is _employee_supervisor_form.erb
users_controller.rb has
def employee_supervisor_edit
#users = User.all
#user = User.find(params[:id])
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
I also have have the following in my users controller.
def user_params
# params.require(:user).permit(:login,
params.permit(:login,
:group_strings,
:name,
:ou_strings,
:email,
:active_employee,
:last_name,
:first_name,
:is_supervisor,
:#supervisor_id)
end
end
If I comment out the whole respond_to block, the form appears but no data is saved. If I put the respond_to block in, then the form is bypassed and it goes right to the show method.
I'm not sure if the problem is related to getting the following error if I use params.require(:user).permit(:login, instead of params.permit(:login,
ActionController::ParameterMissing in UsersController#employee_supervisor_edit
param is missing or the value is empty: user
Rails.root: C:/Users/cmendla/RubymineProjects/employee_observations
Application Trace | Framework Trace | Full Trace
app/controllers/users_controller.rb:134:in `user_params'
app/controllers/users_controller.rb:16:in `block in employee_supervisor_edit'
app/controllers/users_controller.rb:15:in `employee_supervisor_edit'
========== added ==============
I have the following associations in my user.rb
Class User < ActiveRecord::Base
has_many :subordinates, class_name: "User", foreign_key: "supervisor_id"
belongs_to :supervisor, class_name: "User"
======== added : =====================
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
User Login: <%= #user.login %><br>
User Name: <%= #user.name %> <br>
<div class="field">
<%= f.label :active_employee %>
<%= f.check_box :active_employee %>
</div>
<div class="field">
<%= f.label :supervisor %>
<%= f.collection_select(:supervisor_id, User.order('name'), :id, :name, prompt: true)%>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The problem stemmed from my data. Since I am in the development process, I don't have all the error checking I need in place. Null fields or associations pointing to non existent records cause errors where it isn't always obvious (at least to me) that the problem is the data, not the code itself.
I went in with an sql editor and made sure that the contents causing the issues were not set to null and that the columns such as supervisor_id were pointing to actual existing records, not records that were deleted.
I changed params.permit(:login, back to params.require(:user).permit(:login, and now the form is saving as expected.
My next step will be to add validations for input and some error checking for the index and show methods. For the long term, I think that I need to become more proficient with testing as that might show areas that could cause these types of issues.

Basic Rails - how do automatically assign a new database entry to an associated entry it belongs to?

I'd like to automatically associate a new database entry with the database entry it belongs to without having to make a choice while on the form as the user can only come from the category page, so that once you're in a category and you decide to make a new entry within that category, the newly created entry is automatically within that category upon submission. Can anyone offer any help?
My models are as follows:
class Category < ActiveRecord::Base
has_many :guides
end
class Guide < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :ratings
def average_rating
average = ratings.inject(0.0){ |sum, el| sum + el.value }.to_f / ratings.size
average.round(2)
end
end
The link to create the new guide for the category is pretty standard, though I thought that adding an instance variable might automatically associate the entry with the category though it doesn't:
<%= link_to 'New Guide', new_guide_path(#category) %>
Here is the controller for the guide:
class GuidesController < ApplicationController
before_action :set_guide, only: [:show, :edit, :update, :destroy]
# GET /guides
# GET /guides.json
def index
#guides = Guide.all
end
# GET /guides/1
# GET /guides/1.json
def show
end
# GET /guides/new
def new
#guide = Guide.new
end
# GET /guides/1/edit
def edit
end
# POST /guides
# POST /guides.json
def create
#guide = Guide.new(guide_params)
respond_to do |format|
if #guide.save
format.html { redirect_to #guide, notice: 'Guide was successfully created.' }
format.json { render :show, status: :created, location: #guide }
else
format.html { render :new }
format.json { render json: #guide.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /guides/1
# PATCH/PUT /guides/1.json
def update
respond_to do |format|
if #guide.update(guide_params)
format.html { redirect_to #guide, notice: 'Guide was successfully updated.' }
format.json { render :show, status: :ok, location: #guide }
else
format.html { render :edit }
format.json { render json: #guide.errors, status: :unprocessable_entity }
end
end
end
# DELETE /guides/1
# DELETE /guides/1.json
def destroy
#guide.destroy
respond_to do |format|
format.html { redirect_to guides_url, notice: 'Guide was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_guide
#guide = Guide.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def guide_params
params.require(:guide).permit(:name, :category_id, :user_id, :stepOneText, :stepOnePhoto, :stepTwoText, :stepTwoPhoto, :stepThreeText, :stepThreePhoto)
end
end
Form is pretty standard too, is there anything I should put in here to automatically assign it to the category entry it belongs to?
<%= form_for(#guide) do |f| %>
<% if #guide.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#guide.errors.count, "error") %> prohibited this guide from being saved:</h2>
<ul>
<% #guide.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :stepOneText %>
<%= f.text_field :stepOneText %>
</div>
<div class="field">
<%= f.label :stepOnePhoto %>
<%= f.text_field :stepOnePhoto %>
</div>
<div class="field">
<%= f.label :stepTwoText %>
<%= f.text_field :stepTwoText %>
</div>
<div class="field">
<%= f.label :stepTwoPhoto %>
<%= f.text_field :stepTwoPhoto %>
</div>
<div class="field">
<%= f.label :stepThreeText %>
<%= f.text_field :stepThreeText %>
</div>
<div class="field">
<%= f.label :stepThreePhoto %>
<%= f.text_field :stepThreePhoto %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Seems to me that you could go for something like a nested route here :
resources :categories do
resources :guides
end
and then use your new route
<%= link_to 'New Guide', new_category_guide_path(#category) %>
This should make it easier to get the guide's category back when getting the form back.
Assuming that you have a column on your guide table that stores category ID, and your routes are nested as has been recommended, you should be able to add
#guide.category_id = #category.id
To your guides controller create action. And in your form change the first line to
<%= form_for[#category, #guide] do |f| %>
Now this should work
<%= link_to 'new guide', new_category_guide_path(#category) %>
And the current category should be assigned to your guide when it's created.

Carrierwave, Rails 4, and Multiple Uploads

I have been banging my head against the wall trying to get Carrierwave, Rails 4, and Multiple Uploads all working together. I can get a single file upload working just fine as in this and many other projects.
This is not a nested situation - just simply uploading to a single model called Transcription and wanting to create a record for each document uploaded.
I cannot seem to find the correct way to declare the "document" field used for the carrierwave mount
mount_uploader :document, DocumentUploader
as an array for the strong parameters to recognize.
I have tried whitelisting: whitelisted[:document] = params[:transcription]['document'],
declaring the "document" as an array:
params.require(:transcription).permit(..... ,:document => [])
params.require(:transcription).permit(..... , { document: [] })
This all seems more like I am declaring the array for a nested model, but I really want Rails 4's strong parameters to simply see the "document" array created by the file_field, :multiple => true
ie. from the log: form-data; name=\"transcription[document][]
Has anybody successfully accomplished multiple uploads in Rails 4 with strong parameters? If so would you please share?
Thanks...
Cheers,
Bill
This is solution to upload multiple images using carrierwave in rails 4 from scratch
To do just follow these steps.
rails new multiple_image_upload_carrierwave
In gem file
gem 'carrierwave'
bundle install
rails generate uploader Avatar
Create post scaffold
rails g scaffold post title:string
Create post_attachment scaffold
rails g scaffold post_attachment post_id:integer avatar:string
rake db:migrate
In post.rb
class Post < ActiveRecord::Base
has_many :post_attachments
accepts_nested_attributes_for :post_attachments
end
In post_attachment.rb
class PostAttachment < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :post
end
In post_controller.rb
def show
#post_attachments = #post.post_attachments.all
end
def new
#post = Post.new
#post_attachment = #post.post_attachments.build
end
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a, :post_id => #post.id)
end
format.html { redirect_to #post, notice: 'Post was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
def update
respond_to do |format|
if #post.update(post_params)
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a, :post_id => #post.id)
end
end
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to #post }
format.json { head :no_content }
end
end
private
def post_params
params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
end
In views/posts/_form.html.erb
<%= form_for(#post, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<%= f.fields_for :post_attachments do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
</div>
<% end %>
<% if params[:controller] == "post" && params[:action] == "edit" %>
<% #post.post_attachments.each do |p| %>
<%= image_tag p.avatar, :size => "150x150" %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In views/posts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<% #post_attachments.each do |p| %>
<%= image_tag p.avatar_url, :size => "150x150" %>
<%= link_to "Destroy", p, method: :delete %>
<% end %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Back', posts_path %>
In rails 3 no need to define strong parameters and as you can define attribute_accessible in both the model and accept_nested_attribute to post model because attribute accessible is deprecated in rails 4.
CarrierWave doesn't support multiple uploads. It's designed to associate a single file with a single field.
If you want multiple uploads, you need either multiple fields (each with a CarrierWave uploader), or multiple objects each with a single CarrierWave uploader field.
The multiple attribute is also unsupported, so if you use it, it's entirely up to you to get the parameters assigned properly.
I would create a model called Documents with a field that's mounted
class Documents < ActiveRecord::Base
belongs_to :transcription
mount_uploader :doc, DocumentUploader
end
class Transcriptions < ActiveRecord::Base
has_many :documents
end
And I would still have user the below line in my controller:
params.require(:transcription).permit(..... , { document: [] })
The best method for this that I have come across is using the native approach of CarrierWave. If you already have single file upload done, with the native approach it takes less than 5 minutes to get multiple file upload. https://github.com/carrierwaveuploader/carrierwave#multiple-file-uploads

Rails form adding empty entry via model

I am new to ruby, trying to follow the official documentation and create a basic form for creating a post:
<%= form_for #post, :url => { :action => "create" }, :html => {:class => "nifty_form"} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :entry, :size => "60x12" %>
<%= f.submit "Create" %>
<% end %>
The form is successfully adding an entry to the database, but an empty one, I think I must be missing something in my controller? Do I need to pass the variables somehow?
def create
#post = Main.create
end
A basic create action can look like this. You first initialize a new post. Depending on if it successfully saves you proceed.
# app/controllers/posts_controller.rb
class PostsController < ActionController::Base
def create
#post = Post.new(params[:post])
if #post.save
redirect_to #post, notice: 'Post has been created.'
else
render :new
end
end
end
You can shorten your form.
<%= form_for #post do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.text_area :entry, :size => "60x12" %>
<%= f.submit %>
<% end %>
You can see excellent example code along these lines when you generate a scaffold, so I would encourage you to try $ rails generate scaffold Post title body:text and learn by example.
Submitting a form passes the values entered into that form (along with some other information) to the controller as a hash called "params" - the params will contain a block labelled with the name of the form, in this case "post".
You need to use the post block from params in the creation of the new object.
def create
#post = Main.new(params[:post])
if #post.save
# handles a successful save
else
# handles validation failure
end
end
Try:
#post = Main.new(params[:post])
#post.save

Rails nested form error: undefined method `update_attributes' for nil:NilClass

I'm using Mongoid, awesome_nested_fields gem and rails 3.2.8.
I have the functionality working on the client (adding multiple fields), but when I try to save I get a "undefined method `update_attributes' for nil:NilClass" error.
Here is all the relevant info:
profile.rb
class Profile
include Mongoid::Document
include Mongoid::Timestamps
has_many :skills, :autosave => true
accepts_nested_attributes_for :skills, allow_destroy: true
attr_accessible :skills_attributes
end
skill.rb
class Skill
include Mongoid::Document
belongs_to :profile
field :skill_tag, :type => String
end
View
<%= simple_form_for(#profile) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="items">
<%= f.nested_fields_for :skills do |f| %>
<fieldset class="item">
<%= f.input :skill_tag, :label => 'Skills:' %>
Remove Skill
<%= f.hidden_field :id %>
<%= f.hidden_field :_destroy %>
</fieldset>
<% end %>
</div>
Add Skill
</div>
<div class="form-actions">
<%= f.button :submit, :class => 'btn' %>
</div>
<% end %>
profiles_controller.rb
# PUT /profiles/1
# PUT /profiles/1.json
def update
#profile = Profile.find(params[:id])
respond_to do |format|
if #profile.update_attributes(params[:profile])
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' } # Notice
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
*********** UPDATE ************
I ended up switching to Ryan's nested_form gem and it works like a charm.
Your message directly points to the line of execution where it failed, ergo the line:
if #profile.update_attributes(params[:profile])
The problem is that the #profile instance variable is nil and rails can not find the update_attributes method for a nil object.
You could easly check your server log for the params hash, to see what params[:id] (probably it isn't defined at all) by going in the terminal where you started your app.
Or you can check your development log when being in your app folder:
tail -n 1000 development.log | egrep params

Resources