Get all has many from an activerecord hash - ruby-on-rails

I have a model Campain which has many Media.
I do this:
Campain.all.medias
But get this error:
undefined method `medias' for #<Array:0x00000004bbaf40>
How can I get all medias from Campain.all?

The better approach is to use this code:
Campaign.includes(:media).map(&:media)
(Used English grammar, I hope you get the main idea). This will get all in two requests.
---EDIT---
If media - is has_many assosiation, indeed the return will be in form: [[...],[..]] so in that case use flaten to make it just simple array.
In the case when there needed all Mediums that are for all Camplaign use arrays group method to collect unique or just simple uniq. This approach to use Rails classes is preferable, as it is more general and configurable, for example it will apply any default scopes, that may be on Campaign.

Just to complete Dylans Post - I upgraded to Rails 3.1.1 today. My app has ~60 Models and one of these was called Media and it worked well eaven when it was bad english. So I upgraded and I think they patched the pluralizemethod. I wasnt able to call the medias actions and rails said the table 'media' doesnt exist which was the moment I realised my table was called media because I used rails 3.0.3 to create the model. I renamed the model, controller and views names, updated the routes from resources :medias to ressources :media as a quick fix and it worked again!

If you are looking for all Media that has an associated Campaign, you should query via the Media object, not the Campaign object. This will give you one array of Media objects, and only takes one query (assuming your Media object has a campaign_id [that is, Media :belongs_to :campaign]).
Media.where("campaign_id is not null").all

As far as Rails is concerned, the singular of Media is "medium" and the plural is "media", so just try media. However, all will still return an array, which you can't call media on anyway. So you probably want:
#campaigns = Campaign.all
#media = Medium.where(:campaign_id => #campaigns.collect(&:id))
or this (similar to another answer, but you need to flatten the results):
Campaign.includes(:media).map(&:media).flatten

Related

How do I associate two entries in a database that are connected through a many-to-many relationship in Rails?

How do I associate two entries in a database that are connected through a many-to-many relationship in Rails?
I'm trying to associate Users and Issues for an issue tracker. I'm using has_and_belongs_to_many, not :through. I have a :user_id and :issue_id available to me, but there doesn't seem to be User.issues.find(id) or Issue.users.find(id) available to me. I have a route post "/", to: "home#create". I'm trying to make a create method in home_controller.rb.
From the look of it you're calling the method on the User class and not an instance.
If you want to get the issues connected to a user you need to fetch the user first:
User.find(id).issues
If you want to add a record to the association you can use the shovel method or any of the methods generated by the association macro:
User.find(id).issues << Issue.find(3)
User.find(id).issues.push(Issue.find(3))
User.find(id).issue_ids = [1, 2, 3]
Besides that you have a smattering of naming issues in your schema. Use snake_case everywhere in your database schema unless you have a good reason why you want to break the conventions and feel like explicitly configuring table and foreign key names.
I would also really question if you really want to use has_and_belongs_to_many. It should only really be used if you can't foresee that you ever will need to add additional attributes to the join table or never need to query the table directly - it seems pretty unrealistic that that would be true in an issue tracker. You want has_many through: - pretty much always.
I have a route post "/", to: "home#create". I'm trying to make a
create method in home_controller.rb.
Don't throw everything into a junk drawer controller. Think about your app in terms of resources that can be CRUD:ed and create controllers that handle just that resource. You should think about what the relation between a user and an issue is in your domain and how you can model it as an actual entity in the domain logic instead of just plumbing.
Maybe all I need to do is direct you to Rails Guides: Active Record Associations.
There is neither of these
User.issues.find(id)
Issue.users.find(id)
because when you are finding an issue or user by id, you don't use the association. Instead use these:
Issues.find(id)
Users.find(id)
Since the :id is unique this will work and should be what you want.
The only time you want to query issues or users using the association will be when you have the data for the other end of the relationship.
user = User.find(user_id)
issue = user.issues.where(id: issue_id)
Since the :id field is unique, this is the same as Issues.find(id). However if you want to get a collection of a user's issues with some other data, you can put the condition for that data in the where.
You can create an issue for a user this way:
user = User.find(user_id)
issue = User.issues.create( ... )

Rails: Get all associated models from has-many association

I need to get all associated models from the other associated model, on which I want to run query first.
For example, I got Post model and Tag model. I need to get all Posts, which associated with some Tags.
There's no problem, if I have only one Tag – just call 'tag.posts', but if I have more, then one Tag – for example, I need to do somethink like:
Post.where(id: PostTag.where(tag_id: some_ids).pluck(:category_id).uniq)
I belive that Rails have a built-in solution. So, anybody knows it?
My thought is:
Post.joins(:post_tags).where('post_tags.tag_id' => some_ids).uniq
You can make it a scope for easier reuse. I don't think there is a built-in method for this situation.

Rails: eager load on fetching associations, not on find

I've got three nested models: user has many plates and plate has many fruits. I also have a current_user helper method that runs in the before filter to provide authentication. So when I get to my controller, I already have my user object. How can I load all the user's plates and fruits at once?
In other words, I'd like to do something like:
#plates = current_user.plates(include: :fruits)
How can I achieve this?
I'm using Rails 3.1.3.
You will probably want to use the provided #includes method on your relation. DO NOT USE #all unless you intend to immediately work through the records, it will immediately defeat many forms of caching.
Perhaps something like: #plates = current_user.plates.includes(:fruits)
Unfortunately, there are portions of the Rails API that are not as well documented as they should be. I would recommend checking out the following resources if you have any further questions about the Rails query interface:
Query Interface Guide
ActiveRecord::Relation Walkthrough (screencast)
The query interface is possibly the most difficult part of the Rails stack to keep up with, especially with the changes made with Rails 3.0 and 3.1.
You can do
ActiveRecord::Associations::Preloader.new([current_user], :plates => :fruit).run
To eager load associations after current_user was loased. The second argument can be anything you would normally pass to includes: a symbol, an array of symbols, a hash etc
#plates = current_user.plates.all(:include => :fruits)
should do it.

Obfuscating ids in Rails app

I'm trying to obfuscate all the ids that leave the server, i.e., ids appearing in URLs and in the HTML output.
I've written a simple Base62 lib that has the methods encode and decode. Defining—or better—overwriting the id method of an ActiveRecord to return the encoded version of the id and adjusting the controller to load the resource with the decoded params[:id] gives me the desired result. The ids now are base62 encoded in the urls and the response displays the correct resource.
Now I started to notice that subresources defined through has_many relationships aren't loading. e.g. I have a record called User that has_many Posts. Now User.find(1).posts is empty although there are posts with user_id = 1. My explanation is that ActiveRecord must be comparing the user_id of Post with the method id of User—which I've overwritten—instead of comparing with self[:id]. So basically this renders my approach useless.
What I would like to have is something like defining obfuscates_id in the model and that the rest would be taken care of, i.e., doing all the encoding/decoding at the appropriate locations and preventing ids to be returned by the server.
Is there any gem available or does somebody have a hint how to accomplish this? I bet I'm not the first trying this.
What you are describing sounds like a specialized application of a URL slug. Take a look at plugins like acts_as_sluggable or friendly_id. Also look at overriding the to_param method on your User model.
Maybe start here: Best Permalinking for Rails

Calling ActiveRecord's #relationship_ids = [1,2,3] saves immediately. Any workarounds?

I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.

Resources