Similar Setup on Stackoverflow
Hi there,
I have a little problem with my nested form setup...
Model "TimeShifting"
class TimeShifting < ActiveRecord::Base
has_many :article_position_time_shifting_assignments, :dependent => :destroy
has_many :article_positions, :through => :article_position_time_shifting_assignments
accepts_nested_attributes_for :article_position_time_shifting_assignments
end
Join Model "ArticlePositionTimeShiftingAssignment"
class ArticlePositionTimeShiftingAssignment < ActiveRecord::Base
belongs_to :article_position
belongs_to :time_shifting
accepts_nested_attributes_for :article_position, :reject_if => proc { |obj| obj['baan_id'].blank? }
end
Model "ArticlePosition"
class ArticlePosition < ActiveRecord::Base
has_many :article_position_time_shifting_assignments
has_many :time_shiftings, :through => :article_position_time_shifting_assignments
end
The important point in here is that I have some extra attributes in the Join Model ArticlePositionTimeShiftingAssignment...
create_table "article_position_time_shifting_assignments", :force => true do |t|
t.integer "article_position_id"
t.integer "time_shifting_id"
t.integer "created_by"
t.integer "updated_by"
t.datetime "created_at"
t.datetime "updated_at"
t.string "order_number"
t.date "confirmed_date"
t.string "purchase_positions_collection"
end
created_by and updated_by are getting filled out automaticly, order_number and confirmed_date through the form.
Well... I have absolutly no problem to create a new ArticlePosition with
<%= f.simple_fields_for :article_position_time_shifting_assignments do |builder| %>
and then
<%= f.simple_fields_for :article_position do |builder| %>
My problem is that it's always creating a new article_position_time_shifting_assignment record. Even when it's not creating a new ArticlePosition.
#<ArticlePositionTimeShiftingAssignment id: 10, article_position_id: nil, time_shifting_id: 10, created_by: 1, updated_by: 1, created_at: "2012-05-23 14:57:27", updated_at: "2012-05-23 14:57:27", order_number: "", confirmed_date: nil, purchase_positions_collection: "">
Mhhhh... I don't want that :P
accepts_nested_attributes_for :article_position_time_shifting_assignments, :reject_if => proc { |obj| obj['article_position_id'].blank? }
This won't work because there is no article_position_id until the corresponding article_position has been saved :-/
Any ideas to solve this problem?
Cheers,
Michael
Actually it WILL create new record in Mapping table (ArticlePositionTimeShiftingAssignment) and it is correct because it is many-to-many association. So in this case it is not good approach to assign some additional records to Mapping table.
Related
I have the following setup:
Schema.rb
ActiveRecord::Schema.define(version: 20130923235150) do
create_table "addresses", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "user_addresses", force: true do |t|
t.integer "user_id"
t.integer "address_id"
t.string "purpose"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
end
end
User.rb:
class User < ActiveRecord::Base
has_one :user_address
has_one :primary_shipping_address, through: :user_address, class_name: :UserAddress, source: :address
has_one :primary_billing_address, through: :user_address, class_name: :UserAddress, source: :address
end
Address.rb:
class Address < ActiveRecord::Base
has_one :user_address
has_one :primary_shipping_user, through: :user_address, class_name: :UserAddress, source: :user
has_one :primary_billing_user, through: :user_address, class_name: :UserAddress, source: :user
end
UserAddress.rb:
class UserAddress < ActiveRecord::Base
belongs_to :user
belongs_to :address
end
When someone does user.primary_billing_address = address, I want the join model instance to have "billing" set as its purpose. Similarly with shipping and "shipping". Ex.
irb(main):013:0> u = User.new
=> #<User id: nil, created_at: nil, updated_at: nil>
irb(main):014:0> a = Address.create
=> #<Address id: 3, created_at: "2013-09-24 00:13:07", updated_at: "2013-09-24 00:13:07">
irb(main):015:0> u.primary_billing_address = a
=> #<Address id: 3, created_at: "2013-09-24 00:13:07", updated_at: "2013-09-24 00:13:07">
irb(main):016:0> u.save!
=> true
irb(main):017:0> u.user_address
=> #<UserAddress id: 2, user_id: 3, address_id: 3, purpose: nil, created_at: "2013-09-24 00:13:18", updated_at: "2013-09-24 00:13:18">
(not what I want... purpose should be "billing")
How can I do this such that it works for new AND persisted records?. I've come up with solutions that are 90% there, but break on some random spec due to an edge case my approach didn't catch.
The trickiest part to work around is how association= behaves: on new records, it queues the association for assignment through the join model.
PS: I left out the conditionals on the has_one relationships that I'd use to get the address I want. I think this issue is independent of that.
First, the associations are a bit off, both primary_shipping_address and primary_billing_address will return same address. You can change it to
class User < ActiveRecord::Base
has_many :user_addresses # user can have multiple addresses one for shipping and one for billing
has_one :primary_shipping_address,
through: :user_address, class_name: :UserAddress,
source: :address, :conditions => ['user_addresses.purpose = ?','shipping']
has_one :primary_billing_address,
through: :user_address, class_name: :UserAddress,
source: :address, :conditions => ['user_addresses.purpose = ?','billing']
end
To save the purpose while saving the address, there are two options.
Option 1 : Override the default association= method
# alias is needed to refer to original method
alias_method :orig_primary_billing_address=, :primary_billing_address=
def primary_billing_address=(obj)
self.orig_primary_billing_address = obj
self.user_addresses.where("address_id = ?", obj.id).update_attribute(:purpose, 'billing')
end
# repeat for shipping
Option 2 : Create a custom method (I prefer this as it is cleaner and DRY)
def save_address_with_purpose(obj,purpose)
self.send("primary_#{purpose}_address=", obj)
self.user_addresses.where("address_id = ?", obj.id).update_attribute(:purpose, purpose)
end
I'm having some problems with ruby on rails, specifically setting up a many-to-many connection with deal and event through deal_event. I've checked out several similar stackoverflow questions and even http://guides.rubyonrails.org/ but I'm still not getting something..
Here are my models:
deal.rb
class Deal < ActiveRecord::Base
has_many :deal_events
has_many :events, :through => "deal_events"
attr_accessible :approved, :available, :cents_amount, :dollar_amount, :participants, :type
end
event.rb
class Event < ActiveRecord::Base
has_many :deal_events
has_many :deals, :through => "deal_events"
attr_accessible :day, :image, :description, :location, :title, :venue, :remove_image
end
deal_event.rb
class DealEvent < ActiveRecord::Base
belongs_to :deal
belongs_to :event
end
And here are my migration files:
20130102150011_create_events.rb
class CreateEvents < ActiveRecord::Migration
def change
create_table :events do |t|
t.string :title, :null => false
t.string :venue
t.string :location
t.text :description
t.date :day
t.timestamps
end
end
end
20130112182824_create_deals.rb
class CreateDeals < ActiveRecord::Migration
def change
create_table :deals do |t|
t.integer :dollar_amount
t.integer :cents_amount
t.integer :participants
t.string :type, :default => "Deal"
t.integer :available
t.string :approved
t.timestamps
end
end
end
20130114222309_create_deal_events.rb
class CreateDealEvents < ActiveRecord::Migration
def change
create_table :deal_events do |t|
t.integer :deal_id, :null => false
t.integer :event_id, :null => false
t.timestamps
end
end
end
After I seed my database with one deal and one event, I go into the console and type in
deal = Deal.first # ok
event = Event.first # ok
DealEvent.create(:deal => deal, :event => event) # Error: ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: deal, event
deal.events # Error: ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association "deal_events" in model Deal
Any idea on what I'm doing wrong for these two errors to be popping up? Thanks.
You'll need this line in your DealEvent model:
attr_accessible :deal, :event
Although if it's just a relationship table (which it looks like) then you don't create the relationship that way. Use nested forms and the like.
I have a Model called Challenge that is created by a User. It has a Difficulty associated with it as well. When I create the Challenge and hand it the Author (User) and Difficulty, the Difficulty associate works, but the Author (User) one doesn't. The weirdest part is, when you look at the Challenge object, it shows the Author key its associated with.
challenge = Challenge.first
challenge.author (prints Nil) #Doesn't even run a Query
When I create a Challenge using the following code, the user_id is Nil.
user = User.find(1)
diff = Difficulty.find(1)
Challenge.create(:author => user, :difficulty => diff, :title => "My Challenge")
When I create a Challenge using this code, the User gets the relation to the Challenge and the Challenge gets the user_id of the User. But you can only go from User to Challenge. Challenge to User returns Nil.
user = User.find(1)
diff = Difficulty.find(1)
chall = Challenge.create(:difficulty => diff, :title => "My Challenge")
user.authored_challenges << chall
Here are my Models and Tables
# Models
class User < ActiveRecord::Base
has_many :authored_challenges, :class_name => "Challenge"
attr_accessible :display_name, :authored_challenges
end
class Reward < ActiveRecord::Base
has_many :challenges
attr_accessible :name, :value, :challenges
end
class Challenge < ActiveRecord::Base
belongs_to :reward
belongs_to :author, :class_name => "User"
attr_accessible :title, :description, :reward, :author
end
# Tables
create_table :users do |t|
t.string :display_name
t.timestamps
end
create_table :rewards do |t|
t.string :name
t.float :value
t.timestamps
end
create_table :challenges do |t|
t.integer :user_id
t.integer :reward_id
t.string :title
t.string :description
t.timestamps
end
According to: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html the foreign key should be:
:foreign_key => :author_id
Don't forget to add the field. Good Luck!
Have you tried:
belongs_to :author, :class_name => "User", :foreign_key => :user_id
From the Rails documentation:
By convention, Rails assumes that the column used to hold the foreign key on this model is the name of the association with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly
The example given in the docs is very similar to the one you have:
class Order < ActiveRecord::Base
belongs_to :customer, :class_name => "Patron", :foreign_key => "patron_id"
end
I have a Post model that has a polymorphic association with a Vote model:
post.rb:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :total_votes
belongs_to :user
has_many :votes, :as => :votable, :dependent => :destroy
end
vote.rb
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
before_create :update_total
protected
def update_total
self.votable.total_votes ||= 0
self.votable.total_votes += self.polarity
end
end
As you can see in vote.rb I want to accomplish the following:
Each time an instance of Vote is created, I want to update the total_votes column of the votable model instance (in this case an instance of Post).
But nothing happens, when I create a vote for a post with post.votes.create(:polarity => 1), the total_votes column of the post still being nil.
Any suggestions to fix this?
EDIT:
This didn't work either:
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
before_create :update_total
protected
def update_total
self.votable.total_votes ||= 0
self.votable.total_votes += self.polarity
self.votable.save!
end
end
schema.rb:
create_table "posts", :force => true do |t|
t.string "content"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "title"
t.integer "comments_count", :default => 0, :null => false
t.integer "total_votes"
end
When you create the Vote object, the Post object already exists. You update it, but you don't save it. Try with the following code:
def update_total
self.votable.total_votes ||= 0
self.votable.total_votes += self.polarity
self.votable.save!
end
I am struggling with a polymorphic many-to-many association in rails 3.
My Article model looks like this:
class Article < ActiveRecord::Base
has_many :article_tags
has_many :people, :through => :article_tags, :source => :taggable, :source_type => :person
end
My ArticleTag model looks like this:
class ArticleTag < ActiveRecord::Base
belongs_to :article
belongs_to :taggable, :polymorphic => true
end
My Person model looks like this:
class Person < ActiveRecord::Base
has_many :article_tags, :as => :taggable
end
Finally, my Organization model looks like this:
class Organization < ActiveRecord::Base
has_many :article_tags, :as => :taggable
end
I have a schema which looks like this:
ActiveRecord::Schema.define(:version => 20110322234836) do
create_table "article_tags", :force => true do |t|
t.integer "taggable_id"
t.string "taggable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "articles", :force => true do |t|
t.string "title"
t.text "content"
t.date "published_on"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "people", :force => true do |t|
t.string "first_name"
t.string "last_name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "organizations", :force => true do |t|
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
end
end
I was hoping that this would allow me to create people and organizations relationships from my articles like this:
Article.create!
Article.first.people.create!
and hopefully to be able to access it the other way afterwards by
Person.first.articles
Unfortunately I get an error when I try to add a person this way:
Article.first.people.create!
NameError: uninitialized constant Article::person
.../base.rb:1199:in `compute_type'
.../reflection.rb:162:in `klass'
.../association_collection.rb:157:in `transaction'
.../has_many_through_association.rb:41:in `create_record'
.../has_many_through_association.rb:13:in `create!
Any help would be very much appreciated I've tried many alternatives but with no success.
I actually just had to some something similar to this recently. It looks like most of your code is right although in mine for the first section I would try changing do this:
class Article < ActiveRecord::Base
has_many :article_tags
has_many :people, :through => :article_tags, :source => :taggable, :source_type => "Person"
end
Also you can't do: Article.first.people.create({:name => "Tim"})
You would have to assign it to a variable first:
article = Article.first
article.people.create({:name => "Tim"})
Let me know if this works for you. I just skimmed through. If it doesn't i can double check my code again and see if there are any other differences.