I was following RyanB's polymorphic association video which shows examples of implementing a comment system.
http://railscasts.com/episodes/154-polymorphic-association-revised?autoplay=true
I was wondering how do I add a username into the database of the person that is creating the new comment? This way I can display username in the view pages,
Thanks
Too many ways to do it
Solution 1
If you will using authentication for comment system you should add one model user for authentication (suggest to use devise)
class User < ActiveRecord::Base
attr_accessible :email, :password, :username
has_many :comments
end
class Comment < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :commentable, polymorphic: true
belongs_to :user
end
and on controller (take from repository of 154-polymorphic-association-revised )
def create
#comment = #commentable.comments.new(params[:comment])
#comment.user_id = current_user.id
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
render :new
end
end
OR Solution 2
You just add one attribute to comment model (without authentication)
class Comment < ActiveRecord::Base
attr_accessible :content, :username
belongs_to :commentable, polymorphic: true
end
Related
I'm running into an issue with an associated model. I have a nested attributes for my user, to a reviewer. A user can essentially review another person, thus be a reviewer and be the person reviewed.
It's set up like this:
# User
has_many :reviewers
accepts_nested_attributes_for :reviewers
has_many :active_managements, class_name: 'Reviewer',
foreign_key: 'reviewer_id',
dependent: :destroy
class Reviewer < ActiveRecord::Base
belongs_to :user
belongs_to :reviewer_id, class_name: 'User'
end
now in my users controller I have:
class UsersController < ApplicationController
def edit
#user = User.find(params[:id])
#user.reviewers.build
redirect_to root_url && return unless #user.activated?
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to edit_user_path(#user)
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:invitation_token, :first_name, :admin,
:last_name, :title, :email, :password,
reviewers_attributes: [:reviewer_id])
end
the error that I get is:
User(#70197180889680) expected, got String(#70197172430700)
happening on "user_params", so I assume it has to do with my attributes. Anybody know what's up?
The line belongs_to :reviewer_id, class_name: 'User' is incorrect. Try changing it to something like belongs_to :reviewing_user, class_name: 'User' (replacing reviewing_user with whatever name you want to use for this association :), not field name.
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/belongs_to
The immediate fix is here:
#app/models/review.rb
class Reviewer < ActiveRecord::Base
belongs_to :reviewer
end
--
This is how I'd fix the systemic issue:
#config/routes.rb
resources :users do
resources :reviewers #-> url.com/users/:user_id/reviews/new
end
#app/controllers/reviewers_controller.rb
class ReviewersController < ApplicationController
def new
#user = User.find params[:user_id]
#review = user.reviewers.new
end
def create
#user = User.find params[:user_id]
#review = user.reviewers.new reviewer_params
end
private
def review_params
params.require(:reviewer).permit(:user_id, :reviewer_id)
end
end
Models
Apart from this, I think your main issue is the way your models are set up.
Ideally, you want to have reviewer and user as the same data-set (I presume they're both users), which makes your current setup really inefficient...
#app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :reviewers,
class_name: "User",
join_table: :reviewers_users
foreign_key: :user_id,
association_foreign_key: :reviewer_id
end
What you're looking for is something called a self referrential association, which basically allows you to associate the same model in a many-to-many relationship.
In most cases, this will be used with a has_and_belongs_to_many relationship; you could also use a has_many :through although it's not as common.
The above will allow you to use the following:
#user = User.find params[:id]
#user.reviewers #-> collection of users who are reviewers of original user
Currently, I'm making a simple blog-like app, where a user can make a post, and several other users can comment on it.
Is there a way to have a polymorphic attribute belong to more than one Model?
For example,
a Comment will always have an author (User model)
However, a Comment can belong to many other models (Posts, Journals, Articles, etc etc)
so, for (Posts, Journals, Articles) models, polymorphic association is best.
However, for the author (or User relationship), polymorphic would not work, since polymorphic can only belong to one at a time.
Is there a better way around this?
EDIT:
What are the pros/cons of doing this:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
EDIT2:
with the solution above, is there a more elegant way of doing this
def create
#comment = #commentable.comments.new(params[:comment])
#comment.user_id = current_user.id
if #comment.save
flash[:success] = 'Comment created'
redirect_to #commentable
else
flash[:error] = 'Comment not created - is body empty?'
redirect_to #commentable
end
end
without having to save the user_id manually in the controller?
#comment.user_id = current_user.id
You can have both a User relationship as well as a polymorphic relationship representing the model it is associated with. For example:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :document, polymorphic: true
end
class Post < ActiveRecord::Base
has_many :comments, as: :document
end
class Journal < ActiveRecord::Base
has_many :comments, as: :document
end
class Article < ActiveRecord::Base
has_many :comments, as: :document
end
class User < ActiveRecord::Base
has_many :comments
end
Now, you can call comment.user to get the User model for the person who created the comment and comment.document to get the Post, Journal, or Article that the comment is associated with.
I have three models User, Blog and Comment.
User.rb
class User < ActiveRecord::Base
attr_accessible blah blah
has_many :blogs
has_many :comments
end
Blog.rb
class Blog < ActiveRecord::Base
attr_accessible :user_id, :title, :content
belongs_to :user
has_many :comments
end
Comment.rb
class Comment < ActiveRecord::Base
attr_accessible :user_id, :blog_id, :comment
belongs_to :blog
belongs_to :user
end
In create action of Comments Controller
def create
#blog = Blog.where('id=?', params[:blog_id])
#comment = #blog.comments.new(params[:comment])
#comment.save
end
Here how will I pass id of current_user in the :user_id field of comments table, I can make a hidden field for that but it's not safe. PLease help! Thanks in advance.
Would this do what you want?
def create
#blog = Blog.where('id=?', params[:blog_id])
#comment = #blog.comments.new(params[:comment])
#comment.user = current_user # force the user to be the logged-in user
#comment.save
end
I'm feeling a little bit dumb to ask this, but I've been Googling my a*# off.
Well I have the following models:
class Company < ActiveRecord::Base
has_many :employments
has_many :users, through: :employments
validates_presence_of :name
validates_presence_of :description
validates_numericality_of :zip, only_integer: true
validates_presence_of :email
validates_presence_of :street
validates_presence_of :city
validates_presence_of :country
validates_presence_of :telephone
end
class Employment < ActiveRecord::Base
belongs_to :user
belongs_to :company
end
class User < ActiveRecord::Base
has_many :employments
has_many :companies, through: :employments
end
Important here is the company-Model which has some validations.
Now, I have the following Controller to create a new Company:
class CompaniesController < ApplicationController
def create
#company = Company.new(company_params) # The params were set with a private Method
#employment = #company.employments.build(user: current_user, is_admin: true)
if #employment.save
redirect_to :back, flash: { success: 'Success' }
else
#title = 'Create a new company'
render :new
end
end
end
The Problem is, that when I leave the companies-Fields blank, the company gets not created, but the employment-Model gets persistet in the Database.
I believe It has something to do with the Company.new()-Call I have to check, if the #company-Model gets created first, before the #employment-Model gets created.
How can I achieve that the validation gets tested first?
Thank you very much!
To validate associated object you need to use validates_associated. Please note the "Warning" and "Note" in the linked api document.
Try:
class Employment < ActiveRecord::Base
belongs_to :user
belongs_to :company
validates_associated :company
end
in addition to vinodadhikary's answer, you can also try saving the company. so instead of #employment.save, use #company.save. That should also save #employment when #company passes validations.
I have a has_many :through relationship between users and projects via an ownership join model. I want to be able to set an attribute of the ownership model while creating a relationship between a user and a new project. Here is what I have so far:
def create
#project = Project.new(params[:project])
if #project.save
current_user.projects << #project
flash[:success] = "Project created!"
redirect_to #project
else
flash[:error] = "Project not created."
end
end
Basically, I don't know how to set the value "owner_type" in the ownership model when creating a new project for a given user since I don't directly mention the ownership join model in the project creation controller. How do I do that?
Here is my ownership (join) model:
class Ownership < ActiveRecord::Base
attr_accessible :owner_type
belongs_to :project
belongs_to :user
validates :user_id, :presence => true
validates :project_id, :presence => true
validates :owner_type, :presence => true
end
and my User model:
class User < ActiveRecord::Base
attr_accessible :name, :email, :admin, :projects
has_many :ownerships
has_many :projects, :through => :ownerships
accepts_nested_attributes_for :projects
and my Project model:
class Project < ActiveRecord::Base
attr_accessible :name, :description
has_many :ownerships
has_many :users, :through => :ownerships
The key is that you build (not create) the association before you hit #project.save when you hit save the project is persisted first and if it was persisted successfully, the ownership will be created too.
def create
#project = Project.new(params[:project])
#project.ownerships.build(:user => current_user, :owner_type => 'chief')
if #project.save
flash[:success] = "Project created!"
redirect_to #project
else
flash[:error] = "Project not created."
end
end
EDIT: This didn't actually work for me.
In my user model I allow for nested attributes with this line:
accepts_nested_attributes_for :projects
Then, in my projects#create controller action, I nested an attribute while creating the association between the user and the new project as so:
current_user.ownerships.create(:owner_type => 'designer', :project => #project)
To be honest I'm not sure exactly why this works but it does. Would be awesome for someone else to explain it.