Doug
Doug

Reputation: 1714

Rails - Manipulating the returned objects in an ActiveRecord query

So I have the following simplified models and associations:

class Barber < User
    has_many :barber_styles, inverse_of: :barber
end

class Style < ActiveRecord::Base
end

class BarberStyle < ActiveRecord::Base
    belongs_to :barber, inverse_of: :barber_styles
    belongs_to :style
    delegate :name, to: :style
end

If I wanted to make a query based on all BarberStyle's that belong to a specific Barber, is there a way I can include the name of the associated Style?

I want to use a line something like:

BarberStyle.joins(:barber).where(barber: 109).include(:style)

So I'd be able to access an associated style.name. But I don't think a line like this exists.

I know I could just use map to build an array of my own objects, but I have to use a similar query with many different models, and don't want to do a bunch of extra work if it's not necessary.

In one of my previous projects, I was able to render json with the following lines:

@colleges = College.includes(:sports).where(sports: { gender: "male" })
render json: @colleges, include: :sports

But I don't want to render json in this case.

Edit:

I don't really have any unincluded associations to show, but I'll try to elaborate further.

All I'm trying to do is have extra associated models/fields aggregated to each BarberStyle result within my ActiveRecord Relation query.

In an attempt to make this less confusing, here is an example of the type of data I want to pass into my JS view:

[
  #<BarberStyle 
    id: 1, 
    barber_id: 116, 
    style_id: 91,
    style: { name: "Cool Hairstyle" }
    >, 
  #<BarberStyle 
    id: 2, 
    barber_id: 97, 
    style_id: 92,
    style: { name: "Cooler Hairstyle" }
    >,
  etc...
]

The reason I want the data formatted this way is because I can't make any queries on associated models in my JS view (without an AJAX call to the server).

I had very similarly formatted data when I did render json: @colleges, include: :sports in the past. This gave me a collection of Colleges with associated Sport models aggregated to each College result. I don't want to build json in this fashion for my current situation, as it will complicate a few things. But I suppose that's a last resort.

I'm not sure if there's a way to structure an ActiveRecord query where it adds additional fields to the results, but I feel like it should, so here I am looking for it. Haven't found anything in the docs, but then again there is sooooo much left out of those.

Upvotes: 0

Views: 123

Answers (2)

Allam Matsubara
Allam Matsubara

Reputation: 529

Doug as far as I understand your code, BarberStyle must be a joining model between Barber and Style. You mentioned in your comment that you removed the has_many :styles, through: :barber_styles from your Barber model because you thought that it wasn't relevant. That's not true, it's exactly the point that would help you to achieve your goal. Add this relation again then you can do something like this:

barber = Barber.includes(:styles).where(barber: {id: 109})
barber.styles.each { |style| puts style.name }

Since barber.styles is a collection, I added a loop between all the possible styles you can have. But, from that, you can use your data as you feel like, looping through it or any other way you want.

Hope to have helped!

Upvotes: 1

davegson
davegson

Reputation: 8331

First off, in your case inverse_of does not accomplish anything, since you are setting the default values. Remove that.

Secondly, it seems you need to better understand the concept of HABTM relationships. Using has many through is generally a good idea since you can add data and logic to the model in the middle.

It ideally suits your case, so you should set it up like this:

class Barber < User
  has_many :barber_styles
  has_many :styles, through: :barber_styles
end

class Style < ActiveRecord::Base
  has_many :barber_styles
  has_many :barbers, through: :barber_styles
end

class BarberStyle < ActiveRecord::Base
  belongs_to :barber
  belongs_to :style
end

Now it is an easy task to get the styles of a given barber. Just do this:

barber = Barber.find(1)
barber.styles
# => AR::Collection[<#Style name:'Cool Hairstyle'>,<#Style name:'Cooler Hairstyle'>]

Rails automatically uses the BarberStyle model in between to find the styles of a certain barber. I assume this covers your need, if you have extra information stored only in BarberStyle, let me know.

Upvotes: 0

Related Questions