Using Ruby 1.9.3 and ActiveRecord 3.2.6.
I'm having an issue when trying to compare an ActiveRecord Object that has attr_accessible :property set on it, that is contained in an Array of associated Objects using include?(object).
These are my 2 ActiveRecord models, Account and Role.
Account:
class Account < ActiveRecord::Base
# Associations
#
has_many :role_assignments, :dependent => :destroy
has_many :roles, :through => :role_assignments
end
Role:
class Role < ActiveRecord::Base
attr_accessible :title
# Associations
#
has_many :role_assignments, :dependent => :destroy
has_many :accounts, :through => :role_assignments
end
If I then create a couple of Roles (say "Admin" and "Editor") and assign the "Admin" one to an Account, I would assume this would work:
role = Role.find_by_title("Admin")
account = Account.first # => The Account we assigned the "Admin" role to
account.roles.include?(role) # => Should be true but returns false
But this actually returns false!
If I remove the 'attr_accessible :title' from the Role model and repeat the above then it does return true.
So I guess my question is... why would attr_accessible cause this particular issue? or is it a case that I have to do my check to see if role does exist in account.roles a different way?
You could try that
account.role_ids.include?(role.id)
Related
I have three tables, server, domain, site, I'm trying to make it so it works like this:
You create a site, then a primary domain and aliases and assign the primary domain to a site (by selecting a site via drop down box within domain page).
Then in the servers, you select a site and it auto assigns all domains for that site (including aliases) to that server.
The Domains table is also used as a through table for both the site and server tables
However at the moment, when you select a site and then save it it auto-generates the relation rather then using the one's that are already there.
Also for some reason activeRecord won't let me assign the domains to the server either, no matter how I try it:
server.domains = domains
server.domains_id = domains.map(:&id)
server[:domains_id] = domains.map(:&id)
server.assign_attribute(:domains, domains)
server.assign_attribute(:domains_id, domains.map(:&id)
domains_id on the server remains nil despite not erroring (it saves successfully, just domains_id is nil)
Ultimately, what I would like to know is how to manually manage the join table and also why won't rails let me assign values to the attribute?
server.rb:
class Server < ApplicationRecord
before_save :set_domains#, if: :sites_id_changed?
has_many :domains, dependent: :nullify
has_many :sites, through: :domains
has_many :projects, through: :sites
has_many :clients, through: :projects
def set_domains
domains = get_domains Site.where(id: self.site_ids).all
domains += get_domains domains,:domains
domains.each do |domain|
domain.server = self
end
self.domains = domains
end
private
def get_domains(objects,meth=:domain)
objects.first.blank? ? [] : objects.map(&meth).flatten
end
end
domain.rb:
class Domain < ApplicationRecord
alias_attribute :aliases, :domains
alias_attribute :alias_of, :domain
has_many :domains, dependent: :nullify
belongs_to :domain, optional: true
belongs_to :site, optional: true
belongs_to :server, optional: true
def alias?
!self.alias_of.blank?
end
accepts_nested_attributes_for :domains, allow_destroy: true
end
site.rb:
class Site < ApplicationRecord
enum environment: %i{development staging production}
before_save :set_server
belongs_to :project, optional: true
belongs_to :client, optional: true
has_one :domain, dependent: :nullify
has_many :servers, through: :domain
def set_server
self.server = self.domain.server
end
end
In my model an Item is created by a User and can be purchased by many Users, and a User can purchase many Items.
User, Item, and Purchase are defined, using AcvtiveRecord with superfluous details snipped for brevity as follows:
class User < ActiveRecord::Base
# various other fields
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
class Purchase < ActiveRecord::Base
belongs_to :item
belongs_to :user
# various other fields
end
and an rspec test also snipped as follows:
describe "user purchasing" do
it "should allow a user to purchase an item" do
a_purchase = Purchase.create!(:item => #item, # set up in `before :each`
:user => #user # set up in `before :each`
)
a_purchase.should_not eq(nil) # passes
#item.buyers.should include #user # fails
#user.purchased_items.should include #item # fails
end
end
This results in
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #item.buyers.should include #user
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model Item
Likewise if I swap around #file_item.buyers.should include #user and #user.purchased_items.should include #item I get the equivalent
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #user.purchased_items.should include #item
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model User
My migration looks like
create_table :users do |t|
# various fields
end
create_table :items do |t|
t.integer :creator_id # file belongs_to creator, user has_many items
# various fields
end
create_table :purchases do |t|
t.integer :user_id
t.integer :item_id
# various fields
end
What have I done wrong?
You have to specify the following.
class User < ActiveRecord::Base
has_many :purchases
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
has_many :purchases
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
Only when you specify
has_many :purchases
the model will be able to identify the association.
What's the simplest railsy way to validate an attribute of an associated model?
Item
belongs_to :user
validates_presence_of :user
# AND the "is_photographer" column for that user must be true
User
has_many :items
# can be a regular user or a photographer
validate :user_is_photographer, :if => :user
def user_is_photographer
errors.add(:user, "should be a photographer") unless user.is_photographer
end
Update: This was all due to a stupid error: previously, I had defined a method with the same name as one of the methods ActiveRecord creates, which was masking the proper behaviour and breaking everything. I can't answer/close the question for a few more hours, apologies to anyone who looked into this!
I have an infuriating problem with a has_many, :through => relationship in my Rails 3.1 app.
It is infuriating because as far as I can see it is identical to two similar relationships which both work.
The owner of these relationships declares them like this:
has_many :user_skills, :dependent => :destroy
has_many :skills, :through => :user_skills
has_many :user_roles, :dependent => :destroy
has_many :roles, :through => :user_roles
has_many :conversation_users
has_many :conversations, :through => :conversation_users
(I am aware I have not followed standard nomenclature for join tables here - I only read about the convention of both-plural, names-alphabetical after setting this up, and I will refactor later)
The first two pairs of relationships (skills and roles) work just fine.
The final relationship (conversations) does not work fully. user.conversation_users returns the expected array, but user.conversations returns nil. Not an empty array, nil.
I may well be doing something stupid here, so I would be very grateful to anyone who can spot something wrong with the ConversationUser or Conversation models below.
conversation_user.rb
class ConversationUser < ActiveRecord::Base
belongs_to :user, :inverse_of => :conversation_users
belongs_to :conversation, :inverse_of => :conversation_users
validates_presence_of :user
validates_presence_of :conversation
end
conversation.rb
class Conversation < ActiveRecord::Base
has_many :messages, :dependent => :destroy
has_many :conversation_users, :dependent => :destroy
has_many :users, :through => :conversation_users
validates_presence_of :unique_id
end
(I am also aware that these are not really complex enough to justify has_many, :through => over has_and_belongs_to_many, but planned additional functionality will require join models.)
Answering to close question:
This was all due to a stupid error: previously, I had defined a method with the same name as one of the methods ActiveRecord creates, which was masking the proper behaviour and breaking everything.
I have these tables and relationships:
user has_many projects
project has_many tasks
task has_many actions
I would like to build a scope that allows me to select all of the current users actions, regardless of what project or task they belong to.
Thanks
I don't think scopes are necessary for this if you use the nested_has_many_through plugin.
class User < ActiveRecord::Base
has_many :projects
has_many :tasks, :through => :projects
has_many :actions, :through => :tasks
end
class Project < ActiveRecord::Base
has_many :tasks
has_many :actions, :through => :tasks
end
User.first.actions
I found something that works.
In the Actions model:
def self.owned_by (user)
joins("join tasks on actions.task_id = tasks.id").
joins("join projects on tasks.list_id = projects.id").
where("projects.user_id = ?" , user.id)
end
From the console:
u=User.find(1)
Action.owned_by(u).count
=> 521 # which is correct
I'm mot sure if its the best way to do it as I'm a bit new to sql. I get the feeling it could be made more concise.
EDIT Slightly better
Action.joins(:task => [{:project => :user }]).where(:projects => {:user_id => user.id })