Once again, i am having issues with form_for.
I have an activity model, and it look like:
class Activity < ActiveRecord::Base
has_many :acdocs, dependent: :destroy, autosave: true
accepts_nested_attributes_for :acdocs,
reject_if: proc { |attributes| attributes['descr'].blank?},
allow_destroy: true
end
And i have an acdoc model.
acdoc is short for activity document. o read somewhere that if i used the word "document" i could have some issues with JavaScript... then best safe than sorry.
class Acdoc < ActiveRecord::Base
belongs_to :activity
has_attached_file :document
validates_attachment :document,
:presence => true,
content_type: { content_type: ["image/jpeg", "image/gif", "image/png", "application/pdf"] }
end
Since activity can have many acdocs, i use a form_for to handles that:
<%= f.fields_for :acdocs do |acdocs| %>
<div>
<%= acdocs.label :descr" %>
<%= acdocs.text_field :descr %>
<%= acdocs.label :document %>
<%= acdocs.file_field :document b%>
</div>
<% end %>
<p>
<%= f.submit 'add doc', :name => "add_item" %>
</p>
<div class="actions">
<%= f.submit %>
</div>
And for the controller, i use this:
def new
#activity = Activity.new
#activity.acdocs.build
end
def create
#activity = Activity.new(activity_params)
if params[:add_item]
#activity.acdocs.build
render :action => 'new'
else
respond_to do |format|
if #activity.save
format.html { redirect_to #activity, notice: 'Activity was successfully created.' }
format.json { render action: 'show', status: :created, location: #activity }
else
format.html { render action: 'new' }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
end
def update
if params[:add_item]
unless params[:activity][:acdocs_attributes].blank?
for attribute in params[:activity][:acdocs_attributes].permit!
#activity.acdocs.build(attribute.last.except(:_destroy)) unless attribute.last.has_key?(:id)
end
end
#activity.acdocs.build
render :action => 'edit'
else
respond_to do |format|
if #activity.update(activity_params)
format.html { redirect_to #activity, notice: 'Activity was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
end
This setup, kinda work. If i "add_item" a bunch of fields and select the files, all of then get uploaded.
The issue for me is, when a user press add_item, or even, edit a saved activity, the form will open. With the itens, the attach file button and the text: "no file attached". I am sure this file is meant to only tell the user were the file is being uploaded from (from his own computer) rather than the file stored on the app... but this will make the user think that no file was uploaded.
How can i put a text saying that the file is there, when it is there?
Also, this is not the fist time i have similar problems with form for. Sometimes, i want to show something if the object is already on the database. or if its a new one. (like, show a "destroy checkbox" for existing items, but hide for new ones)
How can i do these things?
You can check if the file exists. For example, you can check if the file_name attribute is set in your db, something like this acdoc.document?, or you can check if the file actually exists on the filesystem, like this: acdoc.document.exists?
show_destroy_checkbox if acdoc.document? or
show_destroy_checkobx if acdoc.document.exists?
Cheers!
Related
In my application I have a project ,for each project user can upload many assets. The Upload is done by carrier wave.
I have two questions:
1)is there a better way of writing the create method?
2) How should I change my update method to update the attached files
The model,project.rb
class Project < ActiveRecord::Base
belongs_to :user
has_many :assets
end
the asset mode, asset.rb
class Asset < ActiveRecord::Base
belongs_to :project
belongs_to :user
mount_uploader :attachment, AttachmentUploader #CarrierWave
end
This is my Create method which works fine
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.save
if params[:assets] && params[:assets]['attachment']
params[:assets]['attachment'].each do |a|
#asset = #project.assets.create!(:attachment => a, :user_id=>#project.user.id)
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
end
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
def project_params
params.require(:project).permit(:user_id, :summary, :start_date,assets_attributes: [:id, :project_id, :attachment,:user_id] )
end
here is the Update method
def update
respond_to do |format|
if #project.update(project_params)
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
You can use the accepts_nested_attributes_for method built into active record to easily handle creating, updating, and destroying nested models
http://railscasts.com/episodes/196-nested-model-form-revised?view=asciicast
EDIT FOR QUESTION CHANGES
You seem to have gone through many revisions to your original question, let me see if I can help clear some things up for you.
Let's assume that your asset class has a column called attachment and you have a carrierwave uploader class called AttachmentUploader. You can then mount that class to your asset class like you show in your question
mount_uploader :attachment, AttachmentUploader #CarrierWave
Your not showing your form markup in the question, but based on your controller's project_params method it seems that you're expecting a property called asset_attributes. You'll want to make sure that your form correctly builds that array, most easily accomplished by using the fields_for helper. If your confused about that part refer to this question Rails 4 Nested Attributes Unpermitted Parameters
Basically it should look like this
projects/_form.html.erb
<%= form_for #project, :html => {:multipart => true} do |f| %>
<%= f.error_messages %>
<%= f.fields_for :assets do |builder| %>
<%= render 'assets_fields', :f => builder %>
<% end %>
<p><%= f.submit %></p>
<% end %>
projects/_assets_fields.html.erb
<p>
<%= f.file_field :attachment %>
</p>
When the project form is submitted you don't need to loop over the assets manually determining if they are existing assets that are being updated, new assets that are being created, or assets flagged for deletion. That logic is all handled for you by adding this to your project class
accepts_nested_attributes_for :assets, :allow_destroy => true
Just calling
project.save
or
project.update
will automatically create/update/destroy the nested assets wrapping the whole process in a transaction so it all succeeds or all fails. The only expection to that being that active record can't wrap carrier wave actions in a database transaction so you may end up with orphaned file uploads on your server.
I have the following:
Clients have many Reports and Reports belong to a client.
However on the creation of the Report it is not assigning the client_id into the database, but not sure why?
Am i doing something wrong here?
Client Model
class Client < ActiveRecord::Base
has_many :reports, :dependent => :destroy
end
Report Model
class Report < ActiveRecord::Base
has_attached_file :report
belongs_to :client
end
Client Controller (Update)
# PUT /clients/1
# PUT /clients/1.json
def update
#client = Client.find(params[:id])
respond_to do |format|
if #client.update_attributes(params[:client])
format.html { redirect_to [:admin,#client], :notice => 'Client was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #client.errors, :status => :unprocessable_entity }
end
end
end
Report Controller (Create)
# POST /reports
# POST /reports.json
def create
#report = Report.new(params[:report])
#report.client_id = params[:client][:client_id]
respond_to do |format|
if #report.save
format.html { redirect_to '/admin/clients', :notice => 'Report was successfully created.' }
format.json { render :json => #report, :status => :created, :location => #report }
else
format.html { render :action => "new" }
format.json { render :json => #report.errors, :status => :unprocessable_entity }
end
end
end
Client Edit View
<%= form_for([:admin, #client.reports.build]) do |f| %>
<label class="formlabel">Report Upload</label>
<%= f.file_field :report, :class=>"text-input small-input" %>
<div class="actions">
<br />
<%= f.submit 'Upload', :class => 'button' %>
</div>
<% end %>
Assistance would be appreciated!
I'm curious; because you're using .build in the form_for, the client may already be in the url.
What if you remove:
#report.client_id = params[:client][:client_id]
and submit, what happens then? Because this line is looking incorrectly at the params, so I wonder if you are overwriting that you built in the form_for
Either that, or a hidden field like #Adam said would work.
The client_id doesn't have a related input field in the form on your view. You could add something to your form like:
f.hidden_field :client_id
And then in your controller, set it as:
#report.client_id = params[:report][:client_id]
Alternatively, you could include the client_id in the url.
Stupid Mistake it seems needed to up the end function on the form-for for the client to close it off before opening the form-for the reports.
Then add the field for the client_id and now just hide the field as per Adam suggestion.
Thanks Steph for suggestions as this help me solve this mistake.
Thanks Everyone! :-)
I'm working on my first rails project and am trying to use a file_field to upload photos that belongs_to :album. The form appears on my albums/show.html.erb page, since I wanted to show the album and have the user be able to upload pics from one place. However, when I press submit on the form, it doesn't seem to upload.
here is my photos/_form.html.erb
<%= form_for(#photo, :html => { :multipart => true }, :url => { :action => 'create'} ) do |f| %>
<%= f.label :avatar %>
<%= f.file_field :avatar %>
<%= f.submit %>
<% end %>
this is my albums/show.html.erb page. I added the if/else statement just to test if the #album instance was receiving the picture I uploaded, but it always comes back with "no"
<% if #album.photos.any? %>
yes
<% else %>
no
<% end %>
<div>
<%= render 'photos/form' %>
</div>
photos controller (i'm really confused as to what to set the instance variables in this)
class PhotosController < ApplicationController
def create
#album = Album.find(params[:user_id][:album_id])
#photo = #album.build(params[:photo])
respond_to do |format|
if #album.save
format.html { redirect_to #album, notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album}
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
album model
class Album < ActiveRecord::Base
attr_accessible :avatar, :name, :description
has_many :user_albums
has_many :users, :through => :user_albums
has_many :photos
end
photo model
class Photo < ActiveRecord::Base
belongs_to :album
end
Let me know if you need any other files
You will need,
#album.photos.build(params[:photo])
Also, I assume that your uploading mechanism is correct. :)
Good luck
If I leave the input box blank. I get this error everytime. I don't want it to make new record when it's blank. when not, I want it to make new record.
this input box is nested and the code of controller is written like this to avoid error
def create
# Check if there is any contact info added
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
#girl = Girl.new(params[:girl])
respond_to do |format|
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
end
end
view is like this
<%= form_for(#girl) do |f| %>
....
<div class="field">
<%= f.label :mail %><br />
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
<%= contact.text_field :mail %>
<% end %>
</div>
....
<div class="actions">
<%= f.submit %>
</div>
<% end %>
my model
class Girl < ActiveRecord::Base
has_many :users
has_one :contact
accepts_nested_attributes_for :contact
attr_accessible :id, :name_en, :name_ja, :gender_id, :contact_attributes, :photo, :tag_list
searchable do
text :name_en, :name_ja
text :contact do
contact.mail
end
end
has_attached_file :photo,
:styles => {
:thumb=> "100x100>",
:small => "400x400>" }
acts_as_taggable_on :tags
acts_as_commentable
end
You have to set
#girl = Girl.new
inside your else block, just before
format.html { render action: "new" }
The error happens because you render the new template and inside it the form_for(#girl) gets a nil object - #girl. In order to render the line <%= f.label :mail %><br /> it tries to call the mail method on the given #girl object in order to get its default value. Since the #girl object is nil and not set in the create action before you render the new template you get this error.
UPDATE:
I misunderstood your situation in the answer on the first part of this post. The solution in my opinion is redirecting to the new girl path instead of just rendering the new action. While rendering only renders the view redirecting will make a full-stack request process. Assuming you have the route new_girl_path set you should replace format.html { render action: "new" } with
format.html { redirect_to new_girl_path }
You can run `rake routes and see what named routes you have set.
I problem is the following few lines of code.
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
If mail is empty in user contact you have removed the contact attributes and created only the user object.
So if you call #girl.contact you will get nil.
I don't know why you have removed the contact attributes.If you still want to do it you need to add one more line.
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
#Assuming you have the association like: user has_one contact
#user.build_contact
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
And one more thing
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
can be simply written as
<%= f.fields_for :contact do |contact| %>
Replace same line of code with <%= form_for( :girl, :url => {:action => :create}) do |f| %>
I'm saving nested objects within the objects they belong to, but when i do that they do not use the controller im saving but the parents controller.
class Project < ActiveRecord::Base
belongs_to :company
belongs_to :user
has_many :tasks
accepts_nested_attributes_for :tasks, :allow_destroy => true
end
in the views i have something like this
<% form_for #project do |c| %>
<% c.fields_for :tasks, #project.tasks.last do |p| %>
<%= p.text_field :name %>
<% end %>
<%= submit_tag '+' %>
<% end %>
so what i'm trying to do, is update the user field with the fields for, that last field is specified in the controller.
def show
#project = Project.find(params[:id])
#project.tasks.build
#project.tasks.last.user = current_user # this should pass to the show.html.erb, to be saved back
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #project }
end
end
I'm thinking maybe the solution would be to check if the username is set in the nested objects, and if not to populate it with the current user in:
def update
#project = Project.find(params[:id])
#project.user = current_user
#find anything #project.....user blank and set to current user
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to(#project, :notice => 'Project was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #project.errors, :status => :unprocessable_entity }
end
end
end
I'm hoping that is the solution, and how do it do it?
an example of it running currently is at http://severe-fire-37.heroku.com