Add model atribute to result of a query - ruby-on-rails

I have three models and they look like (simplified):
class Airline < ActiveRecord::Base
attr_accessible :name
has_many :airplanes
has_many :airplane_switches
end
class Airplane < ActiveRecord::Base
attr_accessible :airline_id, :register
belongs_to :airline
has_many :airplane_switches
end
class AirplaneSwitch < ActiveRecord::Base
attr_accessible :airline_id, :airplane_id
belongs_to :airplane
belongs_to :airline
end
Airplanes could have been in some Airlines, so I needed another model that indicates if an Airplane was in one or more Airlines.
I am building a form to let users upload some info about an Airplane, they just select the airplane register (callsign) and then they will get a list to choose in which Airline it was.
This will work over an AJAX request. But, I am trying to figure out how to show the Airline name from my controller, to avoid another AJAX call by fetching another JSON file just to get the name of the Airline based on the airline_id in AirplaneSwitch.
#airplane = Airplane.find_by_register(params[:register])
#airplane_switches = #airplane.airplane_switches # Here I need to join also each Airline.name
I think this way would be more efficient, but I have no idea if it's possible to do.

This should work:
#airplane.airplane_switches.select('*, airlines.name as airline_name').joins(:airline)
Let's say you have variable airplane_switch that contains AirlineSwitch instance fetched in that way. All you need to do to get your airline name is:
airplane_switch.airline_name

Related

Rails 4 Associations: Help setting up database

I need some assistance with my Rails 4 associations. I have the following 4 models:
class User < ActiveRecord::Base
has_many :check_ins
has_many :weigh_ins, :through => :check_ins
has_many :repositionings, :through => :check_ins
end
class CheckIn < ActiveRecord::Base
belongs_to :user
has_one :weigh_in
has_one :repositioning
end
class Repositioning < ActiveRecord::Base
# belongs_to :user
belongs_to :check_in
end
class WeighIn < ActiveRecord::Base
# belongs_to :user
belongs_to :check_in
end
Question: If I am setup this way, how would I input repositionings and weigh_ins separately, but still have them linked through a single check in?
You would have to retain one of the other association's ID in order to make it work.
For example, let's say:
You have created a CheckIn.
You now add a Repositioning to that check in.
Store the ID of the repositioning object
When adding your WeighIn object, you would simply reference the correct CheckIn record: correct_checkin_record = CheckIn.where(repositioning: the_repositioning_id)
You can then add the WeighIn object to that particular record.
An alternative (and simpler) method would be to access the CheckIn directly through the User: correct_checkin_record = #user.checkin -- This would pull in the correct CheckIn every time.
I've included both options to help visualize exactly what is going on in the relation.
Do you want to have users input weigh_ins and repositionings on different pages?
Having weigh_ins and repositionings inputted separately but still be part of a single checkin is fine with that setup. Its just matter of getting the same check_in object and make the associations to that object, which can be done through the controller by passing in check_in ID params and do CheckIn.find(params[:id])

rails association limit record based on an attribute

As you can see in the schema below, a user can create courses and submit a time and a video (like youtube) for it, via course_completion.
What I would like to do is to limit to 1 a course completion for a user, a given course and based one the attribute "pov" (point of view)
For instance for the course "high altitude race" a user can only have one course_completion with pov=true and/or one with pov=false
That mean when creating course completion I have to check if it already exist or not, and when updating I have to check it also and destroy the previous record (or update it).
I don't know if I'm clear enough on what I want to do, it may be because I have no idea how to do it properly with rails 4 (unless using tons of lines of codes avec useless checks).
I was thinking of putting everything in only one course_completion (normal_time, pov_time, normal_video, pov_video) but I don't really like the idea :/
Can someone help me on this ?
Thanks for any help !
Here are my classes:
class CourseCompletion < ActiveRecord::Base
belongs_to :course
belongs_to :user
belongs_to :video_info
# attribute pov
# attribute time
end
class Course < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :courses
has_many :course_completions
end
You could use validates uniqueness with scoping Rails - Validations .
class CourseCompletion < ActiveRecord::Base
belongs_to :course
belongs_to :user
belongs_to :video_info
validates :course, uniqueness: { scope: :pov, message: "only one course per pov" }
# attribute pov
# attribute time
end

Modeling a Subscription in Rails

So, after thinking on this for a while, I have no idea what the proper way to model this is.
I have a website focused on sharing images. In order to make the users happy, I want them to be able to subscribe to many different collections of images.
So far, there's two types of collections. One is a "creator" relationship, which defines people who worked on a specific image. That looks like this:
class Image < ActiveRecord::Base
has_many :creations
has_and_belongs_to_many :locations
has_many :creators, through: :creations
end
class Creator < ActiveRecord::Base
has_many :images, ->{uniq}, through: :creations
has_many :creations
belongs_to :user
end
class Creation < ActiveRecord::Base
belongs_to :image
belongs_to :creator
end
Users may also tag an image with a subjective tag, which is something not objectively present in the image. Typical subjective tags would include "funny" or "sad," that kind of stuff. That's implemented like this:
class SubjectiveTag < ActiveRecord::Base
# Has a "name" field. The reason why the names of tags are a first-class DB model
# is so we can display how many times a given image has been tagged with a specific tag
end
class SubjectiveCollection < ActiveRecord::Base
# Basically, "User X tagged image Y with tag Z"
belongs_to :subjective_tag
belongs_to :user
has_many :images, through: :subjective_collection_member
end
class SubjectiveCollectionMember < ActiveRecord::Base
belongs_to :subjective_collection
belongs_to :image
end
I want users to be able to subscribe to both Creators and SubjectiveTags, and to display all images in those collections, sequentially, on the home page when they log in.
What is the best way to do this? Should I have a bunch of different subscription types - for example, one called SubjectiveTagSubscription and one called CreatorSubscription? If I do go this route, what is the most efficient way to retrieve all images in each collection?
What you want to use is a Polymorphic Association.
In your case, it would look like this:
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :subscribeable, polymorphic: true
end
The subscriptions table would need to include the following fields:
user_id (integer)
subscribeable_id (integer)
subscribeable_type (string)
This setup will allow a subscription to refer to an instance of any other model, as ActiveRecord will use the subscribeable_type field to record the class name of the thing being subscribed to.
To produce a list of images for the currently logged in user, you could do this:
Subscription.where(user_id: current_user.id).map do |subscription|
subscription.subscribeable.images.all
end.flatten
If the performance implications of the above approach are intolerable (one query per subscription), you could collapse your two types of subscribeables into a single table via STI (which doesn't seem like a good idea here, as the two tables aren't very similar) or you could go back to your initial suggestion of having two different types of subscription models/tables, querying each one separately for subscriptions.

Creating new object with relations without the related id

I have a rails app with the following models:
class Product < ActiveRecord::Base
has_many :stores, through: :product_store
attr_accessible :name, :global_uuid
end
class ProductStore < ActiveRecord::Base
attr_accessible :deleted, :product_id, :store_id, :global_uuid
belongs_to :product
belongs_to :store
end
Since this model is for a REST API of a mobile app, I create the objets remotely, on the devices, and then sync with this model. As this happens, it may happen that I have to create a ProductStore before having an id set for Product. I know I could batch the API requests and find some workaround, but I've settled to have a global_uuid attribute that gets created in the mobile app and synced.
What I'd like to know is how can I make this code in my controller:
def create
#product_store = ProductStore.new(params[:product_store])
...
end
be aware that it will be receiving a product_global_uuid parameter instead of a product_id parameter and have it properly populate the model.
I figure I can override ProductStore#new but I'm not sure if there's any ramification when doing that.
Overriding .new is a dangerous business, you don't want to get involved in doing that. I would just go with:
class ProductStore < ActiveRecord::Base
attr_accessible :product_global_uuid
attr_accessor :product_global_uuid
belongs_to :product
before_validation :attach_product_using_global_uuid, on: :create
private
def attach_product_using_global_uuid
self.product = Product.find_by_global_uuid! #product_global_uuid
end
end
Having these kinds of artificial attr_accessors that are only used in model creation is kind of messy, and you want to avoid passing in anything that isn't a direct attribute of the model you are creating where possible. But as you say there are various considerations to balance, and it's not the worst thing in the world.

Rails app has trouble with inter-model saving

I'm working on an app that downloads meta tags from websites and saves then. The downloading happens in a model called Site. I'd like to save off the downloaded robots meta tags into a model called robots_tag which is connected to sites via a join table called meta_tag_sites.
But the method that I've written in the sites model to do this isn't working. When I try to call the method in the console, I get the following error.
undefined method `robots_meta=' for []:ActiveRecord::Relation
Any idea what I'm doing wrong?
class Site < ActiveRecord::Base
attr_accessible :domain
belongs_to :user
has_many :meta_tag_sites
has_many :robots_tags, through: :meta_tag_sites
accepts_nested_attributes_for :robots_tags
# ...
def download_robots_meta_tags
robots_tags = Nokogiri::HTML(Net::HTTP.get(self.domain, "/")).xpath("//meta[#name='robots']")
robots_tags.each do |tag|
self.robots_tags.robots_meta = tag
end
end
# ...
end
class RobotsTag < ActiveRecord::Base
attr_accessible :robots_meta
has_many :meta_tag_sites
has_many :sites, through: :meta_tag_sites
end
class MetaTagSite < ActiveRecord::Base
attr_accessible :site_id, :meta_tag_id
belongs_to :site
belongs_to :robots_tag
end
(BTW, this post is related to an earlier post: Web-scraping Rails App Getting Over-Modelled?).
The problem is here:
self.robots_tags.robots_meta = tag
self.robots_tags is a collection of objects defined by has_many :robots_tags, and you're attempting to assign a specific attribute to that entire collection. You can't do this. If you want to assign to an attribute on a specific object, you have to either iterate over the collection, or select a specific object from the collection via first or last or any of the other Enumerable methods.
By inspection, the offending line appears to be:
self.robots_tags.robots_meta = tag
You should iterate over self.robots_tags instead, with something like:
self.robots_tags.each do |robot_tag|
robot_tag.robots_meta = tag
end

Resources