Building Rails Forms for saving Polymorphic Associations - ruby-on-rails

I have categories model which I would like to be able to use with different models. That's how I ended up using Polymorphic with has_many.
With Rails_admin everything works without a problem. But, when I want to create a form by myself, I can't seem to make it save. Here is what I have:
category.rb
class Category < ActiveRecord::Base
has_many :categorizings, inverse_of: :category, dependent: :destroy
has_many :cars, through: :categorizings, :source => :categorizable,
:source_type => 'Car'
end
categorizing.rb
class Categorizing < ActiveRecord::Base
belongs_to :category
belongs_to :categorizable, :polymorphic => true
end
car.rb
class Car < ActiveRecord::Base
has_many :categorizings, :as => :categorizable, inverse_of: :car, dependent: :destroy
has_many :categories, through: :categorizings
end
vendor.rb
class Vendor < ActiveRecord::Base
has_many :categorizings, :as => :categorizable, inverse_of: :vendor, dependent: :destroy
has_many :categories, through: :categorizings
end
cars_controller.rb
class CarsController < ApplicationController
def new
#car = Car.new
end
def create
#car = current_user.cars.build(car_params)
if #car.save
redirect_to #car
else
render 'new'
end
end
private
def car_params
params.require(:car).permit(:name, :details, :type, :category_ids => [] )
end
end
schema.rb
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "categorizings", force: :cascade do |t|
t.integer "category_id"
t.integer "categorizable_id"
t.string "categorizable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "categorizings", ["categorizable_type", "categorizable_id"], name: "index_categorizings_on_categorizable_type_and_categorizable_id", using: :btree
This is what I have in the form
<%= f.collection_select :category_ids, Category.all, :id, :name %>
And I receive this error:
Unpermitted parameter: category_ids
I am very confused right now and lost in models. Dont know this is the best approach or not. I would be glad if someone could tell me where I do the mistake.

Using this select_tag solved my problem
<%= f.select :category_ids, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>
but stops working if I disable multiple like
:multiple => false

Related

Rails nested attributes: validates_uniqueness_of doesn't work

I have 3 models:
class UserLanguage < ActiveRecord::Base
belongs_to :user
belongs_to :language
end
class Language < ActiveRecord::Base
has_many :user_languages
has_many :users, :through => :user_languages
end
class User < ActiveRecord::Base
has_many :user_languages, :dependent => :destroy
has_many :languages, :through => :user_languages
accepts_nested_attributes_for :user_languages, :allow_destroy => true
end
I'm using nested_form gem to help user select which language(s) they can speak in. The CRUD for that is working fine.
But, I can't validate uniqueness of the UserLanguage. I try this 2 syntax but they didn't work for me:
validates_uniqueness_of :language_id, scope: :user_id
validates :language_id, :uniqueness => {:scope => user_id}
My schema for user_languages table:
create_table "user_languages", force: :cascade do |t|
t.integer "user_id"
t.integer "language_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "user_languages", ["language_id"], name: "index_user_languages_on_language_id", using: :btree
add_index "user_languages", ["user_id"], name: "index_user_languages_on_user_id", using: :btree
What should I do to make sure one user can choose only a language once? Say, if I select English inside the dropdown, my second English will not be saved (duplicate) and rejected.
This is how I did it finally:
class UserLanguage < ActiveRecord::Base
belongs_to :user
belongs_to :language
def self.delete_duplicated_user_languages(user_id)
user_languages_ids = UserLanguage.where(user_id: user_id).pluck(:language_id).sort
duplicate_language_ids = user_languages_ids.select {|language| user_languages_ids.count(language) > 1}
duplicate_language_ids.uniq!
keep_this_language = []
duplicate_language_ids.each do |language_id|
keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language_id).id
end
single_language = user_languages_ids.select {|language| user_languages_ids.count(language) == 1}
single_language.each do |language|
keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language).id
end
UserLanguage.where(user_id: user_id).where.not(id: keep_this_language).destroy_all
end
end
I save all UserLanguages first and delete them (duplicate ones) later.
If User has and should only have one Language, then you could change the cardinality between the models:
class Language < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
has_one :language
end
Then by definition your users will only have one language at a time and your overall model will be simpler.
You can read more about active record associations and cardinalities in this Association Basics guide

Polymorphic association undefined method `build_product'

I am struggling to see what I am doing wrong here..
I have an order model which needs to be able to hold one product, the product needs to be polymorphic.
I have a product/model called orthosis_specification and for some reason I am getting this error when I use it in a fields_for creation.
Migration -
class CreateOrders < ActiveRecord::Migration
def change
create_table :orders do |t|
t.datetime :order_date
t.datetime :date_required
t.boolean :correct
t.references :user, index: true, foreign_key: true
t.references :practitioner, index: true, foreign_key: true
t.references :product, polymorphic: true
t.references :shipping_address, index: true, foreign_key: true
t.references :invoice_address, index: true, foreign_key: true
t.timestamps null: false
end
end
end
Order Controller -
def new
#order = Order.new
#order.build_patient
#order.build_product #Also tried: #order.build_orthosis_specification
end
Order Model -
class Order < ActiveRecord::Base
has_one :patient
belongs_to :product, polymorphic: true
accepts_nested_attributes_for :patient,
reject_if: proc { |attributes| attributes['first_name'].blank? },
allow_destroy: true
accepts_nested_attributes_for :product,
reject_if: proc { |attributes| attributes['transfer_name'].blank? },
allow_destroy: true
def to_s
name
end
end
Orthosis Specification Model -
class OrthosisSpecification < ActiveRecord::Base
has_one :order, :as => :product, class_name: 'Order'
end
Order View -
<%= form_for(#order) do |f| %>
<% if #order.errors.any? %>
<% end %>
<%= f.fields_for :orthosis_specification do |fa| %>
Actual error message -
undefined method `build_orthosis_specification' for #<Order:0x007f8950e29970>
Orthosis Specification Migration -
class CreateOrthosisSpecifications < ActiveRecord::Migration
def change
create_table :orthosis_specifications do |t|
t.string :transfer_name
t.string :modifications
t.string :primary_mods
t.string :top_opening
t.string :side_opening
t.string :chape_position
t.string :scan_file
end
end
end
Any help would be massively appreciated, thanks!
Polymorphic associations don't generate the build_xxx methods. You can create a new product only if you know what kind of product you want to create :
#Creating a new OrthosisSpecification product associated with #order :
#order.product = OrthosisSpecification.new
Documentation : http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Unknown primary key for table

I'm making an extension for refinerycms in rails and this is the structure I follow :
Project has many project_images
Project_images belongs_to Project & Image ( The refinery Image class )
Now when I want to create a new object of ProjectImage in my Project class I always get this error :
Unknown primary key for table refinery_projects_images in model Refinery::Projects::ProjectImage.
I don't need a primary key for this table because it is a join table. Here is the code of my models and migration file:
Migration.rb
class CreateProjectsProjects < ActiveRecord::Migration
def up
create_table :refinery_projects do |t|
t.string :title
t.text :description
t.string :investor
t.string :location
t.string :area
t.string :purpose
t.string :architect
t.string :users
t.integer :position
t.integer :position
t.timestamps
end
add_index :refinery_projects, :id
create_table :refinery_projects_images, :id => false do |t|
t.references :image
t.references :project
t.integer :position
t.string :category
t.string :caption
end
add_index :refinery_projects_images, [:image_id, :project_id], :uniq => true
end
def down
if defined?(::Refinery::UserPlugin)
::Refinery::UserPlugin.destroy_all({:name => "refinerycms-projects"})
end
if defined?(::Refinery::Page)
::Refinery::Page.delete_all({:link_url => "/projects/projects"})
end
drop_table :refinery_projects
drop_table :refinery_projects_images
end
end
Project.rb
module Refinery
module Projects
class Project < Refinery::Core::BaseModel
self.table_name = 'refinery_projects'
attr_accessible :title, :description, :investor, :location, :area, :purpose, :architect, :users, :position, :position, :images_attributes
acts_as_indexed :fields => [:title, :description, :investor, :location, :area, :purpose, :architect, :users]
validates :title, :presence => true, :uniqueness => true
has_many :project_images
has_many :images, :through => :project_images, :order => 'position ASC'
accepts_nested_attributes_for :images, :allow_destroy => false
def images_attributes=(data)
ProjectImage.delete_all(:project_id => self.id)
(0..(data.length-1)).each do |i|
unless (image_data = data[i.to_s]).nil? or image_data['id'].blank?
project_image = self.project_images.new(:image_id => image_data['id'].to_i, :position => i)
# Add caption if supported
if false
project_image.caption = image_data['caption']
end
self.project_images << project_image
end
end
end
end
end
end
ProjectImage.rb
module Refinery
module Projects
class ProjectImage < Refinery::Core::BaseModel
self.table_name = 'refinery_projects_images'
attr_accessible :image_id, :position
belongs_to :image, :class_name => 'Refinery::Image'
belongs_to :project, :class_name => 'Refinery::Projects::Project'
end
end
end
Somebody knows why he keeps looking for the primary key?
Refinery::Core::BaseModel is somehow derived from ActiveRecord::Base. When you use that class then your table layout needs an id. If you don't want an id, Have a look at has_and_belongs_to_many in the rails guides: http://guides.rubyonrails.org/association_basics.html

Is there a way to set the extra value in a join table

Ok so I have this relationship in Rails:
class Position < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
class User < ActiveRecord::Base
has_many :companies, :through => :positions
has_many :positions
class Company < ActiveRecord::Base
has_many :positions
has_many :users, :through => :positions
Here is the schema for positions:
create_table "positions", :force => true do |t|
t.integer "company_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.boolean "regular_user", :default => true
end
The regular_user is signaling the admins and the employees so my question is how do set the regular_user to 0 or false from this data:
#user = User.find(params[:user_id])
#company = Company.find(params[:company_id])
#user.companies << #company
Is there a better way to do this? I was thinking:
Position.create(user_id: params[:user_id], company_id: params[:company_id], regular_user: 0)
But is there a standard for setting associations?
Try this:
class User < ActiveRecord::Base
has_many :positions
has_many :companies, :through => :positions
has_many :companies_as_non_regular_user, :through => :positions,
:conditions => {:"positions.regular_user" => false}
...
end
#user = User.find(params[:user_id])
#company = Company.find(params[:company_id])
#user.companies_as_non_regular_user << #company

Rename foreign_key to override in Rails Convention

I have a problem in association between two classes, so i have a class table here named Post
Class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :post_type , null: false
t.text :content , null: false
t.integer :person_id
end
add_index :posts, :person_id
add_index :posts, :group_id
end
end
and the other one is called Action
class CreateActions < ActiveRecord::Migration
def change
create_table :actions do |t|
t.string :target_type, null:false
t.integer :target_id
t.integer :upvote_count
t.timestamps
end
add_index :actions,:target_id
end
end
so the problem is i want to associate the target_is as the foreign key to the Post class so i did this
class Post < ActiveRecord::Base
has_one :action
end
class Action < ActiveRecord::Base
belongs_to :post , :class_name => 'Target', :foreign_key => 'target_id'
end
but is doesn't work, which when i assign Action object to action method in Post object this error is appeared
Mysql2::Error: Unknown column 'actions.post_id' in 'where clause': SELECT `actions`.* FROM `actions` WHERE `actions`.`post_id` = 6 LIMIT 1
so any help?
You need to set the foreign key on both sides of the association:
class Post < ActiveRecord::Base
has_one :action, :foreign_key => 'target_id'
end
class Action < ActiveRecord::Base
belongs_to :post , :class_name => 'Target', :foreign_key => 'target_id'
end
http://guides.rubyonrails.org/association_basics.html#has_one-association-reference
http://guides.rubyonrails.org/association_basics.html#belongs_to-association-reference
I suppose you are trying to apply polymorphic association. Try this out.
class Post < ActiveRecord::Base
has_one :action, :as => :target
end
class Action < ActiveRecord::Base
belongs_to :target, :polymorphic => true
end

Resources