I have three tables User, User_types and Purchases.
user: id etc
user_purchase_types: id, typename, user_id
purchases: id, user_id, user_purchase_type_id, note
Users can have any number of purchases and user_types. A purchase can have one user_purchase_type.
Users can log in, create types, do purchases etc - this all works fine
However I want, when listing purchases, to see the user_purchase_types.typename, rather than the id number. Simple I think, use belongs_to, they already have the right id fields, should just work. But it doesn't
I have tried a million variations of belongs_to, has_many , has_many through etc etc but cannot get the right relationship and so show the typename rather than the id.
There are the right foreign_key id fields in the tables so this should work.
When listing the purchases in the purchase controller I use #purchase = current_user.purchases.
When looping this to display in the view I think I should be able to use purchase.user_purchase_type.typename but this gives me a 'NoMethodError'.
What am I doing wrong or should I just denormalise the DB and have done with it?
EDIT Models as req
class UserPurchaseType < ActiveRecord::Base
belongs_to :user
belongs_to :purchase
attr_accessible :typename, :id
end
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :purchases, :dependent => :destroy
has_many :user_purchase_types, :dependent => :destroy
end
class Purchase < ActiveRecord::Base
belongs_to :user
has_many :user_purchase_types
attr_accessible :date, :user_purchase_type_id, :note
end
index.erb file
<% #purchases.each do |purchase| %> #this works
<tr>
<td><%= purchase.user_purchase_type.typename %></td> #this fails
<td><%= purchase.date %></td>
<td><%= purchase.note %></td>
</tr><p>
<% end %>
purchase controller
def index
#user_purchase_types = current_user.user_purchase_types # this works
#purchases = current_user.purchases #this works
You just need to add foreign key in your existing association.
class Purchase < ActiveRecord::Base
belongs_to :user
has_many :user_purchase_types, :foreign_key => 'user_purchase_type_id'
attr_accessible :date, :user_purchase_type_id, :note
end
there is a one to one relationship between them rgt?
yes you should be able to be able to access typename, but by using #purchase.user_type.typename. and not purchase.user_type.typename.
and would be better if you could show user models too.
and is current_user defined?
also you can try by finding out the usertype first and then access it:
#user_type = UserType.find(#purchase.user_type_id)
#user_type.typename
You should always try the relationships first on console and see if your getting it rgt
Your relationships are all messed up.
You have a one to many relationships between purchases and user purchase type.
and therefore you can not use #purchase.user_purchase_type.typename
You have to use
#purchase.user_purchase_types.each do |i|
i.typename
end
class Purchase < ActiveRecord::Base
belongs_to :user
has_many :user_purchase_types
attr_accessible :date, :user_purchase_type_id, :note
end
The Purchase -> UserPurchaseType association is a has_many relationship but you seem to be trying to use it with user_purchase_type_id which would indicate a belongs_to :user_purchase_type relationship, but your association is has_many :user_purchase_types
It sounds like you may want something like:
<% #purchases.each do |purchase| %> #this works
<tr>
<td><%= purchase.user_purchase_types.collect(&:typename).join(',') %></td>
<td><%= purchase.date %></td>
<td><%= purchase.note %></td>
</tr><p>
<% end %>
to list all your UserPurchaseType typename's, comma separated.
If you do this, make sure when you load your #purchases in the controller to also include a .includes(:user_purchase_types). This will eager load the association and avoid rails loading each UserPurchaseType as you iterate through.
Related
I've got an App in which User can buy shares in a Portfolio through a wallet.cash_transaction. Now I would like to display all shareholders of the portfolio specifying the user's name and the number of shares in a given portfolio.
#models association
class User < ApplicationRecord
has_one :wallet, as: :walletable
has_many :cash_transactions, through: :wallet
end
class Portfolio < ApplicationRecord
has_one :wallet, as: :walletable
end
class Wallet < ApplicationRecord
belongs_to :walletable, polymorphic: true
has_many :cash_transactions
end
class CashTransaction < ApplicationRecord
belongs_to :wallet
belongs_to :to_wallet, class_name: 'Wallet', optional: true
end
To display shareholders I'll need below join:
> portfolio = Portfolio.last
#shareholders = User.joins(:cash_transactions).where(cash_transactions: { to_wallet: portfolio.wallet }).uniq
Which I can use in the view by below iteration:
<tbody>
<% #shareholders.each do |user| %>
<tr>
<td><%= "#{user.first_name} #{user.last_name}" %></td>
<td><%= user.cash_transactions.where(to_wallet: portfolio.wallet).sum(:shares_number) %></td>
</tr>
<% end %>
</tbody>
Line user.cash_transactions.where(to_wallet: portfolio.wallet).sum(:shares_number) is responsible for the summation of all user shares in the portfolio. This method doesn't seem very efficient to me because it seems like I'll be sum-up unnecessarily at each iteration. I imagine that when there will be hundreds of thousands of users it can be aggravating at every refresh of the page. Is there a better way to do so?
You can write SQL queries to get sum of all matched records
#shareholders = User.joins(:cash_transactions)
.where(cash_transactions: { to_wallet: portfolio.wallet})
.select("users.*, SUM(cash_transactions.shares_number) as total_shares_number")
.group("users.id")
you will get all columns of users table and sum of total shares_number, Add more fields form another table as per your requirement
I have a landlord model and in the table there is a field for listing_agent_id. There is also an agent model where all of their info is stored. In the index view I am trying to us <%= landlord.listing_agent.name but keep getting an error. I have defined agents in my landlords_controller, but it still doesn't seem to be working. Any help would be appreciated.
Landlord Index:
<tbody>
<% #landlords.each do |landlord| %>
<tr>
<td><%= landlord.listing_agent.name %></td>
</tr>
<% end %>
</tbody>
Landlords Controller:
def index
#landlords = Landlord.all
end
def new
#landlord = Landlord.new
#agents = Agent.employees.order(first_name: :asc)
end
Landlord Model:
class Landlord < ActiveRecord::Base
has_many :landlord_addresses
end
Error:
ActiveRecord does not "automagically" create an association just because you have a *_id column. There are just two many possibilities for that to be remotely useful.
To setup an association between Landlord and Agent you would do:
class Landlord < ActiveRecord::Base
belongs_to :listing_agent, class_name: 'Agent'
inverse_of: :landlord
# use inverse_of: :landlords if the relation is one to many.
end
class Agent < ActiveRecord::Base
has_one :landlord, inverse_of: :listing_agent
# or
has_many :landlords, inverse_of: :listing_agent
end
The class_name: 'Agent' option is needed because ActiveRecord cannot deduce the class from the name of the association. inverse_of helps avoid inconsistencies by keeping a single object in memory.
I have the following associations:
#models/contact.rb
class Contact < ActiveRecord::Base
has_many :contacts_teams
has_many :teams, through: :contacts
accepts_nested_attributes_for :contacts_teams, allow_destroy: true
end
#models/contacts_team.rb
class ContactsTeam < ActiveRecord::Base
belongs_to :contact
belongs_to :team
end
#models/team.rb
class Team < ActiveRecord::Base
has_many :contacts_team
has_many :contacts, through: :contacts_teams
end
A contact should always have at least one associated team (which is specified in the rich join table of contacts_teams).
If the user tried to create a contact without an associated team: a validation should be thrown. If the user tries to remove all of a contact's associated teams: a validation should be thrown.
How do I do that?
I did look at the nested attributes docs. I also looked at this article and this article which are both a bit dated.
For completion: I am using the nested_form_fields gem to dynamically add new associated teams to a contact. Here is the relevant part on the form (which works, but currently not validating that at least one team was associated to the contact):
<%= f.nested_fields_for :contacts_teams do |ff| %>
<%= ff.remove_nested_fields_link %>
<%= ff.label :team_id %>
<%= ff.collection_select(:team_id, Team.all, :id, :name) %>
<% end %>
<br>
<div><%= f.add_nested_fields_link :contacts_teams, "Add Team"%></div>
So when "Add Team" is not clicked then nothing gets passed through the params related to teams, so no contacts_team record gets created. But when "Add Team" is clicked and a team is selected and form submitted, something like this gets passed through the params:
"contacts_teams_attributes"=>{"0"=>{"team_id"=>"1"}}
This does the validations for both creating and updating a contact: making sure there is at least one associated contacts_team. There is a current edge case which leads to a poor user experience. I posted that question here. For the most part though this does the trick.
#custom validation within models/contact.rb
class Contact < ActiveRecord::Base
...
validate :at_least_one_contacts_team
private
def at_least_one_contacts_team
# when creating a new contact: making sure at least one team exists
return errors.add :base, "Must have at least one Team" unless contacts_teams.length > 0
# when updating an existing contact: Making sure that at least one team would exist
return errors.add :base, "Must have at least one Team" if contacts_teams.reject{|contacts_team| contacts_team._destroy == true}.empty?
end
end
In Rails 5 this can be done using:
validates :contacts_teams, :presence => true
If you have a Profile model nested in a User model, and you want to validate the nested model, you can write something like this: (you also need validates_presence_of because validates_associated doesn't validate the profile if the user doesn't have any associated profile)
class User < ApplicationRecord
has_one :profile
accepts_nested_attributes_for :profile
validates_presence_of :profile
validates_associated :profile
docs recommend using reject_if and passing it a proc:
accepts_nested_attributes_for :posts, reject_if: proc { |attributes| attributes['title'].blank? }
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Model Names:
1: approval
2: approval_sirs
Associations:
1: approval
has_many :approval_sirs, :foreign_key => 'approval_id', :dependent => :destroy
accepts_nested_attributes_for :approval_sirs, :allow_destroy => true
2: approval_sirs
belongs_to :approval , :foreign_key => 'approval_id'
In approvals.rb
## nested form validations
validate :mandatory_field_of_demand_report_sirs
private
def mandatory_field_of_demand_report_sirs
self.approval_sirs.each do |approval_sir|
unless approval_sir.marked_for_destruction?
errors.add(:base, "Demand Report Field are mandatory in SIRs' Detail") unless approval_sir.demand_report.present?
end
end
end
I'm trying to figure out how I can grab the current instance of an object called "meal" to create a food object that belongs to it.
It works in console... its simple and goes like this:
user = User.first
user.meal.first.meal_foods.create
A user can have many meals, and each meal can have many meal_foods.
The challenge here for me is in the create action of the foods controller in creating my meals_food.
(I'm using STI here hence the "foods" controller and the "meal_food" name)
Currently the create action looks like this:
#food = current_user.meal.meal_foods.build
I've also tried this because something pluralizing the objects name works
#food = current_user.meal.meal_food.build
Here's the error it gives for both
undefined method `meal_foods' for [#<Meal id: 17, user_id: 1, meal_name: "Meal">]:ActiveRecord::Relation
Update:
The issue I'm having here specifically is selecting the correct meal to create a meal_food for.
In console I can select the first one, which is fine. But in the foods controller, I need to select the correct meal for which to create a meal_food.
Writing meals.first would select the very first meal for that user. If I want to select the 3rd meal out of 5, I need to figure a way to grab that meal's id.
I've just tried this:
<%= link_to "new food", foods_path(id: meal.id), method: :create %>
to pass in the meal.id as a parameter that can be used in foods_controller. And then in the foods_controller I did:
#meal_food = current_user.meals.find_by_id(params[:id]).meal_foods.build
It looks like it's submitting because the page is reloaded with a success message, but the meal_food isn't created, so it just doesn't show up.
I checked in console, and there's no new food being created for this users first meal.
Ok I realized that the link_to I wrote above creates this url:
foods?id=29
and the method I'm using to retrieve this id isn't working because params[:id] is looking for a path id, not this url id.
Thanks in advance!
The Models:
class Meal < ActiveRecord::Base
before_save :sanitize
has_and_belongs_to_many :meal_foods
attr_accessible :meal_name
def sanitize
self.meal_name = "Meal"
end
end
class Food < ActiveRecord::Base
attr_accessible :brand, :carbs, :fat, :name, :protien, :type
end
class MealFood < Food
has_and_belongs_to_many :meals
end
class User < ActiveRecord::Base
has_many :meal, dependent: :destroy
has_many :custom_foods, dependent: :destroy
The controllers:
class FoodsController < ApplicationController
#functions
def create
#this is where I need to grab the correct meal, and then create a meal_food for it...
if #meal_food.save!
flash[:success] = "Food created successfully!"
redirect_to meal_path(current_user.id)
else
flash[:error] = "Food couldn't be created."
redirect_to meal_path(current_user.id)
end
end
end
Partials:
Here's the meal partial that gets repeated to display each meal. It has the link_to for creating a meal that will belong to the meal it's under.
<tr>
<thead class=meal-thead>
<td class=meal-thead-name> <%= meal.meal_name %> </td>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
<th> <%= link_to "x", meal_path(meal.id), method: :delete %> </th>
</thead>
<tbody class=meal-tbody>
<%# get reference to current meal and its foods %>
<%= render meal.meal_foods %>
<td class=remove-td-center> <%= link_to "new food", foods_path, method: :create %> </td>
</tbody>
</tr>
Couple of things:
"has_many :meal, dependent: :destroy" should be "has_many :meals, dependent: :destroy"
has_many association names are pluralized. If you want only one here, use has_one instead.
this line is wrong: #food = current_user.meal.meal_food.build, as meal is a has_many, so you need a "first". Also, it doesn't return a food, but a meal_food, so you should say this, otherwise you are confusing yourself:
#meal_food = current_user.meal.first.meal_food.build
It occurs to me that for MealFood => a better name would be Serving. Then your meal can have_many servings, and each serving can have a food. Not a mistake, just more understandable. Then you'd have the following code. Note that I didn't see the point in the STI for foods - it makes much more sense to simply put the amount of the food eaten in the servings table, then you don't need the STI at all.
class Meal < ActiveRecord::Base
before_save :sanitize
has_many :servings
has_many :foods, :through => :servings
attr_accessible :meal_name
def sanitize
self.meal_name = "Meal"
end
end
class Food < ActiveRecord::Base
attr_accessible :brand, :carbs, :fat, :name, :protein, :type
has_many :servings, :inverse_of => :food
end
class Serving < ActiveRecord::Base
attr_accessible :amount
has_many :foods
has_many :meals
end
class User < ActiveRecord::Base
has_many :meals, dependent: :destroy
end
HTH
Sometimes you say User.meal.meal_foods, sometimes you say User.meal.first.meal_foods. User.meal is an array of Meals, hence from console User.meal.first.meal_foods works, and in your code User.meal.meal_foods gives you no method error. Also, you should use plural meals in your code and swap out has_many meal with has_many meals.
There's probably a simple answer to this, but I'm lost.
I created my first Polymorphic Association today to create an activity field.
Here's the activity.rb:
class Activity < ActiveRecord::Base
belongs_to :trackable, :polymorphic => true
end
In the database for activities, I have the columns:
id
name
trackable_id
trackable_type
created_at
updated_at
Here's the note.rb:
class Note < ActiveRecord::Base
has_many :activities, :as => :trackable
after_create :create_an_activity
def create_an_activity
self.activities.build(:name => candidate_id)
end
end
In my index.html.erb view I have:
<% #activities.each do |activity| %>
<p>activity.name</p>
<% end >
My question is:
Currently, activity.name in the view is outputting the id because I have :name => candidate_id. A note is created for a candidate. But, what I really want it to output is candidate.full_name (which is in the candidates table). However, this doesn't work because full_name is not in the notes table. It's in the candidates table. Is there any way to access that? Candidates has_many notes and a note belongs_to a candidate.
enjoyed your skill share with Vin a couple months ago!
I believe what you're looking for can be accessed by going through the parent association, by calling self -> parent -> attribute:
def create_an_activity
self.activities.create(:name => self.candidate.full_name)
end
Also correct me if i'm wrong, but unless you are calling a save later on, it seems like self.activities.create is what you are looking for instead of .build