Rails: Photo uploads (Paperclip) with nesting & strong params - ruby-on-rails

Background:
I'm trying to add photo uploads to an Advert model using Strong Params with the Paperclip gem, where Photo is a separate model which "has_attached_file" :upload, and I am calling forms_for (actually semantic_forms_for as I'm using Formtastic) in the Ads#new view to upload the image to the Photo instance. Params looks ok but I think I've missed something in the controller. Haven't been able to get it working despite multiple iterations of the controller code. What would I need to do differently to get it working?
Would appreciate any tips or pointers! Thank you
--
Ad model:
class Ad < ActiveRecord::Base
belongs_to :user
##edited out irrelevant associations
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, :allow_destroy => true
default_scope { order("created_at DESC") }
#validates_presence_of :user_id, :make, :model, :km, :year, :location
end
--
Photo model:
class Photo < ActiveRecord::Base
belongs_to :ad
has_attached_file :upload, :styles => { :main => "600x500>",
:thumb => "60x45>" },
:default_url => "http://placehold.it/600x500&text=nice+wheels!"
end
AdsController
class AdsController < ApplicationController
def create
#ad = Ad.new(ad_params)
#ad.user_id = current_user.id
#photo = #ad.photos.build
if #ad.save
redirect_to #ad
else
flash.alert="You were missing some super-important details. Try again"
redirect_to new_ad_path
end
end
private
def ad_params ##excluded all attributes except nested upload attrib. for gist
params.require(:ad).permit(photos_attributes: [upload: [:upload_file_name, :upload_content_type]])
end
end
Ads#new
<%= semantic_form_for #ad do |form| %>
<!-- left out all other form fields -->
<%= form.semantic_fields_for :photo do |photo| %>
<%= photo.file_field :upload %>
<% end %>
<% end %>
Ad params hash after submit action (Ad#create)
{"location":"Abha","make":"Bentley","model":"","gear":"","km":"50","price_bid":"500","price_ask":"500","ac":"0","cd":"0","radio":"0","powersteering":"0","abs":"0","satnav":"0","cruise":"0","fwd":"0","convertible":"0","problem_1":"","problem_2":"","problem_3":"","description":"","photo":{"upload":{"original_filename":"mustang-290x218.jpg","content_type":"image/jpeg","headers":"Content-Disposition: form-data; name=\"ad[photo][upload]\"; filename=\"mustang-290x218.jpg\"\r\nContent-Type: image/jpeg\r\n","tempfile":[]}}}
Routes
resources :ads, only: [:new, :create, :show, :index, :edit, :destroy] do
resources :comments, only: [:create, :destroy]
resources :photos, only: [:create, :destroy]
resources :favorite_ads, only: [:create, :destroy]
resource :emails, only: :create
end

The form needs to be specified as multi-part for file uploads to work:
<%= semantic_form_for #ad, :html => {:multipart => true} do |form| %>
In addition, you aren't permitting the tempfile to be passed:
params.require(:ad).
permit(photos_attributes:
[upload:
[:upload_file_name, :upload_content_type, :tempfile]
])

params.require(:ad).permit(photos_attributes: :upload)
this should be enough.

Related

How to update a nested form - Ruby on Rails

I am having an issue with my update method in my recipes controller where I cannot create a new category. Category is a nested resource within recipes. This is my update method
def update
if current_user.id != #recipe.category.user_id
flash.now[:notice] = "You cannot update a recipe that you did not author."
redirect_to recipes_path
else
#recipe.update(recipe_params)
flash.now[:notice] = "#{#recipe.title} has been updated."
redirect_to recipe_path
end
if #category
recipe.build_category
end
end
I just recently added the if #category recipe.build_category but that didn’t do anything.
This is my recipe strong params:
def recipe_params
params.require(:recipe).permit(
:title,
:description,
:category_id,
category_attributes: [:name],
instructions_attributes: [:id,
:step, :_destroy
]
)
end
And my recipes controller before actions:
before_action :redirect_if_not_logged_in
before_action :find_recipe, only: [:show, :edit, :update, :destroy]
before_action :find_category, only: [:index, :new, :create]
I also have this language in my recipes _form:
<%= f.fields_for :category, recipe.build_category do |cb| %>
<div style="margin-left:30px">
<%= cb.label :name %>
<%= cb.text_field :name %>
</div>
<% end %>
and then this in my recipes edit.html.erb
<%= render partial: "form", locals: {recipe: #recipe, category: #category, button_name: "Update Recipe"}%>
These are my recipes & categories routes:
resources :recipes, only: [:index, :show, :new]
end
resources :recipes do
resources :categories, only: [:index, :new, :create]
end
resources :users, only: [:show, :new] do
resources :recipes, only: [:index]
end
This is my recipes model:
belongs_to :category
accepts_nested_attributes_for :category, reject_if: :all_blank
has_many :instructions, dependent: :destroy
accepts_nested_attributes_for :instructions, reject_if: :all_blank, allow_destroy: true
validates :title, presence: true
validates :instructions, presence: true
validates :description, presence: true
end
Does anyone know why I wouldn’t be able to create the category within an existing recipe?
**Side note - my flash errors are also not working. Thanks in advance for any help.

Bidirectional many-to-many association with Rails : how to create from both ways?

I have a question about many-to-many associations in Ruby on Rails.
I have 3 models in my app : Topic, Meeting and Todo associated with a manu-to-many association.
class Todo < ApplicationRecord
belongs_to :topic
belongs_to :meeting
end
then
class Meeting < ApplicationRecord
has_many :todos
end
and
class Topic < ApplicationRecord
has_many :todos
end
I made my routes and controller to be able to create new todos via a meeting :
Rails.application.routes.draw do
resources :meetings, only: [:index, :show, :new, :create, :edit, :update] do
resources :todos, only: [:index, :new, :create]
end
resources :todos, only: [:index, :show, :edit, :update, :destroy]
end
and
class TodosController < ApplicationController
def new
#topic = Topic.find(params[:topic_id])
#todo = Todo.new
end
def create
#todo = Todo.new(todo_params)
#meeting = Meeting.find(params[:meeting_id])
#todo.meeting = #meeting
#todo.save
redirect_to meeting_path(#meeting)
end
private
def todo_params
params.require(:todo).permit(:topic_id, :meeting_id, :note, :deadline, :title)
end
end
and my view :
<h3><%= #meeting.date %></h3>
<%= simple_form_for [#meeting, #todo] do |f| %>
<%= f.input :title %>
<%= f.input :note %>
<%= f.date_field :deadline %>
<%= f.association :topic, label_method: :nom, value_method: :id %>
<%= f.submit "Add a todo" %>
<% end %>
My problem is that I want to be able to create todo via topics aswell and when I add my routes :
resources :topics, only: [:index, :show, :new, :create] do
resources :todos, only: [:index, :new, :create]
end
When I tried to complete my controller and test it, it seems to be tricky. If I add:
#topic = Topic.find(params[:topic_id])
Then it tells me that it needs a meeting...
Any idea ?
You can create the different routes with:
resources :meetings do
resources :todos, only: [:index, :new, :create]
end
resources :topics do
resources :todos, only: [:index, :new, :create]
end
You can avoid duplication by using routing concerns:
concerns :todoable do
resources :todos, only: [:index, :new, :create]
end
resources :topics, concerns: :todoable
resources :meetings, concerns: :todoable
In your controller you can check for the presences of the meeting_id or topic_id parameters:
class TodosController < ApplicationController
before_action :set_parent
def new
#todo = Todo.new
end
def create
#todo = #parent.todos.new(todo_params)
if #todo.save
redirect_to #parent
else
render :new
end
end
private
def parent_class
#parent_class ||= if params[:meeting_id].present?
Meeting
else if params[:topic_id].present?
Topic
else
# raise an error?
end
end
def set_parent
id = params["#{parent_class.model_name.param_key}_id"]
#parent = parent_class.find(id)
end
def todo_params
params.require(:todo)
.permit(:topic_id, :meeting_id, :note, :deadline, :title)
end
end
<%= simple_form_for [#parent, #todo] do |f| %>
<%= f.input :title %>
<%= f.input :note %>
<%= f.date_field :deadline %>
<%= f.association :topic, label_method: :nom, value_method: :id if #parent.is_a?(Meeting) %>
<%= f.association :meeting, label_method: :nom, value_method: :id if #parent.is_a?(Topic) %>
<%= f.submit "Add a todo" %>
<% end %>

Rails create method failing

I'm trying to create "group posts" that are connected to a "user group" and a user. The first line of my create method is failing with this error:
Couldn't find UserGroup with 'id'=
I've been looking at blog building tutorials thinking that my "group post" is acting like a comment but instead of being attached to an article it's attached to a "user group".
I'm pretty new to Rails so it could be simply a syntax issue. Any advise would be appreciated.
Here is the group post create method:
def create
#user_group = UserGroup.find(params[:user_group_id])
#group_post = current_user.group_posts.new(group_post_params)
if #group_post.save
respond_to do |format|
format.html {redirect_to user_group_path(#user_group), notice: "Group Post created!"}
end
else
redirect_to user_group_path(#user_group), notice: "Something went wrong."
end
end
private
def group_post_params
params.require(:group_post).permit(:content, :post_type, :user_group_id)
end
Here is the user group model:
class UserGroup < ActiveRecord::Base
has_many :group_members, dependent: :destroy
has_many :members, through: :group_members, source: :user
has_many :group_posts, dependent: :destroy
validates :name, presence: true, length: {minimum: 5}
validates :searchable, presence: true, length: {minimum: 5}
def owners
members.includes(:group_members).where('group_members.owner = ?', true)
end
def regular_users
members.includes(:group_members).where('group_members.owner = ?', false)
end
end
Here is the group post model:
class GroupPost < ActiveRecord::Base
include PublicActivity::Model
belongs_to :user
belongs_to :user_group
validates :user_id, :presence => true
validates :content, :presence => true
end
And finally the routes:
Rails.application.routes.draw do
devise_for :users, controllers: {registrations: 'registrations'}
root 'pages#home'
resources :users, only: [:index, :show]
resources :friendships, only: [:create, :destroy, :accept] do
member do
put :accept
end
end
resources :posts, only: [:create, :edit, :update, :destroy]
resources :group_posts, only: [:create, :edit, :update, :destroy]
resources :activities, only: [:index] do
member do
put "upvote" =>"activities#upvote"
end
end
resources :user_groups do
resources :group_posts
resources :group_members
end
end
Here is the form partial submitting the group post:
<div class="posts-panel">
<%= form_for(#group_post) do |f| %>
<%= render 'partials/error_messages', object: f.object %>
<div class="form-group">
<%= f.text_area :content, placeholder: "Compose new post...", class: "form-control" %>
</div>
<div class="form-group">
<%= f.select :post_type, [['Request', 'request'],['Report', 'report']], {}, {class: "form-control"} %>
</div>
<div class="form-group">
<%= hidden_field_tag :user_group_id, #usergroup.id %>
<%= f.submit "Post", class: "btn btn-primary" %>
</div>
<% end %>
</div>
Complete Params:
{"utf8"=>"✓", "authenticity_token"=>"thkRQYNcl+ySSoWIE83V22DEqYdttg+TF4coFsmasXkt2mylgB2YG/vAl2KYRey/djTqL5iNSTIyWJpsSWyCQQ==", "group_post"=>{"content"=>"This is it", "post_type"=>"prayer"}, "user_group_id"=>"1", "commit"=>"Post", "controller"=>"group_posts", "action"=>"create"}
The user_group_id was not inside the params hash. Adding the following line to the create method solved the issue:
#group_post.user_group = #user_group

Polymorphic Commenting

I'm currently developing an app which allows users to post from their own account, but if they're an administrator of a group or venue, they can also post as that entity. I'm struggling converting the polymorphic association ideas from some of the other questions out there as generally they're all based around being able to comment on multiple things and not from multiple things.
I think my main issue is that I have my user's post form on the homepage, so it does not have an ID in the URL.
My post controller looks like this:
class PostsController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
before_filter :load_postable
def index
end
def new
#post = Postabe.posts.new(post_params)
end
def create
#post = #postable.posts.build(post_params)
if #post.save
flash[:success] = "Post created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#post.destroy
redirect_to root_url
end
private
def post_params
params.require(:post).permit(:content)
end
def load_postable
resource, id = request.path.split('/')[1, 2]
resource_name = resource.singularize.classify
if resource_name == "User"
#postable = current_user
else
#postable = resource_name.constantize.find(id)
end
end
end
and my _post_form.html.erb partial:
<%= form_for ([#postable, #postable.post.new]), remote: true do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Create a Post..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
my related routes:
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks", :registrations => "registrations" }
resources :users, :only => [:index] do
member do
get :favourite_users, :favourited_users
end
resources :posts
end
resources :venues do
resources :posts
end
resources :groups do
resources :posts
end
Models as follows:
class Post < ActiveRecord::Base
belongs_to :postable, polymorphic: true
end
class User < ActiveRecord::Base
has_many :posts, as: :postable, dependent: :destroy
end
class Venue < ActiveRecord::Base
has_many :posts, as: :postable, dependent: :destroy
end
class Group < ActiveRecord::Base
has_many :posts, as: :postable, dependent: :destroy
end
It seems that I keep getting the error
Couldn't find Post without an ID
but I don't know why it's looking for a Post ID if it's not been created yet. Any help would be appreciated!
You have before_filter :load_postable in your controller. By default it will run for all the actions in your controller, even when the id is not specified. The error is thrown by #postable = resource_name.constantize.find(id), id is nil for index method.
Change this line to:
before_filter :load_postable, except: [:index]

undefined method `discussion_postcomments_path' for #<#<Class:0x7a1c360>:0x7a20c38>

I recently added a new Model (discussion.rb) and Controller (discussions_controller.rb). I am trying to get postcomments to work with discussions.
discussion.rb
class Discussion < ActiveRecord::Base
belongs_to :user
has_many :postcomments, dependent: :destroy
validates :user_id, presence: true
validates :content, presence: true
attr_accessible :content, :user_id
default_scope order: 'discussions.created_at DESC'
end
Here's what I have in routes
resources :discussions, :path => "disc"
resources :users do
member do
get :following, :followers
end
end
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy]
resources :discussions, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
resources :microposts do
resources :postcomments
resources :discussions do
resources :postcomments
end
end
here's the postcomments model
class Postcomment < ActiveRecord::Base
attr_accessible :comment_content
belongs_to :user
belongs_to :micropost
belongs_to :discussion
validates :comment_content, presence: true
validates :user_id, presence: true
default_scope order: 'postcomments.created_at ASC'
end
I'm trying to use this in view except, I get the error posted in the title
<%= form_for([discussion, #comment]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :comment_content %>
</div>
<div class="ItemContainer">
<div class="ItemInput">
<button class="btn" type="submit">
Comment
</button>
</div><div class="ItemCommentCount">
<% end %>
It says that the error is caused from this line
<%= form_for([discussion, #comment]) do |f| %>
Anyone know how I can fix this path problem?
class PostcommentsController < ApplicationController
def create
#micropost = Micropost.find(params[:micropost_id])
#comment = Postcomment.new(params[:postcomment])
#comment.micropost = #micropost
#comment.user = current_user
if #comment.save
redirect_to(:back)
else
render partial: 'shared/_postcomment_form', locals: { micropost: #micropost }
end
end
def createdisc
#discussion = Discussion.find(params[:discussion_id])
#comment = Postcomment.new(params[:postcomment])
#comment.discussion = #discussion
#comment.user = current_user
if #comment.save
redirect_to(:back)
else
render partial: 'shared/_postcomment_form', locals: { discussion: #discussion}
end
end
end
Try consolidating your use of resources :discussions in your routes file. I've edited below assuming you didn't intend to nest discussions under microposts.
resources :discussions, only: [:create, :destroy], path: "disc" do
resources :postcomments
end
resources :users do
member do
get :following, :followers
end
end
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
resources :microposts do
resources :postcomments
end
This line
<%= form_for([discussion, #comment]) do |f| %>
This discussion should be an instance variable as well:
<%= form_for([#discussion, #comment]) do |f| %>
Then you need to define #discussion in the controller

Resources