I have a rails app that is using Active Admin. Basic flow is that I have an Incident model has associations with other models. Connections other models seem fine but for one model (Involves) in particular I get the error uninitialized constant Incident::Involf when creating a new Incident.
Incident model looks like this
class Incident < ActiveRecord::Base
# Relationships
belongs_to :admin_user
has_many :images
has_and_belongs_to_many :incident_types
has_and_belongs_to_many :involves
has_and_belongs_to_many :special_considerations
belongs_to :county
belongs_to :location
# Nested Attributes
accepts_nested_attributes_for :images
accepts_nested_attributes_for :county
accepts_nested_attributes_for :location
accepts_nested_attributes_for :incident_types
accepts_nested_attributes_for :involves
accepts_nested_attributes_for :special_considerations
end
The Involve model looks like this
class Involve < ActiveRecord::Base
# Relationships
has_and_belongs_to_many :incidents
# Aliases
alias_attribute :name, :involved
end
The active admin model for nested Incidents look like this
f.inputs "Vehicles Involved" do
f.input :involves, :as => :check_boxes
end
f.inputs "Special Considerations" do
f.input :special_considerations, :as => :check_boxes
end
Where Special Considerations work if I have f.input :involves, :as => :check_boxes commented out, but I get this error if I don't have it commented out.
Looked at the database and the associations and the code is very similar to others that I am not sure what the problem is.
This issue is resolved.
It looked liked to be an issue with either the pluralization of Involve or involve is a keyword.
I changed the model name to Incident_Involvement and that resolved the issue.
Related
I've seen several questions asked about this along the same vein, e.g.
Using HABTM or Has_many through with Active Admin
but I'm still struggling to get things to work (I've tried multiple ways at this point).
My models (slightly complicated by the 'technician' alias for the user model):
class AnalysisType < ActiveRecord::Base
has_many :analysis_type_technicians, :dependent => :destroy
has_many :technicians, :class_name => 'User', :through => :analysis_type_technicians
accepts_nested_attributes_for :analysis_type_technicians, allow_destroy: true
end
class User < ActiveRecord::Base
has_many :analysis_type_technicians, :foreign_key => 'technician_id', :dependent => :destroy
has_many :analysis_types, :through => :analysis_type_technicians
end
class AnalysisTypeTechnician < ActiveRecord::Base
belongs_to :analysis_type, :class_name => 'AnalysisType', :foreign_key => 'analysis_type_id'
belongs_to :technician, :class_name => 'User', :foreign_key => 'technician_id'
end
I have registered an ActiveAdmin model for the AnalysisType model and want to be able to select (already created) Technicians to associate with that AnalysisType in a dropdown/checkbox. My ActiveAdmin setup currently looks like:
ActiveAdmin.register AnalysisType do
form do |f|
f.input :analysis_type_technicians, as: :check_boxes, :collection => User.all.map{ |tech| [tech.surname, tech.id] }
f.actions
end
permit_params do
permitted = [:name, :description, :instrumentID, analysis_type_technicians_attributes: [:technician_id] ]
permitted
end
end
Whilst the form seems to display okay, the selected technician does not get attached upon submitting. In the logs I'm getting an error 'Unpermitted parameters: analysis_type_technician_ids'.
I've tried multiple ways of doing this following advice in other related SO pages but am always coming up against the same issue, i.e. unpermitted parameterd of some nature. Can anyone point out what I am doing wrong? (I'm using Rails 4 by the way)
Managing the association via has_and_belongs_to_many or has_many relations
does not require the use of accepts_nested_attributes_for. This type of form
input is managing the Technician IDs associated with the AnalysisType record.
Defining your permitted parameters and form like the following should allow
those associations to be created.
ActiveAdmin.register AnalysisType do
form do |f|
f.input :technicians, as: :check_boxes, collection: User.all.map { |tech| [tech.surname, tech.id] }
f.actions
end
permit_params :name, :description, :instrumentID, technician_ids: []
end
In the case where the creation of new Technician records is required that is
when the accepts_nested_attributes_for would be used.
Note: Updated answer to match comments.
I am trying to set up a standard relationship as following:
class Category < ActiveRecord::Base
has_many :post_categories
has_many :posts, :through => :post_categories
accepts_nested_attributes_for :post_categories
end
class Post < ActiveRecord::Base
has_many :post_categories
has_many :categories, :through => :post_categories
accepts_nested_attributes_for :post_categories
attr_accessor :category_ids
end
class PostCategory < ActiveRecord::Base
belongs_to post
belongs_to category
end
I am using ActiveAdmin and need to set up checkboxes to describe the relationship.
I have tried many different ways to have the checkboxes save. Here is my admin post.rb file:
ActiveAdmin.register Post do
permit_params :content, category_ids: []
form do |f|
f.inputs # Include the default inputs
f.inputs "Categories" do
f.input :categories, as: :check_boxes, collection: Category.all
end
f.actions # Include the default actions
end
end
I have tried different permit params such as
permit_params :content, :categories
permit_params :content, post_categories_attributes: [:id, :post_id, :category_id]
permit_params :content, category_ids: [:id]
The database is set up as shown in the rails tutorial, and the relationship seems to work elsewhere except for being saved from activeadmin. I even tried to use param.permit! to permit all params, but still no luck.
I have found many posts of seemingly the same question, but many give different answers and nothing seems to work.
What is wrong?
Little late but this question is googled first for me, so I assume it can be helpful to other "googlers"
#models/post.rb
accepts_nested_attributes_for :categories#, allow_destroy: true
#AA Post conf file
permit_params :content, category_ids: []
Summary
Rails 3.2 with RefineryCMS 2.0. These are my pseudocode models:
Industry
name
has_many companies
has_many works through companies
Company
name
has_many works
belongs_to industry
Work
name
belongs to company
From an instance of Work, I can say work.company.name and get a the name of the associated company. I would expect it to follow that I could also say company.industry.name without a problem. However, I am getting an unhelpful error:
wrong constant name Refinery:Industries
What I would ultimately like to do is follow my associations all the way up ie work.company.industry.name, but the chain is broken between company and industry it seems.
What am I doing wrong here? Here's my code in more detail.
Code
Here are my models. Any idea what would prevent me from accessing industry attributes from an associated company given that industries have_many companies (companys lol) and companies belong_to an industry? Any help would be much appreciated.
Industry Model
module Refinery
module Industries
class Industry < Refinery::Core::BaseModel
...
attr_accessible :name, :description, :position
has_many :companys, :class_name => '::Refinery::Companys::Company', :dependent => :nullify
has_many :works, :through => :companys
end
end
end
Company Model
module Refinery
module Companys
class Company < Refinery::Core::BaseModel
...
attr_accessible :name, :position, :industry_id
has_many :works, :class_name => '::Refinery::Works::Work', :dependent => :destroy
belongs_to :industry, :class_name => '::Refinery:Industries::Industry'
end
end
end
Work Model
module Refinery
module Works
class Work < Refinery::Core::BaseModel
...
attr_accessible :name, :description, :position, :company_id
belongs_to :thumbnail, :class_name => '::Refinery::Image'
belongs_to :Company, :class_name => '::Refinery::companys::company'
end
end
end
Then in my erb file I'm doing this:
<% #works.each do |work| %>
...
<h5>
<%= work.company.name %>
</h5>
<% end %>
That one works.
This one gives me an error though:
<% #clients.each do |client| %>
<h5>
<%= client.industry.name %>
</h5>
<% end %>
That error reads:
wrong constant name Refinery:Industries
There is at least a double colon missing in your Company model:
belongs_to :industry, :class_name => '::Refinery:Industries::Industry'
should be
belongs_to :industry, :class_name => '::Refinery::Industries::Industry'
I haven't really looked at the rest of the code, but this is a first error.
How can I validate relations in models in RoR? For example I have 3 models:
class Post < ActiveRecord::Base
belongs_to :blog
has_one :user, :through => :blog
validates :blog_id, :presence => true
end
class Blog < ActiveRecord::Base
belongs_to :user
has_many :posts, :dependent => :destroy
end
class User < ActiveRecord::Base
has_many :blogs
has_many :posts, :through => :blogs
end
And in my controller:
#post = current_user.blogs.find(params[:post].delete(:blog_id)).posts.build(params[:post])
But when I want to create post I get:
Can't mass-assign protected attributes: blog_id
I shouldn't get this error, because I am delete blog_id from params hash, or don't? Any way, what the better way of validating blog_id accessory to User.blogs in my Post model?
If you want to set the on which blog the post should be published after writing, you have to put the blog_id into the whitelist by setting attr_accessible
So in your example your Post model should look like
class Post < ActiveRecord::Base
belongs_to :blog
attr_accessible :blog_id, :title, :content
validates :blog_id, :presence => true
end​
Besides this. Be careful how you set up your relation. The difference between has_one and belongs_to is where the foreign key goes. It goes to where you define the belongs_to. has_one says that one of something is yours, so something points back to you. It doesn't make much sense to say that a Post has_one user...
It is enough to have a Post only belong to a Blog. You still can do something like current_user.posts by how you setup the relationship in the user model like you already did...
I'd recommend reading the following links http://guides.rubyonrails.org/association_basics.html
http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
Validations for :blog_id in Post model is perfect.
#post = current_user.blogs.find(params[:post].delete(:blog_id)).posts.build(params[:post])
:blog_id is deleted from params[:post], but let's look at the thing from a different view.
#blog = current_user.blogs.find(params[:post].delete(:blog_id))
#post = #blog.build(params[:post])
params[:post] does not have :blog_id, but build method automatically assigns blog_id to #blog.id.
That's why error for :blog_id is not coming up.
If you wanna avoid the mass assignment warning, you can make the :blog_id attribute accessible.
Given the following associations, I need to reference the Question that a Choice is attached through from the Choice model. I have been attempting to use belongs_to :question, through: :answer to perform this action.
class User
has_many :questions
has_many :choices
end
class Question
belongs_to :user
has_many :answers
has_one :choice, :through => :answer
end
class Answer
belongs_to :question
end
class Choice
belongs_to :user
belongs_to :answer
belongs_to :question, :through => :answer
validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ]
end
I am getting
NameError uninitialized constant User::Choice
when I try to do current_user.choices
It works fine, if I don't include the
belongs_to :question, :through => :answer
But I want to use that because I want to be able to do the validates_uniqueness_of
I am probably overlooking something simple. Any help would be appreciated.
You can also delegate:
class Company < ActiveRecord::Base
has_many :employees
has_many :dogs, :through => :employees
end
class Employee < ActiveRescord::Base
belongs_to :company
has_many :dogs
end
class Dog < ActiveRecord::Base
belongs_to :employee
delegate :company, :to => :employee, :allow_nil => true
end
Just use has_one instead of belongs_to in your :through, like this:
class Choice
belongs_to :user
belongs_to :answer
has_one :question, :through => :answer
end
Unrelated, but I'd be hesitant to use validates_uniqueness_of instead of using a proper unique constraint in your database. When you do this in ruby you have race conditions.
A belongs_to association cannot have a :through option. You're better off caching the question_id on Choice and adding a unique index to the table (especially because validates_uniqueness_of is prone to race conditions).
If you're paranoid, add a custom validation to Choice that confirms that the answer's question_id matches, but it sounds like the end user should never be given the opportunity to submit data that would create this kind of mismatch.
My approach was to make a virtual attribute instead of adding database columns.
class Choice
belongs_to :user
belongs_to :answer
# ------- Helpers -------
def question
answer.question
end
# extra sugar
def question_id
answer.question_id
end
end
This approach is pretty simple, but comes with tradeoffs. It requires Rails to load answer from the db, and then question. This can be optimized later by eager loading the associations you need (i.e. c = Choice.first(include: {answer: :question})), however, if this optimization is necessary, then stephencelis' answer is probably a better performance decision.
There's a time and place for certain choices, and I think this choice is better when prototyping. I wouldn't use it for production code unless I knew it was for an infrequent use case.
So you cant have the behavior that you want but you can do something that feels like it. You want to be able to do Choice.first.question
what I have done in the past is something like this
class Choice
belongs_to :user
belongs_to :answer
validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ]
...
def question
answer.question
end
end
this way the you can now call question on Choice
It sounds like what you want is a User who has many Questions.
The Question has many Answers, one of which is the User's Choice.
Is this what you are after?
I would model something like that along these lines:
class User
has_many :questions
end
class Question
belongs_to :user
has_many :answers
has_one :choice, :class_name => "Answer"
validates_inclusion_of :choice, :in => lambda { answers }
end
class Answer
belongs_to :question
end
The has_many :choices creates an association named choices, not choice. Try using current_user.choices instead.
See the ActiveRecord::Associations documentation for information about about the has_many magic.