I have the following models:
class Programme < ActiveRecord::Base
has_and_belongs_to_many :nationalities, class_name: 'Nation', join_table: 'nationalities_nations'
has_and_belongs_to_many :destinations, class_name: 'Nation', join_table: 'destinations_nations'
accepts_nested_attributes_for :nationalities
accepts_nested_attributes_for :destinations
end
and
class Nation < ActiveRecord::Base
has_and_belongs_to_many :nationality_programmes, class_name: 'Programme', join_table: 'nationalities_nations'
has_and_belongs_to_many :destination_programmes, class_name: 'Programme', join_table: 'destinations_nations'
accepts_nested_attributes_for :nationality_programmes
accepts_nested_attributes_for :destination_programmes
end
In active admin I have the following configuration which pre-selects any existing stored country references correctly (See screenshot).
ActiveAdmin.register Programme do
permit_params :title,
destinations_ids: [:id],
nationalities_ids: [:id]
form do |f|
f.actions
f.inputs 'Countries / Regions' do
f.input :nationalities, :as => :select, :input_html => {:multiple => true}
f.input :destinations, :as => :select, :input_html => {:multiple => true}
f.input :title
end
f.actions
end
end
However, when I select other countries, the form successfully saves, but the references aren’t stored.
This is my schema:
ActiveRecord::Schema.define(version: 20140522131219) do
create_table "destinations_nations", force: true do |t|
t.integer "programme_id", null: false
t.integer "nation_id", null: false
end
create_table "levels_programmes", force: true do |t|
t.integer "programme_id", null: false
t.integer "level_id", null: false
end
create_table "nationalities_nations", force: true do |t|
t.integer "programme_id", null: false
t.integer "nation_id", null: false
end
create_table "nations", force: true do |t|
t.string "slug", limit: 2
t.string "name"
end
create_table "programmes", force: true do |t|
t.string "title"
end
end
Update: Cross-posted this issue on active_admin#3196 which is now closed, thanks to Gregorio's help.
I made it work by changing
permit_params :title,
destinations_ids: [:id],
nationalities_ids: [:id]
to
permit_params :title,
destination_ids: [],
nationality_ids: []
Related
I'm using rails 4 with schema_plus gem.
I want to be able to access models this way:
phrase = Phrase.create(name:'car')
another_phrase = Phrase.create(name:'vehicle')
phrase.synonym_phrases << another_phrase
p phrase.synonym_phrases
My relationship setup:
class Phrase < ActiveRecord::Base
has_many :synonyms
has_many :synonym_phrases, through: :synonyms
end
class Synonym < ActiveRecord::Base
belongs_to :phrase
belongs_to :synonym_phrase, class_name: 'Phrase', foreign_key: :synonym_id
end
After migratind database, the generated schema looks like this:
ActiveRecord::Schema.define(version: 20141211103911) do
create_table "phrases", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "synonyms", force: true do |t|
t.integer "phrase_id"
t.integer "synonym_id"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["phrase_id"], :name => "fk__synonyms_phrase_id"
t.index ["synonym_id"], :name => "fk__synonyms_synonym_id"
t.foreign_key ["phrase_id"], "phrases", ["id"], :on_update => :no_action, :on_delete => :no_action, :name => "fk_synonyms_phrase_id"
t.foreign_key ["synonym_id"], "synonyms", ["id"], :on_update => :no_action, :on_delete => :no_action, :name => "fk_synonyms_synonym_id"
end
In which the second foreign key assigment is wrong, because it points to synonyms table, instead pointing to phrases table (self-referentiall).
I found out, that adding foreign_key: { references: :phrases } to migration solves the problem.
# db/migrate/20141211103911_create_synonyms.rb
class CreateSynonyms < ActiveRecord::Migration
def change
create_table :synonyms do |t|
t.integer :phrase_id
t.integer :synonym_id, foreign_key: { references: :phrases }
t.timestamps
end
end
end
After database drop and migrate the schema was generated correctly.
# db/schema.rb
- t.foreign_key ["synonym_id"], "synonyms", ["id"], :on_update => :no_action, :on_delete => :no_action, :name => "fk_synonyms_synonym_id"
+ t.foreign_key ["synonym_id"], "phrases", ["id"], :on_update => :no_action, :on_delete => :no_action, :name => "fk_synonyms_synonym_id"
I'm curious is there a way of setting this relationship without editing the migration files? Maybe some other naming convention in models? Any ideas?
I followed http://railscasts.com/episodes/17-habtm-checkboxes-revised?view=asciicast tutorial to set up a has_many through relationship and when I try to access information from one model it works but not from the other.
I can access the Category information from the Product model via #product.category_ids and #product.categories, but the reverse isn't true. I can't access the Product information from the Category model. Using #category.product_ids or #category.products gives me the error NoMethodError: undefined method 'product_ids' for #<Category:0x007fa70d430e98>
Product.rb
class Product < ActiveRecord::Base
attr_accessible :category_ids
has_many :categorizations
has_many :categories, through: :categorizations
accepts_nested_attributes_for :categorizations, :allow_destroy => true
end
Category.rb
class Category < ActiveRecord::Base
attr_accessible :product_ids
has_many :categorizations
has_many :products, through: :categorizations
end
-- EDIT --
Schema.rb
ActiveRecord::Schema.define(:version => 20130926192205) do
create_table "categories", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "products", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "categorization", :force => true do |t|
t.integer "product_id"
t.integer "category_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "categorization", ["product_id", "category_id"], :name => "index_categorization_on_product_id_and_category_id", :unique => true
add_index "categorization", ["product_id"], :name => "index_categorization_on_product_id"
add_index "categorization", ["category_id"], :name => "index_categorization_on_category_id"
end
To access the records from each object you should be able to:
#category.products
and
#product.categories
That will give you the associated objects.
product_ids is not a attribute on a category and it does not have accepts_attributes_for :products like your category model so removing attr_accessible :product_ids should fix the error.
In my database I am trying to get a one to many relationship between outlets and articles.
I am getting the following error when that relationship is used:
undefined method `outlet_id' for #<Article:0x007fc353887e58>
Here are the models:
class Article < ActiveRecord::Base
belongs_to :analyst
belongs_to :outlet
has_and_belongs_to_many :loe
attr_accessible :article_body, :author, :distribution, :loe, :most_important, :pubdate, :publication, :state, :submitted, :summary, :title, :url, :analyst_id, :loe_ids, :outlet_id
end
class Outlet < ActiveRecord::Base
has_many :articles, foreign_key: :title
attr_accessible :distribution, :name, :state, :article_ids
end
Here are the schema:
create_table "articles_loes", :id => false, :force => true do |t|
t.integer "article_id"
t.integer "loe_id"
end
create_table "loes", :force => true do |t|
t.string "name"
t.string "customer"
t.integer "article_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "loes", ["article_id"], :name => "index_loes_on_article_id"
create_table "outlets", :force => true do |t|
t.string "name"
t.integer "articles_id"
t.integer "distribution"
t.string "state"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "outlets", ["articles_id"], :name => "index_outlets_on_articles_id"
And here is the chunk of the view that calls on :outlet :
<div class="span4">
<%= f.association :loe %>
<%= f.association :outlet %>
</div>
If anyone has any ideas I'd really appreciate them. I think I might need an index of Outlets in Article? I'm not really sure how to implement that if that is the case. Thanks in advance.
Right now there is no way for your Outlet model to associate with the articles that it has. Once you say belongs_to, you need to have an outlet_id column. So you need to add an outlet_id (integer) column to your Article model and populate it with the id of the outlet they belong to. If an Article can belong to many outlets in that case you need to create a many-to-many relationship through a joint table.
Ahoy guys,
I'm new to Rails, and I feel like I'm definitely missing something crucial here, because it seems like this should be an easily solvable problem.
I've set up a Page model and a Coord model (with help from the getting started tutorial), and Coord successfully belongs_to Page. I'm trying to apply similar logic to make another model, Comment, belong to Coord, and only belong to Page via Coord.
Do I use :through for an association that (I think) only needs to link in one direction? As in Page < Coord < Comment?
At the moment I have:
class Page < ActiveRecord::Base
attr_accessible :description, :name
has_many :coords
has_many :comments, :through => :coords
end
Coord model:
class Coord < ActiveRecord::Base
belongs_to :page
has_many :comments
attr_accessible :coordinates, :x, :y
validates :x, :presence => true
validates :y, :presence => true
end
Then the Comment model:
class Comment < ActiveRecord::Base
belongs_to :coord
belongs_to :page
attr_accessible :body
end
I still keep getting errors about comments being an undefined method, or an association not being defined. Apologies if this is a common question, I don't personally know anyone who knows Rails, and the documentation only has examples too far removed from mine (to my knowledge). Thanks
Edit: added DB schema
ActiveRecord::Schema.define(:version => 20120712170243) do
create_table "comments", :force => true do |t|
t.text "body"
t.integer "coord_id"
t.integer "page_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "comments", ["coord_id"], :name => "index_comments_on_coord_id"
add_index "comments", ["page_id"], :name => "index_comments_on_page_id"
create_table "coords", :force => true do |t|
t.string "coordinates"
t.integer "x"
t.integer "y"
t.integer "page_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "coords", ["page_id"], :name => "index_coords_on_page_id"
create_table "pages", :force => true do |t|
t.string "name"
t.string "description"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
Page
class Page < ActiveRecord::Base
has_many :coords
has_many :comments, :through => :coords
end
Coord
class Coord < ActiveRecord::Base
belongs_to :page
has_many :comments
end
Comment
class Comment < ActiveRecord::Base
belongs_to :coord
has_one :page, :through => :coord
end
Using the above, you don't need page_id in the comments table.
Reference: A Guide to Active Record Associations
My migration right now :
class CreateActivities < ActiveRecord::Migration
def self.up
create_table :activities do |t|
t.integer :account_id, :null => false
t.integer :target_id, :null => false
t.string :target_type, :null => false
t.string :event_type, :null => false
t.integer :employee_id
t.string :name
t.timestamps
end
add_index :activities, [:target_id, :target_type]
With this I can now call a target like so :
Activity.first.target
And it will bring up the target_id, based on the target_type.
How would I do the opposite of that so that I can select a target, and if it has any associated Activities, they will show up?
Like so :
Job.find(1234).activities
# Where Job.find(1234) is the target_id of many activities.
If I understand right, this should do it:
class Job < ActiveRecord::Base
has_many :activities, :conditions => ['target_type = ?', 'Job'], :as => :target
end