So I'm currently working on a new version of a JSON API which needs to be backwards compatible. I have the following models set up:
class Student
# this is a student of type "version 1"
has_one :student_information
has_one :family_information
#...
end
class V2::Student < ::Student
# this is a student of type "version 2"
# which accesses the same table as the version 1
self.table_name = 'students'
end
So, all the associations are getting inherited, which is fine. But the version 2 of the student is defined the way, that family_information is being removed. In order to stay compatible with the API version I can't remove it from the Student base class, but want to remove it from the V2::Student class.
How can I achieve this? Is this even necessary? Better solutions?
You could make a V1 also just like the V2 and put the associations in V1 instead of direct in the Student class.
class Student
#...
end
class V1::Student < ::Student
# this is a student of type "version 1"
has_one :student_information
has_one :family_information
self.table_name = 'students'
end
class V2::Student < ::Student
# this is a student of type "version 2"
# which accesses the same table as the version 1
self.table_name = 'students'
end
Related
I'm working on an rails API, more specifically in a create operation.
The workflow that I have is this, I have two rails applications, one is an API and the other is an interface. The API manages the different backend operations in order to handle data, store data, and respond in json format to the interface. The interface serves as the frontend, just making http requests to the API in order to display the information.
In my API I have the 3 model listed below:
class Team < ApplicationRecord
has_many :team_users
has_many :users, through: :team_users
end
class User <ApplicationRecord
has_many :team_users
has_many :teams, through: :team_users
end
class TeamUser < ApplicationRecord
belongs_to :user
belongs_to :team
end
Basically I'm sending an array of user ids from the Interface to the API and I would like to make a query to find out if there's already a team formed by the users (user ids) that I've passed to the API.
I have already tried to do this:
Team.joins(:team_users).where('team_users.user_id' => [3,5])
The problem with this query is that it returns every team that contains the users with id that are equal to 3 or 5. The correct result would be to return a team that has the users 3 and 5 or 5 and 3 as their team members.
Thanks in advance.
Update
The original business rule is this, have an aplication that keeps track of trees, so I have a model named tree and when we create a tree whe must say what team created this tree. So I used a multi select user dropdown field with select2 js library that is how I'm passing the user_ids to the API. So the basic idea is to check is theres already a team composed only by the users passed to the API, if there is already a team I use it's id and say that the tree was registered by that team, if there insn't a team with coposed only buy the users I create a new team and reference it's Id to the tree.
You can approach the problem in different ways. Scrolling over each Team record to check if it contains the associated user_ids is pretty straightforward but inefficient:
team.user_ids.sort == user_ids.sort
But we can make it performant by reversing the process, i.e. iterating over the user_ids to find out corresponding teams, taking their intersection of Team ids and finally checking if any team_id holds those user_ids. This line will return true if there's already a team formed by the users (user ids):
user_ids.map { |id| User.find(id).team_ids }.reduce(:&).present?
You may include it in the User class scope like below:
class User < ApplicationRecord
...
def self.team_exists_for?(user_ids)
# return early
# what should it return when user_ids is empty
# return ??? if user_ids.blank?
# what should it return when any of the id is absent from :users table
# set_of_all_user_ids = Set.new(User.pluck(:id))
# set_of_user_ids = Set.new(user_ids)
# return ??? unless set_of_user_ids.subset? set_of_all_user_ids
# finally
user_ids.map { |id| User.find(id).team_ids }.reduce(:&).present?
end
...
end
Update
So you want to find the team which only has those users provided by user_ids or create a team with them and assign back to the Tree model instance. Combining both approaches described above and defining a scope in the Team model itself seems like a better solution.
class Team < ApplicationRecord
...
def self.find_or_create_for(user_ids)
# find all team_ids which contain user_ids (inclusive)
team_ids = user_ids.map { |id| User.find(id).team_ids }.reduce(:&).flatten
if team_ids.present? # search for the team which ONLY has 'user_ids'
team_id = team_ids.find { |id| Team.find(id).user_ids.sort == user_ids.sort }
end
return Team.find(team_id) if team_id
# or create a team with user_ids and return
team = Team.create! # create a team with required attributes
team.user_ids = user_ids
team
end
...
end
i have implemented this as
add a field key: string on Team and in Team model
class Team < ApplicationRecord
has_many :team_users
has_many :users, through: :team_users
#callback
before_validation :update_key
def update_key
self.key = Team.key_for_users(self.users)
end
def self.key_for_users(users)
users.sort.map(&:id).join("_")
end
end
so basically after this callback whenever you will create a team there will be a key
for example: -
users = [3,5]
then key in Team will be 3_5
or users = [5,3]
then key in Team will be 5_3
From this we can easily get the result what you wanted
example: -
user_ids = [3,5]
[14] pry(main)> user_ids_simple = user_ids.join('_')
=> "3_5"
[15] pry(main)> user_ids_reverse = user_ids.reverse.join('_')
=> "5_3"
and query will be like this: -
Team.where("key IN (?)",[user_ids_simple, user_ids_reverse])
it may be helpful for you. thanks
I've seen a lot of many-to-many associations, but it seems that the common trend is that they end up using has_many :through relationships.
Let's say you have the following:
class User < ActiveRecord::Base
has_many :user_relationships
has_many :relations, :through => :user_relationships
end
class UserRelationship < ActiveRecord::Base
belongs_to :user
belongs_to :related_user, class_name: "User"
end
Given this type of relationship you end up setting the relationship up as follows:
user1.relations << user2
user2.relations << user1
Or this:
user1.user_relationships.build(related_user_id: 2)
user2.user_relationships.build(related_user_id: 1)
Resulting in rows in the join table looking like this:
user_id | related_user_id
1 | 2
2 | 1
So that when setting up the relation such as the above, you can see that the following can be accomplished
user1.relations.include? user2 = true
user2.relations.include? user1 = true
My question is: Is there a way to accomplish the above, or at least something similar to the above in speed, in Rails WITHOUT having to create 2 rows for every single two-way relationship and maintain the ability to see the relationship from both ends in an efficient manner, reducing the space complexity of creating this relationship by half...
Apologies if this is a noobie question, but I'm new to Rails, just starting to get the hang of things. It's easy to find out how to set these up, but I find it much harder to find out how to actually implement them in an efficient manner
Here's (roughly) what I've done. I'm going to skip a ton of the detail, but am happy to share more if it's helpful. This may be heinous, so I'm curious about others' thoughts.
Essentially, I have a Relationship model something like:
module ActsAsRelatingTo
class Relationship < ActiveRecord::Base
validates :owner_id, presence: true
validates :owner_type, presence: true
validates :in_relation_to_id, presence: true
validates :in_relation_to_type, presence: true
belongs_to :owner, polymorphic: true
belongs_to :in_relation_to, polymorphic: true
acts_as_taggable
acts_as_taggable_on :roles
end
end
Then, I've created a acts_as_relating_to module (again, glossing over the details) that includes stuff like:
module ActsAsRelatingTo
def acts_as_relating_to(*classes_array)
# re-opens the class at run time so I can do things like add
# new instance methods on-the-fly
class_eval do
before_destroy :tell_to_unrelate
has_many :owned_relationships,
as: :owner,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
has_many :referencing_relationships,
as: :in_relation_to,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
# iterates through arguments passed in 'acts_as_relating_to' call
# in the Person model, below.
classes_array.each do |class_sym|
# This is a method created on-the-fly. So, when I call
# (in the Person model, below) 'acts_as_relating_to :people',
# then I get a method on each instance of 'Person' called
# 'people_that_relate_to_me. You can also create class methods
# using `define_singleton_method`.
#
# The reason for doing all of this via the define_method
# is that it lets me create any number of 'things_i_relate_to'
# methods on any class descending from ActiveRecord::Base
# (more on this in a bit). Which, if I understand it
# correctly, is how a lot of the ActiveRecord functionality gets
# into a model in the first place.
define_method(class_sym.to_s + "_that_relate_to_me") do |options={}|
... some stuff
end
# Same as above, but know I'm defining a method called
# 'people_i_relate_to'
define_method(class_sym.to_s+"_i_relate_to") do |options={}|
... some more stuff
end
# I can also create static methods and incorporate them in the
# 'Person' class. I just define them in modules (such as
# ActsAsRelatingTo::InstanceMethods and ActsAsRelatingTo::ClassMethods)
# and then either 'include' (for instance methods) or 'extend'
# (for class methods) them.
include InstanceMethods
extend ClassMethods
end
end
end
end
# Here, I'm telling ActiveRecord::Base to 'extend' this module.
# That makes the 'acts_as_relating_to' method available in
# any class that descends from ActiveRecord::Base.
ActiveRecord::Base.extend ActsAsRelatingTo
Then, I can do something like:
class Person < ActiveRecord::Base
# Here, I am calling the method that I defined above, passing in
# :people, :organizations, and :programs. This is exactly the
# sort of thing you do all the time when you say something like
# 'has_one :foo', or 'belongs_to :bar'.
acts_as_relating_to :people, :organizations, :programs
# Here, I am calling a method I have that builds on acts_as_relating_to,
# but which I did not show, that creates administrative methods on
# the person so that I can say stuff like 'person.administrate organization'.
# Or, 'organization.administrators'.
acts_as_administering :organizations, :programs
...
end
So, if I have person_1 and person_2 and I have one relationship record where the owner is person_1, and the in_relation_to is person_2, then I can say person_1.people_i_relate_to and get back person_2. Or, I can say, person_2.people_that_relate_to_me and get back person_1.
I've also go an acts_as_administering module which builds on the acts_as_relating_to module and lets me do stuff like person_1.administered_organizations and person_1.administered_programs.
I've probably rattled on too long. Anyway, if it's interesting, I can say more.
Cheers!
I have 3 models
class Company < ActiveRecord::Base
has_many : CompanyAccount
has_many : CompanyContact
end
class CompanyContact < ActiveRecord::Base
belongs_to : Company
end
class CompanyAccount < ActiveRecord::Base
belongs_to : Company
end
As both the CompanyAccount and CompanyContact models belong to the Company model, they have a similar "company_id" field. I have retrieved some Accounts through a query:
#CompanyAccounts = CompanyAccount.where.not(balance:nil)
Now, using the common company_id field I am trying to retrieve all the data from my CompanyContacts table that belong to the same Company associated with the CompanyAccounts I queried above (in other words, I am trying to get the rows which have the same company_id). I have made several attempts using "joins" but everything failed so far. Could anyone give me what would be the appropriate syntax in this context? Thanks.
First things first, please pay attention to proper Ruby naming conventions. Ruby classes and modules should use CamelCase while symbols, methods, and variables should all use snake_case. Given this, I would re-write your models as follows
class Company < ActiveRecord::Base
has_many: company_accounts
has_many: company_contacts
end
class CompanyContact < ActiveRecord::Base
belongs_to: company
end
class CompanyAccount < ActiveRecord::Base
belongs_to: company
end
Now, onto your question. I would use includes to eager load the associated company and it's associated contacts. For example
#company_accounts = CompanyAccount.includes(company: :company_contacts).where.not(balance: nil)
What this will do is for each company account with a balance, it will load it's associated company and the company's associated contacts into memory so that you can access it
#company_accounts.each do |account|
# access to the company
account.company
# access to the company's contacts
account.company.contacts
end
Hope this helps.
So I've built a new controller = "Categories_controller.rb" and a new Model = "Category.rb" and now I would normally take my Savedfriend.rb model and use it with Category.rb model like so;
<%= category.savedfriends.size %>
However this time around I keep getting;
uninitialized constant Category::Savedfriend
It's driving me crazy. I do have models all set with belongs_to.
By Rails convention, if you haven't specified your class_name on the association, it is going to look for a singularized, camelized version of the association name for the class name. If, for instance, you have a model SavedFriend, your association should be named saved_friends. If it can't find the class for the association, Rails tends to look for a scoped class within the class that's trying to call it. The error is a little obscure, but I've seen it plenty of times when I have a typo in my associations.
# in app/models/saved_friends.rb
class SavedFriend < ActiveRecord::Base
belongs_to :category
end
# in app/models/category.rb
class Category < ActiveRecord::Base
has_many :saved_friends
end
Also, if your naming scheme for files and classes is as sporadic as it is in your question, you're going to have a bad time. File names should be lowercase and underscored, class names should be a camelized version of the file name. i.e. Categories_controller.rb should be categories_controller.rb, and the class should be CategoriesController. Similarly, saved_friend.rb should contain class SavedFriend.
I need to set up a multiple class inheritance model here for the following models. Basically I'm building an extensible contacts directory. From a base class Contact I intend to derive other classes i.e something on the lines of :
class Contact
# the super class
attr_accessible :name, :about
end
class Person < Contact
attr_accessible :first_name, last_name, :description, :works_for_company_id
end
class Company < Contact
attr_accessible :company_name, :location, :services
end
Each model corresponds to a different table - I'm assuming that there would be a has_one belongs_to relation ship between teh parent and the child classes however I was wondering if theres a gem that can ease it a bit. Or if I have to do it myself how would I actually accomplish it.
For example contact.name is actually person.first_name+' '+person.last_name for a person or company.company_name for the company. How do I structure my database and associations to get this right?