Rails 4 - associations between controllers, id not getting passed - ruby-on-rails

I have two controllers categories and products. Products belongs to categories but I'm having trouble setting up the relationship.
In category.rb
has_many :products
In product.rb
belongs_to :category
validates :category_id, :presence => true
validates :name, :presence => true, :uniqueness => true
When I try to create a new product, the record does not save because category_id is blank. My product form looks like so:
<%= form_for #product, :html => { :class => 'form-horizontal' } do |f| %>
<%= f.hidden_field('category_id', :value => params[:category_id]) %>
<div class="control-group">
<%= f.label :name, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :name, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :category, :class => 'control-label' %>
<div class="controls">
<% #cat = Category.all %>
<%= select_tag 'category', options_from_collection_for_select(#cat, 'id', 'name') %>
</div>
</div>
<div class="control-group">
<%= f.label :price, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :price, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :description, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :description, :class => "tinymce", :rows => 10, :cols => 120 %>
<%= tinymce %>
</div>
</div>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
products_path, :class => 'btn' %>
</div>
<% end %>
and in the products controller I have:
def new
#product = Product.new
#category = #product.category
end
I've tried looking through other questions on SO but haven't had any luck finding the correct way to pass the category id to products.
I hope I've been clear enough and I'm happy to provide any extra information that may be needed.
EDIT
I've made the following changes to the products controller as suggested by I not get the error: Cannot find Category without an ID
before_filter :set_category, only: [:create]
def set_category
#category = Category.find(params[:category_id])
end
def create
#product = #category.products.new(product_params)
#....
end
I'm using nested routes like so:
resources :categories do
resources :products
end

You should set product's category in create action:
def create
#product = #category.products.new(product_params)
# ...
end
in new action, you should just have
def create
#product = Product.new
end
Of course, you need to set #category (#category = Category.find(params[:category_id])) instance variable before (in before_filter, for example).
You should also remove this hidden field from view and remove category_id from allowed params if you don't want users to set category_id manually and set form_for arguments properly, since you're using nested resources:
form_for [#category, #product]

Related

trouble with rails rendering partial and finding custom controller

Background: I would like to make a team and have the user verify the address of that team before the team is saved.
In my application I have a form that creates a team when the form is submitted. Within this form I have a partial that is suppose to render with a field location. When the user clicks submit within the partial form the location field (within the partial form and not the create team form) should go to the verify_Address action within the teams_controller. Instead of this happening I get an error when I load the page.
The error on pageload:
undefined local variable or method `verify_address' for #<#<Class:0x000001063ec8d8>:0x00000104555af0>
with this line highlighted: <%= form_for :team, url: verify_address, method: :post, remote:true do |f|%>
below are my files within the app.
route.rb file:
resources :users, :path => :captains, :as => :captains, :controller => "captains" do
resources :teams, :only => [:new, :create, :edit, :update, :destroy], controller: 'captains/teams'
end
resources :teams, :only => [:index, :show] do
resources :users, :path => :teammates, :as => :teammates, :controller => "teammates"
end
put 'captains/:id/teams/verify_address' => "teams#verify_address",as: 'verify_address'
get 'captains/:id/teams/verify_address' => "teams#verify_address"
controller/captains/teams_controller.rb:
class Captains::TeamsController < ApplicationController
respond_to :html, :js
def new
#team = Team.new
#captain = current_user
end
def verify_address
#address = params[:team][:location]
#validate_address = Team.validate_address(#address)
end
def create
#captain = current_user.id
#team = Team.create(
:name => params[:team][:name],
:location => params[:team][:location],
:sport => params[:team][:sport],
:captain_id => #captain,
:avatar => params[:team][:avatar]
)
if #team.present?
redirect_to #team # show view for team
end
binding.pry
end
end
the partial views/captains/teams/_verify_address.html.erb:
<%= form_for :team, url: verify_address, method: :post, remote:true do |f|%>
<div class = "form-group">
<%= f.label :location %>
<%= f.text_field :location, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.submit "Verify address" ,class: 'btn btn-success' ,id: 'verify_address' %>
</div>
<% end %>
the main form views/captains/teams/new.html.erb:
<%= form_for :team, url: captain_teams_path(#captain, #team), method: :post do |f|
%>
<div class="form-group">
<%= f.label :avatar %>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</div>
<div class = "form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.label :sport %>
<%= f.text_field :sport, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.label :location %>
<%= f.text_field :location, class: 'form-control', placeholder: "Enter wiki title", id:'team_title' %>
</div>
<div class = "form-group">
<%= f.submit class: 'btn btn-success' ,id: 'team_role_submit' %>
</div>
<% end %>
</div>
<%= render partial: "/captains/teams/verify_address", locals: { address: #address, validate_address: #validate_address}%>
</div>
Creating a custom route verify_address generates verify_address_path url helper, which you should use in your form.

How to implement upload photo feature in rails?

My repo: https://github.com/Gtar69/artstore_hw2
I want to implement upload more than 1 photo in certain product.
The basic idea is that administrator enter edit page of the product and can "upload new photos" for certain product.Im struggling in "how to create a photo object in edit page and add this photo object to product. !!! I can not successfully create a photo object in edit page .....
Very Thanks
in view/admin/products/edit.html.erb
<h2> 編輯 <%= #product.title %> </h2>
<%= simple_form_for [:admin, #product ] , :html => { :class => "form form-horizontal" } do |f| %>
<div class="form-group">
<div class="col-sm-10">
<%= f.input :title %>
</div>
</div>
<div class="form-group">
<div class="col-sm-10">
<%= f.input :description, :as => :text %>
</div>
</div>
<div class="form-group">
<div class="col-sm-1">
<%= f.input :quantity %>
</div>
</div>
<div class="form-group">
<div class="col-sm-1">
<%= f.input :price %>
</div>
</div>
<!-- 20140623 upload more than one photo feature added -->
<div>
<%= simple_form_for [:admin, #product, #product.photos.build] do |f| %>
<%= f.input :image , :as => :file %>
<div class="form-group">
<%= f.submit "upload new photos", :class => "btn btn-default" , :disable_with => 'Submiting...' %>
</div>
<%end%>
</div>
<div class="form-group">
<%= f.submit "Submit", :class => "btn btn-default" , :disable_with => 'Submiting...' %>
</div>
<% end %>
in controllers/admin/products_controller.rb
def edit
#product = Product.find(params[:id])
##photo = #product.photos.build
##product.add_photo_to_product(#photo)
end
in controllers/admin/photos_controller.rb
def create
#binding.pry
#product = Product.find(params[:product_id])
#photo = #product.photos.create(photo_params)
#product.add_photo_to_product(#photo)
redirect_to admin_products_path
end
private
def photo_params
params.require(:photo).permit(:image)
end
in model/product.rb
class Product < ActiveRecord::Base
has_many :photos
accepts_nested_attributes_for :photos
validates :title , :presence => true
validates :quantity , :presence => true
def default_photo
photos.first
end
def add_photo_to_product(photo)
photos << photo
end
end
in photos.rb
class Photo < ActiveRecord::Base
belongs_to :product
mount_uploader :image, ImageUploader
end

Ruby Rails submit two forms in one click

I have these forms:
<%= form_for(#user) do |f| %>
<div>
<%= f.number_field :money, :value => #user.money %>
</div>
<% end %>
and
<%= form_for #product, :url => product_path, :html => { :multipart => true } do |f| %>
<div>
<%= f.label :count, 'How Many product?' %><br />
<%= f.number_field :count, :value => "1" %>
</div>
<div>
<%= f.submit('submit') %>
</div>
<% end %>
is there any way to submit this two at once when clicking submit button ? Thanks!
A service object might be a good way to approach this.
class Order
include ActiveModel::Model
attr_accessor :money, :count
def initialize(user=nil, product=nil)
#user = user
#product = product
#money = user.money
#count = 1
end
def persisted?
false
end
def save
// this code needs to save to db
end
end

Rails create two objects from one form

I have two models with a has_one relationship between them. The Event class which "has_one" Location. What I am trying to achieve is in the form where I create an event, I have a "Where?" field which is populated asynchronously through the Foursquare API.
Since I could not possibly migrate the whole of the Foursquare API to my own database, I want to generate a Location entry in my database the first time a location is mentioned in an event.
At the moment the result I am looking for is completed, but I think I am using to many hidden fields and a lot of javascript assignments. Is there a better practice than the one I am using to achieve the same result?
new.html.erb
<%= form_for :event, :url=> new_event_path, :method => :get, :remote => :true, :html => {:class => 'form-horizontal'} do |f| %>
<%= fields_for :location do |f1|%>
<%= f1.hidden_field :lat, :id=> 'foursquare-latitude' %>
<%= f1.hidden_field :lng, :id => 'foursquare-longitude' %>
<%= f1.hidden_field :foursquare_id, :id =>'foursquare-id' %>
<%= f1.hidden_field :name, :id =>'location-name-field' %>
<% end %>
<!-- TODO Encapsulate in location model -->
<%= f.hidden_field :latitude, :id=>'latitude-field' %>
<%= f.hidden_field :longitude, :id=>'longitude-field' %>
<div class="control-group">
<%= f.label :title , :class => "control-label" %>
<div class="controls">
<%= f.text_field :title %>
</div>
</div>
<div class="control-group">
<label class="control-label" for="location">Where?</label>
<div class="controls">
<input type="text" id="location" data-provide="typeahead">
<br/>
<img src="../img/fsq.png" alt="Powered By Foursquare" width="240px" />
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => "control-label" %>
<div class="controls">
<%= f.text_area :body, :rows => 6 %>
</div>
</div>
<div class="control-group">
<div class="controls">
<%= f.submit %>
</div>
</div>
<% end %>
events controller
def new
#location = Location.new(params[:location])
if #location.save
#event = Event.new(params[:event])
#event.author = current_user
#event.location_id = #location.id
respond_to do |format|
if #event.save
format.js {render :action => "new"}
else
format.js {render :action => "new"}
end
end
end
end

Self Joining ActiveRecord, Parent_Post_ID and me

So I have a self-joining model defined. Basically a post on a forum, and a parent_post that it belongs to.
class Post < ActiveRecord::Base
has_many :replies, :class_name => "Post"
belongs_to :thread, :class_name => "Post", :foreign_key => "parent_post_id"
end
Which seems fundamentally sound. I created a new RESTful route for the reply action, and an action and view.
Routes:
resources :forums do
resources :posts do
member do
get 'reply'
end
end
end
The view layer and the control action seems to be where I'm getting hosed up.
def reply
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build
#post.thread = #forum.posts.find(params[:id])
#post.title = "RE: #{#post.thread.title}"
end
def create
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build(params[:post])
#post.user = current_user
if #post.save
redirect_to forum_post_path(#forum, #post), notice: 'Post was successfully created.'
else
render action: "new"
end
end
And in the view layer I was just trying to use the same scaffold generated form partial I'm using for the standard new and edit actions.
#reply.html.erb
<%= render :partial => 'form' %>
#_form.html.erb
<%= form_for [#forum,#post], :html => { :class => 'form-horizontal' } do |f| %>
<fieldset>
<legend><h1>New Thread</h1></legend>
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field span9' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area span9' %>
</div>
</div>
<div class="form-actions">
<%= f.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_posts_path(#forum), :class => 'btn' %>
</div>
</fieldset>
<% end %>
However, the parent_post_id is getting lost when I'm creating the post and it's getting set to nil. Do I need to create another action? Is there some other way to set the thread? Some third thing?
This will work:
Reply action:
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.build
#post.thread = #forum.posts.find(params[:id])
#post.title = "RE: #{#post.thread.title}"
Then add this to your view
<%= f.hidden_field :parent_post_id, #post.thread.id %>
BTW I question whether you need a custom reply method as opposed to using built-in RESTful methods but this should fix your problem and that wasn't really your question.
add
<%= hidden_field_tag :forum_id , #forum.id %>
to your form
So basically, when you're submitting to the Posts#create action you're submitting a url that looks something like this /forum/1/posts which removes the parent_post_id from the url. Since you're using that parent_post_id to build that url, you need a way to POST with it.
My suggestion is allowing a POST to a reply resource that is nested in the posts resource.
(ie POST /forums/1/posts/1/reply)
So maybe something like this
resources :forums do
resources :posts do
# :show is actually just pointing to a form
resource :reply, :only => [:show, :create],
:controller => 'reply' #otherwise gets routed to 'replies'
end
end
So you would also need a ReplyController but that would basically match your reply method on your post controller with a few changes.
def show
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.find(params[:post_id])
#reply = #forum.posts.build
#reply.thread = #post
#reply.title = "RE: #{#post.thread.title}"
end
def create
#forum = Forum.find(params[:forum_id])
#post = #forum.posts.find(params[:post_id])
#reply = #forum.posts.build(params[:reply])
#reply.thread = #post
#reply.user = current_user
if #reply.save
redirect_to forum_post_path(#forum, #post), notice: 'Reply was successfully created.'
else
render action: "show"
end
end
The biggest problem would be that you would have to abstract your Post fields from your form for block. That's because the url you're trying to POST to is going to be different. But it shouldn't be too bad just doing something like this:
reply/show.html.erb
<%=
form_for #reply, :url => forum_post_reply_path(#forum, #post),
:html => { :class => 'form-horizontal' } do |builder|
%>
<fieldset>
<legend><h1>New Reply</h1></legend>
<%= render "posts/post_fields", :f => builder %>
<div class="form-actions">
<%= builder.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_post_path([#forum, #post]), :class => 'btn' %>
</div>
</fieldset>
<% end %>
posts/_form.html.erb
<%= form_for [#forum,#post], :html => { :class => 'form-horizontal' } do |builder| %>
<fieldset>
<legend><h1>New Thread</h1></legend>
<%= render "post_fields", :f => builder %>
<div class="form-actions">
<%= builder.submit 'Submit', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', forum_posts_path(#forum), :class => 'btn' %>
</div>
</fieldset>
<% end %>
posts/_post_fields.html.erb
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'text_field span9' %>
</div>
</div>
<div class="control-group">
<%= f.label :body, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :body, :class => 'text_area span9' %>
</div>
</div>
Note: There's probably a better way to declare the routes than I have, but I don't really know.

Resources