I have a form attached to profiles where short comments can be submitted. I want to capture the author's name though so I can display it in a tooltip when hovering over the comment's body.
In my create method in the controller I have:
def create
#comment = Comment.new(params[:comment])
#comment.save!
redirect_to profile_path(#comment.profile)
end
Inside my migration:
t.timestamps
t.integer :profile_id
t.string :author_id
t.string :body
Profile model:
belongs_to :user
accepts_nested_attributes_for :user
has_many :comments
Comment model:
belongs_to :profile
ProfilesController:
def show
#user = User.find(params[:id])
#profile = user.profile
#superlative = #profile.superlatives.new
end
And my form:
<%= form_for #comment do |f| %>
<%= f.hidden_field :profile_id, :value => #profile.id %>
<%= f.hidden_field :author_id, :value => "#{current_user.profile.first_name} #{current_user.profile.last_name}" %>
<%= f.text_field :body %>
<%= f.submit 'Add new' %>
<% end %>
I was thinking of linking the :author_id to current_user.profile.id and using that association to display :first_name and :last_name which are attributes of the profile. Or is there a simpler, better way?
UPDATE: I got it to display the name though I'm still curious if there's a better way.
Your solution looks fine, but I'd store the User (or whatever class current_user returns) instead of the Profile:
In app/models/comment.rb:
class Comment < ActiveRecord::Base
belongs_to :profile
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
... rest of the code ...
end
You then change your migration to:
t.integer :author_id
and your controller method to:
def create
#comment = Comment.new(params[:comment].merge(:author_id => current_user.id))
#comment.save!
redirect_to profile_path(#comment.profile)
end
In your view (I used the title attribute do create a tooltip, but feel free to use whatever method you like):
<div class="comment" title="<%= #comment.author.profile.first_name %> <%= #comment.author.profile.last_name %>">
<%= #comment.body %>
</div>
I would suggest something like this:
In your routes.rb create a nested resource for comments
resources :users do
resources :comments
end
In your User model
class User
has_many :comments
end
In your Comment model
class Comment
belongs_to :user
end
In your CommentsController in the new and create methods
#comment = User.find(params[:user_id]).comments.new(params[:comment])
So the comment automagically gets created as belonging to that User and you don't have to pass anything around.
Then, in your Comment view, you could just call its owners name
#comment.user.first_name
Related
I need some advice on building a has many through relationship between USER, THING and EXTRA models.
My USER model is slightly modified inside Devise gem and is noted as Creator whereas other models belonging to USER receive :created_things form.
In my app, USERS create THINGS can later add EXTRAS to their THINGS.
I chose has many through because I want to have unique data on all three models and be able to call both THINGS and EXTRAS from the USER "CREATOR" model.
I have built this many different ways and after 10 years of solving my problems by reading stackoverflow, I am finally submitting this request for support! Thank you for your help.
I have tried creating user and extra references on the THING model and declaring nested attributes in the USER and THING model. I have tried several examples from stackoverflow inside the create and new methods but nothing seems to work.
class User < ApplicationRecord
has_many :created_things, class_name: Thing, foreign_key:
:creator_id, :dependent => :destroy
has_many :extras, through: :created_things
accepts_nested_attributes_for :extras, :reject_if => :all_blank,
allow_destroy: true
class Thing < ApplicationRecord
belongs_to :creator, class_name: User
has_many :extras
accepts_nested_attributes_for :extras, :reject_if => :all_blank,
allow_destroy: true
class Extra < ApplicationRecord
belongs_to :creator, class_name: User, inverse_of: :thing
belongs_to :created_things
Members Index.html.erb
<% if thing.extras.exists? %>
<% thing.extras.each do |extra| %>
<%= extra.title %> <%= link_to "[+]", edit_extra_path(extra) %>
<% end %>
<% else if thing.extras.empty? %>
<%= link_to "+1 EXTRA", new_extra_path(current_user) %>
<% end %>
<% end %>
class MembersController < ApplicationController
before_action :authenticate_user!
def index
#user = current_user
#created_extras = #user.extras
#created_things = #user.created_things
end
class ExtrasController < ApplicationController
def new
#extra = Extra.new
end
def create
#extra = current_user.extras.build(extra_params)
if #extra.save
I am able to create a new EXTRA but the :thing_id remains nul as it does not display when called on the show extra view. Therefore I am not surprised that when I return to the member index page that my thing.extras.exists? call is returning false and the created extra never displays under the THING view. My attempts to modify the extra controller have failed and I some of my reading sugested the extras controller is not necessary in this relationship so I am really at a loss on how this is built. I'm assuming I am missing something in new and create methods maybe in things or user controller? Perhaps I'm missing something in routes resources? Any advice is greatly appreciated. Thank you.
Ok, I figured it out. I really didn't need has many through for this model and I did a lot of testing of the syntax on each model.rb and in the end was able to figure it out from this stackoverflow . . .
[Passing parent model's id to child's new and create action on rails
Here are my the various parts of setting up a has many and belongs to relationship with nested attributes.
class Thing < ApplicationRecord
belongs_to :creator, class_name: User
has_many :extras, inverse_of: :thing, :dependent => :destroy
accepts_nested_attributes_for :extras, allow_destroy: true
class Extra < ApplicationRecord
belongs_to :thing, inverse_of: :extras
extras_controller.rb
class ExtrasController < ApplicationController
def new
#extra = Extra.new(thing_id: params[:thing_id])
end
def create
#user = current_user
#extra = Extra.new(extra_params)
#extra.user_id = #user.id
if #extra.save
flash[:success] = "You have added a new Extra!"
redirect_to #extra #extras_path later
else
flash[:danger] = "The form contains errors"
render :new
end
end
edit.html.erb things
<% if #thing.extras.exists? %>
<p>current extras associated with <%= #thing.title %>: </p>
<% #thing.extras.each do |extra| %>
<p><%= extra.title %> <%= link_to "[+]", edit_extra_path(extra) %>
/ <%= link_to "[-]", extra_path(extra), method: :delete %> </p>
<% end %>
<% end %>
<%= link_to "+1 EXTRA", new_extra_path(thing_id: #thing.id) %>
<%= render 'things/form' %>
I am creating simple blog level application. below are my models.
class User < ActiveRecord::Base
attr_accessible :name,:posts_count,:posts_attributes , :comments_attributes
has_many :posts
has_many :comments
accepts_nested_attributes_for :posts , :reject_if => proc{|post| post['name'].blank?} , :allow_destroy => true
end
class Post < ActiveRecord::Base
attr_accessible :name, :user_id ,:comments_attributes
belongs_to :user
has_many :comments
accepts_nested_attributes_for :comments
end
class Comment < ActiveRecord::Base
attr_accessible :content, :post_id, :user_id
belongs_to :user
belongs_to :post
end
I am trying to create user,post and comment in one form by using accepts_nested_attributes_for feature of rails. Below is my controller and view code.
Controller-----------
class UsersController < ApplicationController
def new
#user = User.new
#post = #user.posts.build
#post.comments.build
end
def create
#user = User.new(params[:user])
#user.save
end
end
Form----------
<%= form_for #user do |f| %>
<%= f.text_field :name %>
<%= f.fields_for :posts do |users_post| %>
<br>Post
<%= users_post.text_field :name %>
<%= users_post.fields_for :comments do |comment| %>
<%= comment.text_field :content %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
With the above code i am successfully able to create new user,post and comment but the problem is that i am not able to assign newly created user to newly created comment.when i checked the newly created comment into the database i got below result.I am getting user_id field value of "nil".
#<Comment id: 4, user_id: nil, post_id: 14, content: "c", created_at: "2014-05-30 09:51:53", updated_at: "2014-05-30 09:51:53">
So I just want to know how we can assign newly created comment to newly created user???
Thanks,
You will have to explicitly assign user_id for comments! You are nesting comments under posts, so comments would be having post_id assigned by default but though you are nesting comments under user form indirectly, there is no direct nesting of comments under user, so user_id remains blank in comments.
Try writing after create callback in Comment model to set user_id
In comment.rb
after_create{|comment|
comment.user_id = post.user_id
comment.save
}
Hope this helps :)
I'm rather new to Rails and I'm writing a signup form that includes nested models. When I submit the form, the user is saved just fine, but the nested model does not save anything to the Subscription db, and the console throws no errors.
I sincerely hope I'm not missing something insanely obvious, and I appreciate any tips you can share. Thanks!
Here is the code-
Models:
class Plan < ActiveRecord::Base
attr_accessible :posts, :name, :price
has_many :users
end
class User < ActiveRecord::Base
belongs_to :plan
has_many :events
has_one :subscription, :autosave => true
accepts_nested_attributes_for :subscription
attr_accessible :subscription_attributes
def save_with_payment
if valid?
customer = Stripe::Customer.create(
email:email,
plan: plan_id,
card: stripe_card_token )
self.stripe_customer_token = customer.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
end
class Subscription < ActiveRecord::Base
attr_accessible :plan_id, :status, :user_id
belongs_to :user
end
This is the User controller:
def new
#user = User.new
plan = Plan.find(params[:plan_id])
#user = plan.user
#user.build_subscription
end
def create
#user = User.new(params[:user])
if #user.save_with_payment
sign_in #user
flash[:success] = "Welcome to the SendEvent!"
redirect_to #user
else
render 'new'
end
end
This is the form:
<%= form_for #user, :html => {:class => "form-inline"} do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="control-group">
<%= f.label :name, :class => "control-label" %>
<%= f.text_field :name %>
</div>
# A few more fields here and...
# The nested model:
<%= f.fields_for :subscription do |builder| %>
<%= builder.hidden_field :status, :value => true %>
<% end %>
<%= f.submit "Create my account", class: "btn btn-large btn-primary", id: "submitacct" %>
<% end %>
Sample app from RailsCasts
RailsCasts Episode #196: Nested Model Form (revised)
Maybe help you.
I am new to Rails 3 and having trouble with saving records in the Join table. I have been looking around and trying different examples found on this website and from the documentation or books, but I don't understand why I can't get it to work. I am trying to create Authorization by creating Roles and associate them to users. So far I have been trying to assign roles from the update action in the Users controller without prevail.
I have 3 models: the User.rb, role.rb, and assignment.rb (the join table)
class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :roles, :through => :assignments, :foreign_key => :role_id
accepts_nested_attributes_for :roles
attr_accessor :password, :role_ids
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :status, :description, :username, :roles_attributes
...
end
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments, :foreign_key => :user_id
accepts_nested_attributes_for :users
attr_accessible :name
end
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
accepts_nested_attributes_for :roles
end
The Users controller in the update action I have the following
class UsersController < ApplicationController
...
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
#user.roles.build
flash[:success] = "Profile updated"
redirect_to #user
else
#title = "Edit" + " " + #user.username
render 'edit'
end
end
...
end
and in the 'edit' view page I intend to have checkboxes to update the User record with an associated role:
EDIT: Changed the "check_box" with "check_box_tag" ... the check boxes appear properly, but the values are not saved.
<%= form_for(#user) do |f| %>
...
<div class="field">
<%= f.label :roles %><br />
<%= f.fields_for :role_ids do |r| %>
<% #roles.each do |role| %>
<%= check_box_tag "user[roles][]", role.id, #user.roles.include?(role.id) %>
<%= role.name %>
<% end %>
<%= hidden_field_tag "user[roles][]", "" %>
<% end %>
</div>
<% end %>
With this code I even get an error where 'Roles' have no association.
EDIT: this was corrected with the accepts_nested_attributes_for :role. Thanks!
No association found for name `roles'. Has it been defined yet?
I am really confused where I am doing something wrong. Your help would be much appreciated.
Aurelien
You have to use the same name with "accepts_nested_attributes_for" as you used defining the association:
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
accepts_nested_attributes_for :role
end
Finally solved the problems and thought I could share.
The models associations but I did change the attr_accessible:
class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :roles, :through => :assignments, :foreign_key => :role_id
accepts_nested_attributes_for :roles
attr_accessor :password
attr_accessible ..., :roles_ids
...
end
In the User controller for the edit and update action.
def edit
#title = "Edit" + " " + #user.username
#roles = Role.find(:all)
#user.assignments.build
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
redirect_to #user
else
#title = "Edit" + " " + #user.username
render 'edit'
end
end
The important part was the view part and assigning the right names for the checkbox tags
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :roles %><br />
<%= f.fields_for :role_ids do |r| %>
<% #roles.each do |role| %>
<%= check_box_tag "user[role_ids][]", role.id, #user.roles.include?(role) %>
<%= role.name %>
<% end %>
<%= hidden_field_tag "user[role_ids][]", #user.id %>
<% end %>
</div>
The check_box_tag lets the form save an array and gives more control than check_box
Then in order to assign the multiple Role ids, the name of the check_box_tag should include user[roles_ids][].
Finally the last parameter of the check_box_tag returns if the User has already the roles and checks the checkboxes if true.
I must admit that the name part of the check_box_tags is really confusing but it works :).
I have following two models
class Office < ActiveRecord::Base
has_many :locations, :dependent => :destroy
end
class Location < ActiveRecord::Base
belongs_to :office
end
I have a new.html.erb for the office model and the below code in OfficeController
def create
#office = Office.new(params[:deal])
if #office.save
redirect_to office_url, :notice => "Successfully created office."
else
render :action => 'new'
end
end
How can I add fields for Location model in new.html.erb of Office?
I want to be able to have fields for locations in the same form.
You'll have to use nested attributes to do this. Fortunately, Rails makes it pretty easy. Here's how to do it:
First, signify to Office that you're giving it Location fields as well by adding this line:
accepts_nested_attributes_for :location.
Now, in new.html.erb, add the fields that you want. Say we want to have city and state:
<%= f.fields_for :location do |ff| %>
<%= ff.label :city %>
<%= ff.text_field :city %>
<%= ff.label :state %>
<%= ff.text_field :state %>
<% end %>
That's it!