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.
Related
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( ... )
I have a project that is built with a tagging model to reference three different models: artist, article, event. I have associated each model to the tagging model via has_many through:. I have two problems; both related to each other:
I have 5 "default" methods that I wish to be able to call restfully from the tagging controller/model: popular, upcoming, events, articles, and artists. Each method is designed to do exactly as it's name implies. The issue lies within these methods being a query by nature. How can I maintain a consistent schema where 5 of the many taggings models I have require a special attribute (let's call it content) that should subsequently call the appropriate method?
When calling any one of the taggings models, it will return (in it's content attribute) an array of the 3 models specified earlier. Other than adding a method within each model that contains a localized description of the model and then calling upon that type method to match another hardcoded string elsewhere, is there any alternative? I don't like how the implementation I just described requires me to hardcode values. Eek.
I'm very new to Ruby on Rails, so I apologize if this is an obvious solution. However I have spent a week looking into ways to solve this compound problem that I'm trying to solve. Any input is appreciated!
As far as i unterstand you need a polymorphic association. Because tags can be applied on different models, the clue is to treat all these models in polymorphic manner by marking them as :taggable on the association to tags.
I would just use the popular acts_as_taggable_on gem for it.
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
I have a Bill model with nested Customer model.
The Customer model has a phone number with a uniqueness validation on it.
While creating the bill I want to fetch the existing record based on the phone number or create a new one if such doesn't exist.
How should I do it in a RESTful way?
you would use the find_or_create_by method which would look something like this in your case:
fetchedRecord = Bill.find_or_create_by_phone_number(customer.phone_number)
You can look at the find_or_create or find_or_create_by methods (which are dynamically created). A little Googling should get you there the rest of the way, I think.
It doesn't seem like these answers are what you are asking.
Forget about Rails, my question would be, what's the RESTful way to create a resource that might already exist? Should you POST to the resources (list) URL, and then expect a HTTP status code of 201 if the resource was created and a 200 if it already existed?
Seems like this should be spelled out in a standard somewhere.
By the way, this is how I am handling it--with status codes.
I place mine in the the association callback before_add
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.