Avoid creating duplicate objects from subclasses of a given model in Rails - ruby

I have a model group.rb with subclasses organization.rb, company.rb, etc. I'm wondering if there is a way to create an organization with the name "Rails Beginners Society" without also creating a company with the name "Rails Beginners Society"? As it stands right now it looks like if I do something like Organization.find_or_create_by_name(:name => #profile.organization) I end up not only creating an organization, but also a company and all the other subclasses of group.rb with the name I supply in my Controller.
Any ideas/guidance would be much appreciated!
My Models look like this:
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end
class Organization < Group
end
Etc...

It sounds like what you're trying to do is STI (Single Table Inheritance). Rails does support this.
class Group < ActiveRecord::Base
end
class Organization < Group
end
etc...
If you have a column type in your groups table, Rails should handle everything for you.

Related

One to many Rails relationship. Is has_many required (Rails 5)?

I'm trying to generate a one-to-many relationship between users and advice. One user can have many advices.
rails g model User
rails g model Advice user:references
I noticed that this doesn't automatically generate the line has_many :advices in the user.rb file. However, in the advice.rb file, the line belongs_to :user was auto-generated.
In the rails console, I am still able to create multiple Advices for one user without an error.
My question is, is the has_many :advices line necessary in the user.rb file? If not, then why do the Rails guides recommend it?
The has_many association tells Rails that the objects are related and adds methods to the object for querying the associated objects. You could live without it, but it makes things easier.
See the first chapter here:
http://guides.rubyonrails.org/v2.3.11/association_basics.html
Another reference:
https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many
When you use one-to-many associations you are telling your User model that he has zero or more instances of the model Advice, while Advice belongs to only one model User and its reference to it.
This is how your models should be:
class User < ApplicationRecord
has_many :advices
end
class Advice < ApplicationRecord
belongs_to :user
end
Even if you don't need to declare it right now, you might need it in a long run, so it is always better to have it set. You can read more about this topic here.

implementing Active Record Guide 2.4 The has_many :through Association

I'm trying to implement the example in http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
But I'm not sure what the proper way is? Do you create migrations first and then apply the has many through in the model by changing the model?
hospital$ rails g model Physician name:string
invoke active_record
create db/migrate/20140807183053_create_physicians.rb
create app/models/physician.rb
invoke test_unit
create test/models/physician_test.rb
create test/fixtures/physicians.yml
hospital$ rails g model Patient name:string
invoke active_record
create db/migrate/20140807183112_create_patients.rb
create app/models/patient.rb
invoke test_unit
hospital$ rails g model Appointment physician:references patient:references appointment_date:datetime
invoke active_record
create db/migrate/20140807183152_create_appointments.rb
create app/models/appointment.rb
invoke test_unit
create test/models/appointment_test.rb
create test/fixtures/appointments.yml
These are the models generated:
class Appointment < ActiveRecord::Base
belongs_to :physician
belongs_to :patient
end
class Patient < ActiveRecord::Base
end
class Physician < ActiveRecord::Base
end
is there a step by step guide on implementing a has many through model? The guide explains the idea, I understand it but I'm not sure about the correct way to actually implement it or if it even matters? I'm confused
Well, the order in which you write the migration or the code of your app doesn't matter. But your code will only work once you run the migration with rake db:migrate.
In your posted code, you're obviously missing the has_many methods. Maybe you like this quick tip about what is and how to build has_many through associations
Edit:
You absolutely can manually put the has_many in it. The generator makes it easier though. But if you're confuse, I suggest you to write the code manually and run the migration after.

Multiple controllers, two "depths" of relationships

I'm writing my first app in Ruby on Rails (I've only went through railstutorial.org before ) which is simple Electronic Prescription Service and I run on one thing I can't cope with.
I want to create form for adding new prescription as well as adding some medicines which belong to this newly created prescription.
First problem is - how can I actually add medicines to the prescription when during filling the form prescription doesn't exist yet? What I did before was I first create prescription with #user.prescription.create(presc_attributes) and later #prescription.relations.create(medicine_id).
Now I need to do this probably on one form sending the whole thing using one button ( one HTML request, am I right? ) unless you guys have got better idea. I'm also struggling with an idea where to actually put method creating this prescription. Should it be in PrescriptionController#new or maybe connected to RelationsController#new as well?
I've read couple of articles about nested fields_for but they don't seem to match exactly what I need.
Im really new to RoR so sorry if I missed something important.
Here is my EER as well
http://i.stack.imgur.com/sa9CB.png
UPDATE---
ahhh i see, I think what you want is a relationship with an inverse_of.
If you are using a belongs_to on the join model, it is a good idea to set the :inverse_of option on the belongs_to, which will mean that the following example works correctly (where tags is a has_many :through association):
#post = Post.first
#tag = #post.tags.build name: "ruby"
#tag.save
The last line ought to save the through record (a Taggable). This will only work if the :inverse_of is set:
class Taggable < ActiveRecord::Base
belongs_to :post
belongs_to :tag, inverse_of: :taggings
end
If you do not set the :inverse_of record, the association will do its best to match itself up with the correct inverse. Automatic inverse detection only works on has_many, has_one, and belongs_to associations.
FROM http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Rails 3: How do I work with methods on a related object?

I'm updating a site with Rails 3.1 and adding some features. This application tracks tests for a collection of sites. Originally I was storing the grade for each test as a column in the Tests table but now I have created a new model called Reviews to keep a history of grades and comments assigned each test. The models look like this:
class Site < ActiveRecord::Base
has_many :tests
has_many :reviews, :through => :tests
class Test < ActiveRecord::Base
has_many :reviews
belongs_to :site
class Review < ActiveRecord::Base
belongs_to :test
has_one :user
Previously I could do something like this in the Site model to get some reporting data on grades:
def total_a_grades
tests.count(:conditions => "reviewer_grade = 4 and review_status = 3")
end
Since the grades are now out of the Test model I created this method to access the most recent grade for each test:
def last_grade
return reviews.find(:last).grade
end
This works fine for displaying the most recent grade in the test view, but how can I use it at the site level? Since the value is now in a method that's not stored in the database, I don't know how to work with it and can't find anything related to this at the major online tutorial/screencast sites.
Thanks in advance for your help!
The joins method allows access to the has_many :through association's attributes. Or start with the desired association, and the required joins are included for you.
tests.joins(:reviews).where("reviews.grade = 5").count
reviews.where("grade = 5").count
sources:
Active Record Query Interface at Rails Guides, section 11 Joining Tables
Railscasts 181: Include vs. Joins. Some of the methods described have changed; check out Railscasts 202.
Railscasts 215: Advanced Queries in Rails 3. Includes some examples of associations and joins.

Filtering child objects in a has_many :through relationship in Rails 3

Greetings,
I have an application where Companies and Users need to belong to each other through a CompanyMembership model, which contains extra information about the membership (specifically, whether or not the User is an admin of the company, via a boolean value admin). A simple version of the code:
class CompanyMembership < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
class Company < ActiveRecord::Base
has_many :company_memberships
has_many :users, :through => :company_memberships
end
class User < ActiveRecord::Base
has_many :company_memberships
has_many :companies, :through => :company_memberships
end
Of course, this makes it simple to get all the members of a company via company.users.all, et al. However, I am trying to get a list of all Users in a Company who are admins of that Company (and also to test whether a user is an admin of a given company). My first solution was the following in company.rb:
def admins
company_memberships.where(:admin => true).collect do |membership|
membership.user
end
end
def is_admin?(user)
admins.include? user
end
While this works, something feels inefficient about it (it's iterating over each membership, executing SQL each time, right? Or is Relation smarter than that?), and I'm not sure if there's a better way to go about this (perhaps using scopes or the fancy new Relation objects that Rails 3 uses?).
Any advice on the best way to procede (preferably using Rails 3 best practices) would be greatly appreciated!
I believe I was going about this the wrong way, specifying conditions on company_memberships instead of users, which was what I actually wanted (a list of Users, not a list of CompanyMemberships). The solution I think I was looking for is:
users.where(:company_memberships => {:admin => true})
which generates the following SQL (for company with ID of 1):
SELECT "users".* FROM "users"
INNER JOIN "company_memberships"
ON "users".id = "company_memberships".user_id
WHERE (("company_memberships".company_id = 1))
AND ("company_memberships"."admin" = 't')
I'm not sure yet if I'll need it, but the includes() method will perform eager loading to keep down the number of SQL queries if necessary:
Active Record lets you specify in
advance all the associations that are
going to be loaded. This is possible
by specifying the includes method of
the Model.find call. With includes,
Active Record ensures that all of the
specified associations are loaded
using the minimum possible number of
queries.queries. RoR Guides: ActiveRecord Querying
(I'm still open to any suggestions from anyone who thinks this isn't the best/most effective/right way to go about this.)
An even cleaner way would be to add an association to your Company model, something like this:
has_many :admins, :through => :company_memberships, :class_name => :user, :conditions => {:admin => true}
You might have to dig into the rails doc to get the exact syntax right.
You shouldn't need :include, unless you have other classes associated with :user that you might reference in your view.
How about something like this:
Company.find(:id).company_memberships.where(:admin => true).joins(:user)
I stumbled across this answer and believe that nowadays there is a nicer way using has_many association scopes (has_many documentation):
has_many :admins, -> { where(admin: true) }, through: :company_memberships, class_name: :user
The second parameter of a has_many association can be a proc or lambda that contains your filter.
I made the activerecord_where_assoc gem to do this.
With it, no need for joins or includes. Doing eager loading remains your choice, not an obligation.
users.where_assoc_exists(:company_memberships, admin: true, company_id: #company.id)
The question is unclear on how to filter the company, so I added it myself. Without this, if a user can be in multiple company, it being admin in one company could mean being treated as an admin in another company.
The gem doesn't work in Rails 3, but we are 9 years later and Rails 4.1 and more are supported.
Here are the introduction and examples. Read more details in the documentation.

Resources