Rails: pictures not uploading? - ruby-on-rails

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

Related

Rails, form_for and paperclip

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!

Rails 4: checkbox and has_many through

This example has been taken from Rails 4 Form: has_many through: checkboxes
models:
#models/user.rb
class User < ActiveRecord::Base
has_many :animals, through: :animal_users
has_many :animal_users
end
#models/animal.rb
class Animal < ActiveRecord::Base
has_many :users, through: :animal_users
has_many :animal_users
end
#models/animal_user.rb
class AnimalUser < ActiveRecord::Base
belongs_to :animal
belongs_to :user
end
The user form:
#views/users/_form.html.erb
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
# Checkbox part of the form that now works!
<div>
<% Animal.all.each do |animal| %>
<%= check_box_tag "user[animal_ids][]", animal.id, f.object.animals.include?(animal) %>
<%= animal.animal_name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Strong params within the users_controller.rb
def user_params
params.require(:user).permit(:name, animal_ids: [])
end
I followed this example and could not save the join table. I have two question here
What type should be animal_ids, string or integer?
How to save the form?
Currently I am saving it like this
def create
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'user was successfully created.' }
format.json { render json: #user, status: :created, location: #user}
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
And this only create user but not the join table. How can I do this?
#user.save is not passing in the nested attributes (animal_ids)
You'll need to pass the params like this:
#user = User.new(user_params)
And in your User model (user.rb) you need to add something like:
accepts_nested_attributes_for :animals
accepts_nested_attributes_for :animal_users

How to avoid the error undefined method 'mail' for nil:NilClass

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

Rails: Form for selecting an existing parent when creating new child records?

I have a has_many and belongs_to association set up between two models: Project and Task.
I'd like to be able to create a form which enables me to create a new Task and assign an existing Project as a parent. For example, this form might have a pulldown for selecting from a list of existing projects.
There are only a finite set of projects available in this application, so I've created Project records via a seeds.rb file. I do not need to make a form for creating new Projects.
I believe I've achieved a solution by using a collection_select form helper tag in the new Task form. I'm pretty happy with how this works now, but just curious if there are other approaches to this problem.
#models/project.rb
class Project < ActiveRecord::Base
has_many :tasks, :dependent => :destroy
end
#models/task.rb
class Task < ActiveRecord::Base
belongs_to :project
end
#controllers/tasks_controller.rb
class TasksController < ApplicationController
def new
#task = Task.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #task }
end
end
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to(#task, :notice => 'Task was successfully created.') }
format.xml { render :xml => #task, :status => :created, :location => #task }
else
format.html { render :action => "new" }
format.xml { render :xml => #task.errors, :status => :unprocessable_entity }
end
end
end
end
#views/new.html.erb
<h1>New task</h1>
<%= form_for(#task) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="select">
<%= collection_select(:task, :project_id, Project.all, :id, :name) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', tasks_path %>
I just reviewed your code and this looks fantastic to me. One small tweak:
<%= f.collection_select(:project_id, Project.all, :id, :name) %>
This is just slightly cleaner in that you're still using the |f| block variable
Since you mentioned other approaches, I would definitely mention and actually recommend, you use formtastic. The associations are handled automatically and keeps your code clean and also gives you some great customization options.

Rails form_for model in different controller

I have the following model classes:
class Upload < ActiveRecord::Base
...
has_many :reviews, :order => "created_at DESC"
...
end
class Review < ActiveRecord::Base
...
belongs_to :upload
belongs_to :user
validates_presence_of :description
...
end
My upload/show view has a form for to capture a review for the specific upload:
<% form_for(#review) do |f| %>
<%= f.error_messages %>
...
<p>
<%= f.text_area :description, :rows => 5, :cols => 80 %>
</p>
...
<p>
<%= f.submit 'Submit Review' %>
</p>
<% end %>
When the review validation fails how do I display the error messages in the review form that is part of the upload/show view?
My ReviewController does this:
def create
#review = current_user.reviews.new(params[:review])
if #review.save
flash[:notice] = 'Review was successfully created.'
redirect_to( #review.upload )
else
render :action => :new
end
end
Obviously render :action => :new does not work because I need to display the show action of the UploadsController and not the new action of the ReviewsController.
I'm pretty sure there is a simple way to do this, I just can't figure it out!
Your review controller action should be receiving params['upload_id'] to associate the review with its upload, either through the URL (if reviews are a nested route like POST /uploads/1/reviews), or from a hidden field.
You can use render :template to do your redirection:
def create
#review = current_user.reviews.new(params[:review])
#upload = Upload.find(params['upload_id'])
#review.upload = #upload
if #review.save
flash[:notice] = 'Review was successfully created.'
redirect_to( #upload )
else
flash[:error] = 'Review could not be created.'
render :template => 'uploads/show'
end
end
It's also acceptable to just render the form for the review by itself (i.e. the default 'reviews/new') until the form entry is correct instead of showing the whole page for the upload.

Resources