How to update a nested form - Ruby on Rails - 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.

Related

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

Why does simple_form raise an "No route matches [POST] "/tenants"" error with nested resources

I am trying to make a simple form for creating a new Tenant, knowning that when you create a tenant, a Devise User(with "role: "tenant") has to be created at the same time. But when I submit the form it raises a "Routing Error" (No route matches [POST] "/tenants").
(I'm using Devise and Pundit)
I have a Property model having many Tenants models
class Property < ApplicationRecord
belongs_to :owner
has_many :tenants, dependent: :destroy
validates :address, presence: true
end
class Tenant < ApplicationRecord
belongs_to :user
belongs_to :property
has_many :incidents, dependent: :destroy
validates :first_name, presence: true
validates :last_name, presence: true
validates :email, presence: true, uniqueness: true
validates :phone, presence: true
validates :rental_price, presence: true
validates :start_date, presence: true
validates :rental_time, presence: true
end
Here are the routes:
Rails.application.routes.draw do
root to: 'pages#home'
devise_for :users
resources :agencies do
resources :owners, only: [:index, :new, :create]
end
resources :owners, only: [:show, :edit, :update, :destroy] do
resources :properties, only: [:new, :create]
end
resources :properties, only: [:index, :show, :edit, :update, :destroy] do
resources :tenants, only: [:new, :create]
end
resources :tenants, only: [:index, :show, :edit, :update, :destroy] do
resources :incidents, only: [:new, :create]
end
resources :incidents, only: [:index, :show, :edit, :update, :destroy]
resources :workers
the tenants controller:
class TenantsController < ApplicationController
before_action :set_tenant, only: [:show, :edit, :update, :destroy]
def create
#tenant = Tenant.new(tenant_params)
#tenant.user = User.create(email: #tenant.email, password: random_password, role: "tenant")
#tenant.property = Property.find(params[:property_id])
if #tenant.save
redirect_to :root
else
render :new
end
authorize(#tenant)
end
def new
#tenant = Tenant.new
#tenant.user = User.new
#tenant.property = Property.find(params[:property_id])
authorize(#tenant)
end
def show
authorize(#tenant)
end
def index
#tenants = policy_scope(Tenant).order(created_at: :desc)
end
def edit
authorize(#tenant)
end
def update
authorize(#tenant)
end
def destroy
authorize(#tenant)
#tenant.destroy
redirect_back(fallback_location: root_path)
end
private
def random_password
(('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a).shuffle.first(6).join
end
def set_tenant
#tenant = Tenant.find(params[:id])
end
def tenant_params
params.require(:tenant).permit(:first_name, :last_name, :email, :phone, :rental_price, :start_date, :rental_time)
end
end
the new.html.erb
<%= simple_form_for [#property, #tenant] do |f| %>
<%= f.input :first_name, label: false, placeholder: 'Prénom' %>
<%= f.input :last_name, label: false, placeholder: 'Nom' %>
<%= f.input :email, label: false, placeholder: 'Email' %>
<%= f.input :phone, label: false, placeholder: 'Téléphone' %>
<%= f.input :rental_price, label: false, placeholder: 'Montant du loyer en euros' %>
<%= f.input :start_date, placeholder: 'Date de début de location' %>
<%= f.input :rental_time, label: false, placeholder: 'Durée de location' %>
<%= f.submit " OK " %>
<% end %>
and the policies
class TenantPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def show?
admin? || (agency? && user.agency.tenants.include?(record)) || (tenant? && user.tenant == record)
end
def index?
admin? || (agency? && (record - user.agency.tenants).empty?)
end
def create?
admin? || (agency? && user.agency.properties.include?(record.property))
end
def update?
admin?
end
def destroy?
admin? || (agency? && user.agency.tenants.include?(record))
end
end
I would really like to understand why it doesnt work here as i made exactly the same thing for the property model (belonging to owner) and it worked.
Thanks!
Your error is occurring because you removed the :create action from your routes:
resources :tenants, only: [:index, :show, :edit, :update, :destroy] should include :create
I found what was wrong:
I forgot to instantiate #property in my controller methods.

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

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

Rails: Photo uploads (Paperclip) with nesting & strong params

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.

Resources