I have an Entry model and a Category model, where an Entry can have many Categories (through EntryCategories):
class Entry < ActiveRecord::Base
belongs_to :journal
has_many :entry_categories
has_many :categories, :through => :entry_categories
end
class Category < ActiveRecord::Base
has_many :entry_categories, :dependent => :destroy
has_many :entries, :through => :entry_categories
end
class EntryCategory < ActiveRecord::Base
belongs_to :category
belongs_to :entry
end
When creating a new Entry, I create it by calling #journal.entries.build(entry_params), where entry_params is the parameters from the entry form. If any categories are selected, however, I get this error:
ActiveRecord::HasManyThroughCantDissociateNewRecords in Admin/entriesController#create
Cannot dissociate new records through 'Entry#entry_categories' on '#'. Both records must have an id in order to delete the has_many :through record associating them.
Note that the '#' on the second line is verbatim; it doesn't output an object.
I have tried naming my categories selectbox on the form to categories and category_ids but neither make a difference; if either is in the entry_params, the save will fail. If no categories are selected, or I remove categories from entry_params (#entry_attrs.delete(:category_ids)), the save works properly, but the categories don't save, obviously.
It seems to me that the problem is that an EntryCategory record is attempting to be made before the Entry record is saved? Shouldn't build be taking care of that?
Update:
Here's the relevant parts of schema.rb, as requested:
ActiveRecord::Schema.define(:version => 20090516204736) do
create_table "categories", :force => true do |t|
t.integer "journal_id", :null => false
t.string "name", :limit => 200, :null => false
t.integer "parent_id"
t.integer "lft"
t.integer "rgt"
end
add_index "categories", ["journal_id", "parent_id", "name"], :name => "index_categories_on_journal_id_and_parent_id_and_name", :unique => true
create_table "entries", :force => true do |t|
t.integer "journal_id", :null => false
t.string "title", :null => false
t.string "permaname", :limit => 60, :null => false
t.text "raw_body", :limit => 2147483647
t.datetime "created_at", :null => false
t.datetime "posted_at"
t.datetime "updated_at", :null => false
end
create_table "entry_categories", :force => true do |t|
t.integer "entry_id", :null => false
t.integer "category_id", :null => false
end
add_index "entry_categories", ["entry_id", "category_id"], :name => "index_entry_categories_on_entry_id_and_category_id", :unique => true
end
Also, saving an entry with categories works fine in the update action (by calling #entry.attributes = entry_params), so it does seem to me that the problem is only based on the Entry not existing at the point that the EntryCategory records are attempted to be created.
I tracked down the cause of this error to be within the nested_has_many_through plugin. It seems that the version I had installed was buggy; after updating to the most recent version, my build works again.
Why do you call
self.journal.build(entry_params)
instead of
Entry.new(entry_params)
If you need to create a new entry associated to a specific Journal, given a #journal, you can do
#yournal.entries.build(entry_params)
Related
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.
so i have 2 models:
create_table "holders", :force => true do |t|
t.string "faceid"
t.integer "badges_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "holders", ["badges_id"], :name => "index_holders_on_badges_id"
create_table "badges", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
i need 2 things:
to get all the badges of a certain faceid holder
to get all the holders of a certain badge.
i know its really noobs question but until now i didnt work with references so i dont really understood from the literature how to make the connection.
You actually need a many to many association on your holder and badge models. So you have to options either use has many :through or use has_and_belongs_to_many. The difference between the two can be found here. I am taking the example for has_many :through.
You need to create three models.
class Holder < ActiveRecord:Base
has_many :badges_holders
has_many :badges, :through => :badges_holders
end
class Badge < ActiveRecord:Base
has_many :badges_holders
has_many :holders, :through => :badges_holders
end
class BadgesHolder < ActiveRecord:Base
belongs :badge
belongs :holder
end
And your migration files needs to be:
create_table "holders", :force => true do |t|
t.string "faceid"
t.timestamps
end
add_index "holders", ["badges_id"], :name => "index_holders_on_badges_id"
create_table "badges", :force => true do |t|
t.string "name"
t.text "description"
t.timestamps
end
create_table "badges_holders", :force => true do |t|
t.integer "holder_id"
t.integer "badge_id"
t.timestamps
end
Now you can easily use Holder.find_by_faceid('xyz').badges to find the all hedges held by the holder whose faced is xyz. And Badge.first.holders to get all the holders for the first bedge.
For your question HABTM will be a good option as you do not need any extra field in the join table, so you can just use has_and_belongs_to_many in both of your models and you don't need BadgesHolder model in that case. And for the migration of the join table, replace first line with create_table "badges_holders", :id => false, :force => true do |t| a and remove t.timestamps as the join table for HABTM should not have any other column than the foreign keys.
If it's some Ruby on Rails, you must have 2 models :
class Holder < ActiveRecord:Base
has_many :badges
end
class Badge < ActiveRecord:Base
belongs_to :holder
end
Your entry called badges_id should not be in your holders table ; you should have a holder_id on your "badges" table.
Then, you can simply call
Holder.find_by_faceid('foobar').badges
and
Badge.find(1337).holder
If your badge can belongs to many holders, then you have to write a has_and_belongs_to_many relation.
I am rewriting a PHP based support ticket system in Rails and have run into a snag.
I have my users table created and my tickets table created
create_table "tickets", :force => true do |t|
t.integer "user_id", :null => false
t.integer "department_id", :null => false
t.integer "upload_id", :null => false
t.string "subject", :null => false
t.text "body", :null => false
t.string "status_id", :null => false
t.text "url", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
t.string "fName", :null => false
t.string "lName", :null => false
t.string "seKey", :null => false
t.boolean "isAdmin"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "auth_token"
end
Everything is working great, I am able to create tickets, etc...
Now I am needing to assign 1 or more admins to a ticket and am not sure if I should use a has_and_belongs_to_many relationship or a has_many :through relationship.
The way it is currently setup in PHP is just using a join table that matches a userID with a ticketID. I don't think I will ever need any other data relating to the relationship other than that so is has_and_belongs_to_many the best option?
Also, will this cause an issue since the ticket is already associated with a record in the user table (the original creator)? Basically a ticket will have multiple connections to the users table, one will be the person who submitted it and the rest are users who are assigned to handle it.
Both work; however, personally, I prefer a has_many :through relationship. The reason being is you have access to the join table. To do this generate a model called ticket_user with a field called user_id and ticket_id. Then in the model add
belongs_to :user
belongs_to :ticket
then you can add to the ticket model
has_many :ticket_users
has_many :users :through ticket_user
and this to the user model
has_many :ticket_users
has_many :ticket :through ticket_user
Then to retrieve all of a users tickets do
user.tickets
To get all of a tickets users do
ticket.users
For more info check out this guide
this may work - create two relationships between user and ticket, ticket belongs_to creator, :class_name user and ticket has_many assigned_users, class_name user and in user model has_many created_tickets and has_many assigned_tickets
In your case has_and_belongs_to_many is the extra relationship.
has_and_belongs_to_many :members, :class_name => "User", :join_table => "members_tickets"
You can have to create one table members_users with ticket_id and member_id as fields as shown in http://guides.rubyonrails.org/association_basics.html#choosing-between-has_many-through-and-has_and_belongs_to_many
now you can get list of assigned members like below
#ticket.members # list of members
#ticket.members << #user # add new member
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
I'm a complete rails newbie, so forgive me if this is trivial.
I have an Inventory model that either belongs_to a Store or a Traveling Party:
class Inventory < ActiveRecord::Base
belongs_to :trader, :polymorphic => true
end
class Store < ActiveRecord::Base
has_one :inventory, :as => :trader, :dependent => :destroy
end
class TravelingParty < ActiveRecord::Base
has_many :travelers, :dependent => :destroy
has_one :inventory, :as => :trader, :dependent => :destroy
validates_presence_of :speed, :ration, :position
accepts_nested_attributes_for :travelers, :reject_if => :reject_traveler, :allow_destroy => true
accepts_nested_attributes_for :inventory, :allow_destroy => true
def reject_traveler(attributes)
attributes['profession'].blank? and attributes['name'].blank?
end
end
I created a form that, when submitted, creates a Traveling Party and a number of Travelers. Now I'd like the form to also create an Inventory and initialize all the variables to 0. I know the following doesn't address variable initialization, but it doesn't even seem to put a row of null values into the Inventory database table.
class TravelingPartiesController < ApplicationController
def new
#traveling_party = TravelingParty.new
5.times do
traveler = #traveling_party.travelers.build
end
#inventory = #traveling_party.inventory.create
end
def create
#traveling_party = TravelingParty.new(params[:traveling_party])
if #traveling_party.save
flash[:notice] = "Successfully created traveling party and travelers."
redirect_to '/store/'
else
flash[:error] = "Please specify a leader."
redirect_to '/new/'
end
end
def index
end
end
For good measure, here is what the database schema looks like:
ActiveRecord::Schema.define(:version => 20111018224808) do
create_table "inventories", :force => true do |t|
t.integer "ox"
t.integer "food"
t.integer "clothing"
t.integer "ammunition"
t.integer "money"
t.integer "axle"
t.integer "wheel"
t.integer "tongue"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "trader_id"
end
create_table "stores", :force => true do |t|
t.string "name"
t.integer "location"
t.integer "priceScale"
t.datetime "created_at"
t.datetime "updated_at"
end
# Could not dump table "travelers" because of following StandardError
# Unknown type 'relations' for column 'traveling_party_id'
create_table "traveling_parties", :force => true do |t|
t.integer "speed"
t.integer "ration"
t.integer "position"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Is there a reason the inventory database table isn't being affected at all? And once that works, what would be the best way to initialize a traveling_party.inventory to have all 0s? (i.e., values for ox, food, clothing, etc).
This may because your inventories table does not include a 'trader_type'. This is required for polymorphic associations.
create_table "inventories", :force => true do |t|
t.integer "trader_id"
t.string "trader_type"
end
Edit:
To set all the values initially to 0, the best way would be to put a default value onto the fields in the table. (If you want it to always be initialized to 0 if there is no other option, otherwise they will default to nil)
I believe you can create a migration with
change_table(:inventories) do |t|
t.change :ox, :integer, :default => 0
end