I'm getting the error unknown attribute id when I run
#user.payments.create(payment_params)
I know this means I need a user_id in my payments table. But I'm using polymorphic associations (perhaps incorrectly?) and my payments table has a payee_id and a payer_id (for each "type" of user). In the above method the #user instance is a payer.
Here are the payment and user models:
class Payment < ActiveRecord::Base
belongs_to :payee, :class_name => 'User', :foreign_key => 'payee_id'
belongs_to :payer, :class_name => 'User', :foreign_key => 'payer_id'
end
class User < ActiveRecord::Base
has_many :payments
end
And the create action in the payment controller:
def create
#user = User.find_or_create_by(user_params)
#payment = #user.payments.create(payment_params)
end
The polymorphic associations are confusing me. How can I correct this error?
This is not a polymorphic association. You have to define relations correctly to get the payments you need to get. From the code i understand that this is kind of loan application.
The user in the above model does not have a single type of payment. There are two types of payments, the ones user gets money for, lets say incoming_payments and the one which user has to give money outgoing_payments. The relation should be defined like
class Payment < ActiveRecord::Base
belongs_to :payee, :class_name => 'User', :foreign_key => 'payee_id'
belongs_to :payer, :class_name => 'User', :foreign_key => 'payer_id'
end
class User < ActiveRecord::Base
has_many :incoming_payments, :class_name => 'Payment', :foreign_key => 'payee_id'
has_many :outgoing_payments, :class_name => 'Payment', :foreign_key => 'payer_id'
end
So if the user you are querying payments for is a payer then you should call user.outgoing_payements and if the user is a lender then you should call user.incoming_payments
Related
I have a User model, a Listing model and an Order model. A user can either place an order or publish a listing which others can place an order for. Thus, a User can be customer as well as supplier.
My Order model has listing_id, from_id and to_id.
My question is, how can I set up associations between these models ? I read the rails guide on associations but the example there were dealing with separate customer and supplier models.
class User < ActiveRecord::Base
has_many :listings, :foreign_key => :supplier_id, :inverse_of => :supplier
has_many :orders, :foreign_key => :customer_id, :inverse_of => :customer
end
class Listing < ActiveRecord::Base
belongs_to :supplier, :class_name => 'User'
belongs_to :order
end
class Order < ActiveRecord::Base
belongs_to :customer, :class_name => 'User'
has_many :listings
end
Here is what I'm trying to do:
class Cashflow < ActiveRecord::Base
belongs_to from_account, :class_name => 'Account'
belongs_to to_account, :class_name => 'Account'
end
class Account < ActiveRecord::Base
has_many :cashflows
end
where Account::cashflows is obviously a list of all cashflows that either have the account_id stored in from_account or in to_account.
I'm confused. What is the proper way of handling such a case? How bad design is this? What would be the proper way of designing such a relation?
I think you have the right structure as there can only two accounts be involved in a particular transaction/cashflow. if you use many to many association you would need to handle the validation for not involving more or less than 2 accounts. For your current structure you can change your moidel associations to be:
class Cashflow < ActiveRecord::Base
belongs_to from_account, :class_name => 'Account', :foreign_key => :from_account
belongs_to to_account, :class_name => 'Account', :foreign_key => :to_account
end
class Account < ActiveRecord::Base
has_many :debits, :class_name => 'Cashflow', :foreign_key => :from_account
has_many :credits, :class_name => 'Cashflow', :foreign_key => :to_account
def cashflows
transactions = []
transactions << self.debits
transactions << self.credits
transactions.flatten!
## or may be the following commented way
# Cashflow.where('from_account = ? OR to_account = ?', self.id, self.id)
end
end
This way you can keep track of the amount debited/credited in a particular account and also get the accounts involved in a particular transaction/cashflow.
Suggestions on top of my mind
1) Your class (table) cashflows should have two columns from_account and to_account.
2) from_account and to_account should have the id of the account concerned
3) cashflows should belongs_to :account
4) account should has_many :cashflows. Ideally it should be cash_flows
These should be good starting points. Don't they meet your requirements?
I think you should use has and belongs to many association here:
class Account < ActiveRecord::Base
has_and_belongs_to_many :incoming_cashflows, :class_name => 'Cashflow', :join_table => :incoming_cashflows_accounts
has_and_belongs_to_many :outcoming_cashflows, :class_name => 'Cashflow', :join_table => :outcoming_cashflows_accounts
end
class Cashflow < ActiveRecord::Base
has_and_belongs_to_many :from_accounts, :class_name => 'Account', :join_table => :incoming_cashflows_accounts
has_and_belongs_to_many :to_accounts, :class_name => 'Account', :join_table => :outcoming_cashflows_accounts
end
Also you will need some validation code allows to add only one account to Cashflow.
Im having a problem in rails. Im actually solving it but I guess theres a easier way out there.
I got user/membership/group models and user/invitation/event models. Membership joins user and group. Invitation joins user and event.
The membership and invitation model are equal. Group and event do have some equal some different columns. The membership/invitation model both have a boolean column "accepted", meaning the user which is invited to a group/event has to accept this invitation before he is a member/participant.
Now if a user signs in all group and event invitations should appear in a list. In fact I want to add more notifications to the system later on and events aren't even included in mine yet.
My solution is to add a notification model which belongs to user. So every user has many notifications. Additionally, this model is polymorphic and belongs to membership AND invitation.
#user model
class User < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
has_many :invitations, :dependent => :destroy
has_many :events, :through => invitations
has_many :notifications
#membership model (equal to invitation model)
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
has_one :notifications, :as => :noticeable
#group model (equal to event model but participants for members and invitation for membership)
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
has_many :members, :through => :memberships, :source => :user,
:conditions => ['memberships.accepted = ?', true]
#notification model
class Notification < ActiveRecord::Base
belongs_to :user
belongs_to :noticeable, :polymorphic => true
Ive added some data to the database and Im testing it in the console
myUser = User.find(6) # will be exchanged with current_user in the actual program
I will run through all notifications with each do... but for the start I test all further actions on one notification
myNotice = myUser.notifications.first
so whether the noticeable_type of myNotices is membership or invitation I will render it as group or event notification
in this case noticeable_type=membership
myGroup = Group.find(Membership.find(myNotice.noticeable_id).group_id)
--> Do you want to join the Group "myGroup.name"? Yes | No
On Yes: Membership.find(myNotice.noticeable_id).accepted = true
On No: Membership.find(myNotice.noticeable_id).destroy
And: myNotice.destroy
Thats the my idea.
Is this the way to solve the problem?
The "each do" which goes through all notifications will be in a view file. Which means "Group.find(Membership.find(myNotice.noticeable_id).group_id)" has to be in the view file or a partial aswell. Isn't that a bit ugly?
I think Ive used a lot of "find" which means many SQL queries. Isn't there a way to reduce them with any "Ruby on Rails"-magic?
Thank you :)
Needed something similar for my app, so updating the answer here in case someone finds it useful.
#user model
has_many :membership_notices, :through => :notifications, :source => :membership, :conditions => {"notifications.noticeable_type" => "membership"}
has_many :event_notices, :through => :notifications, :source => :event ,:conditions => {"notifications.noticeable_type" => "event"}
The notifications are now accessible as
user.membership_notices
user.event_notices
Finally, I found how to solve this! I bumped into this problem again and googled "reverse polymorphic". I found the following post:
https://gist.github.com/runemadsen/1242485
Polymorphic Associations reversed.
It's pretty easy to do polymorphic associations in Rails: A Picture
can belong to either a BlogPost or an Article. But what if you need
the relationship the other way around? A Picture, a Text and a Video
can belong to an Article, and that article can find all media by
calling #article.media
This example shows how to create an ArticleElement join model that handles the polymorphic relationship. To add fields that are common to all polymorphic models, add fields to the join model.
class Article < ActiveRecord::Base
has_many :article_elements
has_many :pictures, :through => :article_elements, :source => :element, :source_type => 'Picture'
has_many :videos, :through => :article_elements, :source => :element, :source_type => 'Video'
end
class Picture < ActiveRecord::Base
has_one :article_element, :as =>:element
has_one :article, :through => :article_elements
end
class Video < ActiveRecord::Base
has_one :article_element, :as =>:element
has_one :article, :through => :article_elements
end
class ArticleElement < ActiveRecord::Base
belongs_to :article
belongs_to :element, :polymorphic => true
end
t = Article.new
t.article_elements # []
p = Picture.new
t.article_elements.create(:element => p)
t.article_elements # [<ArticleElement id: 1, article_id: 1, element_id: 1, element_type: "Picture", created_at: "2011-09-26 18:26:45", updated_at: "2011-09-26 18:26:45">]
t.pictures # [#<Picture id: 1, created_at: "2011-09-26 18:26:45", updated_at: "2011-09-26 18:26:45">]
I'm trying to create some sort of many-to-one association. The basic premise is a money/transaction flow system to keep track of money between a user's two accounts (perhaps between a wallet and a checking account).
I have a Transaction model, which stores the basic information - what account to debit from, what account to credit to, what the amount is.
I also have an Account model, which the user can create multiple ones of (maybe one for Wallet, one for Credit Card, one for Checkings Account, etc).
The problem I think I'm running into is that my Transaction model references the Account model twice, once for a credit_id and once for a debit_id.
I'm trying to figure out the association I need, and I think I need a many-to-one (many transactions, one account). I don't think I need a join table, but I'm not entirely sure.
Here's my basic model code, I'm really not sure where to go from here.
class Transaction < ActiveRecord::Base
attr_accessible :amount, :description, :credit_id, :debit_id
belongs_to :user
belongs_to :debit, :class_name => "Account"
belongs_to :credit, :class_name => "Account"
end
And then for Account model:
class Account < ActiveRecord::Base
attr_accessible :name, :credit_transactions, :debit_transactions
belongs_to :user
has_many :transactions
has_many :credit_transactions, :through => :transactions, :source => :credit
has_many :debit_transactions, :through => :transactions, :source => :debit
end
With this model implementation, I can get transaction.credit and transaction.debit correctly, but when I try do something like account.credit_transactions, I get this error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: transactions.account_id: SELECT "accounts".* FROM "accounts" INNER JOIN "transactions" ON "accounts".id = "transactions".debit_id WHERE (("transactions".account_id = 3))
I'm honestly kinda stuck about where to go next, so any help would be appreciated. Thanks!
[edit: updated model codes]
By the sounds of it your transaction has one credit account and one debit account?
class Transaction < ActiveRecord::Base
has_one :credit_account, :class_name => "Account", :foreign_key => "credit_account_id"
has_one :debit_account, :class_name => "Account", :foreign_key => "debit_account_id"
end
IF that doesn't work, could you give a little more info on how the relationships between your models should work?
Sorry for the delay, but finally figured it out. Here's my code
class Transaction < ActiveRecord::Base
attr_accessible :amount, :description, :long_description, :credited_id, :debitted_id, :custom_credit, :custom_debit
belongs_to :user
belongs_to :debitted, :class_name => "Account"
belongs_to :credited, :class_name => "Account"
and for account
class Account < ActiveRecord::Base
attr_accessible :name, :debit_shorthand, :credit_shorthand, :credit_transactions, :debit_transactions
belongs_to :user
has_many :credit_transactions, :class_name => "Transaction", :foreign_key => 'credited_id'
has_many :debit_transactions, :class_name => "Transaction", :foreign_key => 'debitted_id'
Thanks all who helped!
I have User.rb:
has_many :sent_messages, :class_name => "Message", :foreign_key => "sent_messageable_id"
Each Message.rb has an account_id and a :givereceive method:
belongs_to :account
I want to create a method for the Account.rb model so that I can show all the Users (uniquetly) who sent a message where the account_id is the same as that account and :givereceive = "Give"
I tried the following:
User.joins(:sent_messages).where(
{:sent_messages => [:account_id => self.account_id,
:givereceive => "Give"]})
But I get an error that there is no association with Messages.
Also, this wouldn't remove duplicate instances (for example, the same user created several messages with the same Account).
I am testing it in the console and have the metawhere gem.
Thanks.
Try changing joins(:messages) to joins(:sent_messages)
User.joins(:sent_messages).where(
{:sent_messages => [:account_id => self.account_id,
:givereceive => "Give"]})
Add these associations to your models:
class Message < ActiveRecord::Base
belongs_to :account
belongs_to :user, :foreign_key => "sent_messageable_id"
end
class Account < ActiveRecord::Base
has_many :give_messages, :class_name => "Message", :conditions => {:givereceive => "Give"}
has_many :users, :through => :give_messages
end
Now to get the users you are looking for just call #account.users.