I want to add the ability of a user to have several pictures associated with his / her user account.
I have the following classes:
class User < ActiveRecord::Base
has_many :assets
accepts_nested_attributes_for :assets
end
class Asset < ActiveRecord::Base
belongs_to :assetable, :polymorphic => true
belongs_to :user
end
I want to have a screen that just has the upload image functionality:
def add_profile_picture
#user=User.find(params[:id])
1.times {#user.assets.build}
end
form:
<%= form_for #user do |u| %>
<%= u.fields_for :assets do |asset| %>
<%= asset.file_field :asset %>
<%= asset.text_field :description %><br />
<% end %>
<%=u.submit %>
<% end %>
When I submit, it looks like the id value goes in ok in development.log:
"id"=>"1"
but I get the error:
undefined method `update_attributes' for nil:NilClass
Since I just have the asset fields, is there anything special I need to do? Also, because the belongs_to :user exists, could that be causing problems?
Basically:
asset:
user_id:
assetable_type:
assetable_id:
Any help would be appreciated. Don't do much Rails forms stuff.
thx
edit #1
class UsersController < ApplicationController
def add_profile_picture
#user=User.find(params[:id])
1.times {#user.assets.build}
end
thx
Okay - there are a few problems with your code here. I would highly recommend you read both the Action Controller Overview and the Rails Routing guides to get some more information about this.
In any case, you're getting the error because the form you have there will be trying to use the users#update action in the UsersController.
You've got a couple options. One is to create the necessary routes for the custom action, or you can create a nested resource, and make a form for adding the asset.
In this case, you'd do something like this:
in routes.rb
resources :users do
resources :assets, :only => [:new, :create] # Or any other actions you might want. It's best practise to limit these.
end
Then, in the AssetsController, you can do something similar to this:
def new
#asset = Asset.new
end
def create
#asset = Asset.new(params[:asset])
#asset.user_id = params[:user_id] if params[:user_id]
#asset.save!
end
and your form will look something like this:
<%= form_for #asset do |f| %>
<%= f.file_field :asset %>
<%= f.text_field :description %><br />
<%=f.submit %>
<% end %>
Related
This should be super simple, but I can't see what I'm doing wrong.
The form in the 'new' page for uploads is getting an error.
'Uploads' belong to 'Event'
'Event' has many 'Uploads'
routes.rb is (as far as I know) correct.
I'm planning on using Refile to upload files to S3 (as per this tutorial... not sure if this is relevant at all though)
Upload.rb
class Upload < ActiveRecord::Base
belongs_to :event
attachment :upload_file
end
Event.rb
class Event < ActiveRecord::Base
has_many :uploads
end
uploads_controller.rb
class UploadsController < ApplicationController
before_action :set_event
def new
#upload = #event.uploads.create
end
private
def set_event
#event = Event.find(params[:event_id])
end
end
Routes.rb
Rails.application.routes.draw do
devise_for :users
root 'pages#home'
resources :events do
resources :coupons
resources :uploads
member do
post :check
end
end
views/uploads/new.html.erb (example)
<%= form_for #upload do |f| %>
<%= f.text_field :name %>
<% end %>
When I navigate to the 'new' page, I get the following error:
undefined method `upload_path' for #<#:0x007fb8709229f0>
Why can't I add a new Upload associated with Event? I know I'm missing something super simple, but I can't put my finger on it.
As uploads is nested in events, you get url for your upload path as follow:
/events/1/uploads/new
In this case, you have to specify #event in your form_for method like this:
<%= form_for [#event, #upload] do |f| %>
Or simply
<%= form_for #event.upload do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end%>
So I have two models
class First < ActiveRecord::Base
belongs_to :story
end
class Story < ActiveRecord::Base
has_one :first
end
I want to create a new first, which is associated with a story. Each story can only have one first. I'm trying to use this as my form_for
<%= form_for ([#story, #first]) do |f| %>
<%= f.label :message %><br/>
<%= f.text_field :message %><br/>
<%= f.submit %>
<% end %>
However, I don't know how to set this up in my routes to cater for this. At the moment, I get a "undefined method story_firsts_path" error. Here is my firsts controller
class FirstsController < ApplicationController
def new
#story = Story.new
#first = #story.build_first
end
end
Am I way off here or am I somewhat on the right track?
Thanks!
You should use nested routes to define story and first. Like this:
resources :stories do
resources :firsts
end
I have two models one Topic and Topic_Content.
With the following code
Route
resources :topics do
resources :topic_contents
end
Topic
class Topic < ActiveRecord::Base
has_one :topic_content
accepts_nested_attributes_for :topic_content
end
TopicContent
class TopicContent < ActiveRecord::Base
belongs_to :topics
end
Controller
class TopicsController < ApplicationController
def new
#topic = Topic.new
end
def create
# render text: params[:topic].inspect
#topic = Topic.new(topic_params)
#topic.save
end
private
def topic_params
params.require(:topic).permit(:title, topic_content_attributes: [:text])
end
end
View
<%= form_for #topic do |f| %>
<%= f.label 'Topic:' %>
<%= f.text_field :title %>
<%= f.fields_for :topic_contents do |tf| %>
<%= tf.label :text %>
<%= tf.text_area :text %>
<% end %>
<%= f.submit %>
<% end %>
The title will be saved correct in the topic table but the topic_content(text) wouldn't saved in the database, and I couldn't find the problem.
I'm not a Rails expert, but I'm certain you need to build the association in your controller.
In your new and edit actions you need to have:
def new
#topic = Topic.new
#topic_content = #topic.build_topic_content
end
Because this is a has_one/belongs_to you need to have it look that way. If it was a many association you'd build it with something like #topic_content = #topic.topic_contents.build.
I'm pretty sure it's just a matter of building the association in the right controller, which, I believe, for you, is the topic controller.
Your view should be as follow:
f.fields_for :topic_content do |content_fields|
^
I am using build, fields_for, and accepts_nested_attributes_for to create a new registration note on the same form as a new registration (has many registration notes). Great.
Problem: On the edit form for the existing registration, I want another new registration note to be created, but I don't want to see a field for each of the existing registration notes.
I have this
class Registration < ActiveRecord::Base
attr_accessible :foo, :bar, :registration_notes_attributes
has_many :registration_notes
accepts_nested_attributes_for :registration_notes
end
and this
class RegistrationsController < ApplicationController
def edit
#registration = Registration.find(params[:id])
#registration.registration_notes.build
end
end
and in the view I am doing this:
<%= form_for #registration do |r| %>
<%= r.text_field :foo %>
<%= r.text_field :bar %>
<%= r.fields_for :registration_notes do |n| %>
<%= n.text_area :content %>
<% end %>
<% end %>
and it is creating a blank text area for a new registration note (good) and each existing registration note for that registration (no thank you).
Is there a way to only create a new note for that registration and leave the existing ones alone?
EDIT: My previous answer (see below) was bugging me because it's not very nice (it still loops through all the other registration_notes needlessly). After reading the API a bit more, the best way to get the behaviour the OP wanted is to replace:
<%= r.fields_for :registration_notes do |n| %>
with:
<%= r.fields_for :registration_notes, #registration.registration_notes.build do |n| %>
fields_for optionally takes a second parameter which is the specific object to pass to the builder (see the API), which is built inline. It's probably actually better to create and pass the new note in the controller instead of in the form though (just to move the logic out of the view).
Original answer (I was so close):
Just to clarify, you want your edit form to include a new nested registration note (and ignore any other existing ones)? I haven't tested this, but you should be able to do so by replacing:
<%= r.fields_for :registration_notes do |n| %>
with:
<%= r.fields_for #registration.registration_notes.build do |n| %>
EDIT: Okay, from a quick test of my own that doesn't work, but instead you can do:
<%= r.fields_for :registration_notes do |n| %>
<%= n.text_area :content if n.object.id.nil? %>
<% end %>
This will only add the text area if the id of the registration note is nil (ie. it hasn't been saved yet).
Also, I actually tested this first and it does work ;)
If you want to create a new registration form on your edit action, you can just instantiate a new registration_note object. Right now, your form is for the existing registration object.
I believe this is what you want:
class RegistrationsController < ApplicationController
def edit
#new_registration_note = RegistrationNote.new
#registration = Registration.find(params[:id])
#registration.registration_notes.build
end
end
In your view, you should pass a hidden param that references the registration record id:
<%= form_for #new_registration_note do |r| %>
<%= r.hidden_field :registration_id, :value => #registration.id %>
<%= r.text_area :content %>
<% end %>
Now, you can create your new registration note that belongs to #registration. Make sure you have a column in your registration_notes table to point to the registration. You can read more about associations here: http://guides.rubyonrails.org/association_basics.html
Thank you so much for your help as I said in my post the only problem with the approach from "Zaid Crouch"(I don't know how to make a reference to a user hehe) is that if the form has error fields the form will be clear and boom after the page reloading you'll have nothing filled in your form and can you imagine if you form is like 20 or 30 fields that would be a terrible user experience of course
Here is my solution that works with validation models:
class Registration < ActiveRecord::Base
attr_accessible :foo, :bar, :registration_notes_attributes
has_many :registration_notes
has_one :new_registration, class_name: 'RegistrationNote'
accepts_nested_attributes_for :new_registration
end
class RegistrationsController < ApplicationController
def edit
#registration = Registration.find(params[:id])
#registration.build_new_registration
end
end
<%= form_for #registration do |r| %>
<%= r.text_field :foo %>
<%= r.text_field :bar %>
<%= r.fields_for :new_registration do |n| %>
<%= n.text_area :content %>
<% end %>
<% end %>
I'm using simple_form in my example if you want to see the same working with validations and transaction take a look at the complete post here:
http://elh.mx/ruby/using-simple_form-for-nested-attributes-models-in-a-has_many-relation-for-only-new-records/
As Heriberto Perez correctly pointed out the solution in the most upvoted answer will simply discard everything if there's a validation error on one of the fields.
My approach is similar to Heriberto's but nevertheless a bit different:
Model:
class Registration < ActiveRecord::Base
has_many :registration_notes
accepts_nested_attributes_for :registration_notes
# Because 0 is never 1 this association will never return any records.
# Above all this association don't return any existing persisted records.
has_many :new_registration_notes, -> { where('0 = 1') }
, class_name: 'RegistrationNote'
accepts_nested_attributes_for :new_registration_notes
end
Controller:
class RegistrationsController < ApplicationController
before_action :set_registration
def edit
#registration.new_registration_notes.build
end
private
def set_registration
#registration = Registration.find(params[:id])
end
def new_registration_params
params.require(:registration).permit(new_registrations_attributes: [:content])
end
end
View:
<%= form_for #registration do |r| %>
<%= r.text_field :foo %>
<%= r.text_field :bar %>
<%= r.fields_for :new_registration_notes do |n| %>
<%= n.text_area :content %>
<% end %>
<% end %>
As a first-time Rails user, I have to say I'm loving the Rails way of doing things. However, I'm running into an issue trying to create a simple form. I get the following error:
undefined method `categories_path' for #<#<Class:0x007f0440365880>:0x007f0430256cd8>
I tried creating a categories_path method in the controller (though I'm not sure what it would be for), but that didn't fix the error. Any rails experts out there know what's going on?
Here's the relevant code:
views/category/new.html.erb
<%= form_for #category do |f| %>
<%= f.label :category %>
<%= f.text_field :name %><br />
<%= f.submit %>
<% end %>
routes.rb
Jackeyes::Application.routes.draw do
scope "/admin" do
resources :product, :category
end
end
category_controller.rb
class CategoryController < ApplicationController
def index
#category = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(params[:category])
#category.save
end
end
Make your resources plural:
resources :products, :categories
And try again.