Updating 'through' model's attributes when creating new association - ruby-on-rails

I have three models Post, Product, Mention
Product has many Posts through Mentions
When I create new association like:
product.posts << post
I'd like corresponding Mention record to have created_at value the same as post's created_at.
How can I do this?

I would do it with a callback on the Mention model
after_create :set_created_at
def set_created_at
self.created_at = self.post.created_at
self.update
end

Related

How to access variables in model

I am trying to get access to a property contained inside my user object.
My user model has_many: posts. In the controller how would i gain access to these posts? Would i create a method in the model?
def posts
#posts = Post.find(User_id: params[:id])
end
or can i directly access the posts for the user. User.posts Since i am currently residing in the controller, is the controller aware of the currently selected model? Or do i have to pull the information again?
You can query the database for all the posts with a specific user_id, like this:
#posts = Post.where(user_id: params[:id])
Alternatively, you can find the user first and then fetch all posts associated with that user, like this:
user = User.find(params[:id])
#posts = user.posts
Assuming your id in params is the id of your user, you can use user = User.find(params[:id]) to get the user and #posts = user.posts to get all the posts of this user.
So, it is not about where you are, It is about what you are calling.
I'm sure you are familiar with relationships...
When you have relationships, it means that you can get to one relation from the other through whatever association exists between them.
If I am my father's son, then you can get me directly by checking my father's children. ( you don't necessarily have to get all children in the village first )
So, bringing all my story above together, with the association between your Post and User, you can always call user.posts (user being an instance of User) and post.user ( with post being an instance of Post)
The Ruby on Rails guides have a section on associations, which is what you want. It's here: http://guides.rubyonrails.org/association_basics.html
In a nutshell, because you have added an association in your user model to a number of post records, Rails will build a helper method in your user model called posts. You can use that to access all the posts associated with that user.
When you create a post, the post record needs to have a column called user_id. This will provide the 'physical' link between the user and post models. You can access the posts from a user like so:
user.posts each do |post|
# do something with post.content
end
To get posts that match some criteria in the posts collection you can query like this:
posts = user.posts.where(:something => 'matches criteria')
If you know there's only one post that matches the criteria, you can do this:
post = user.posts.where(:something => 'matches criteria').first
The post model also needs a belongs_to :user association. (The belongs_to will generate a helper method called user in the post model which you can then use to access the user record from the post.) For example:
user_email = post.user.email
The user record does not require a post_id column since Rails knows that user.post refers to the post table and automagically generates a query using user_id.
Anyway, the guide I linked to above will give you all the information you need and more too.

Which rails model do I put a multiple table query in

I have two tables called
Product (prodID: integer, prodName: string, userID: FK)
and
User(userID:integer,userName:string).
The user can have many products. I want to write a query that gets me all the products for userID=10. I don't however understand which model I should put this in- the user or the product model or does it not matter? Presumably the output of the model will be fed to the controller it relies on so I should put it in the model that relates to the view I want to show it in? Is this correct?
You can directly use association method, no need of writing model method for fetching user's products.
In user.rb:
has_many :products
In product.rb
belongs_to :user
and from controller
User.where('id = ?', params[:id]).first.try(:products)
So, above query will fetch products if user with given id is found.
In your controller:
#user = User.find(params[:id])
#products = User.of_products(params[:id])
If you don't want to use #user in your action then you can avoid calculating #user.
In user.rb:
has_many :products
def self.of_products(user_id)
User.includes(:products).where(id: user_id)
end
This will give you all products of #user

Ruby on rails implementing "repost" action for Post's model

I have a Userand Post model with the association one-to-many. I tried to implement a repost action, add a link has_and_belongs_to_many through a table reposts.
But I was faced with the following challenges:
1) Post to feed loaded as follows:
followed_users="SELECT followed_id FROM relationships WHERE follower_id = :user ";
replics_posts="SELECT micropost_id FROM replics_users WHERE user_id = (:user)"
reposts="SELECT post_id FROM reposts WHERE user_id = (:user)"
where("user_id IN(#{followed_users}) OR user_id= (:user) OR id IN(#{replics_posts}) OR id in (#{reposts})", user: user);
and sorted by date modified. Repost similarly sorted, from which there is a situation that is repost in the middle feed.
2) No additional effort, followers do not see reposts user.
These problems can be solved through the auxiliary array with the need to fast, but it looks ridiculous and non-optimal solution.
How can I get out of the situation?
P.S. I think the solution can be found by reference in the field "Content" in the Post model on the same field, another object. Then repost action will not need a separate table and will consist only of a new Post object with a pointer to the contents of the original post. But I do not know how to do this in Ruby on Rails.
Thank you for your help!
I corrected as follows:
1) In the Post model added a new field repost_id and reference to yourself:
has_many: reposts, class_name: "Post", foreign_key: "repost_id", dependent:: destroy;
(relation to the model User not changed)
2) Added to Post's controller method repost
def repost
orig_post=Micropost.find(params[:id]);
if(orig_post)
Micropost.create(user_id:current_user.id,
content: orig_post.content,
repost_id:orig_post.id);
respond_to do |format|
format.js
end
end
end
(Do not forget to realize meets both the route and validations creation of the post)
The result is a correct model of behavior actions repost with dependencies and correct display in the feed. But sadly, this approach involves storing duplicate data in the table Posts in the "Content" field.

How to get the scoped attributes when creating a new object through a has_many association

When creating objects through a has_many association like User.first.books.create!(...), the new book gets the user_id automatically from the association.
Is there any way to get that user_id if I call my own create method? i.e. User.first.books.own_create_method
def self.own_create_method
# how to get the user object?
end
Thanks!
To define User.first.books.own_create_method you would use:
def self.own_create_method
book = build
# something custom you want to do with book
book.save
end
self. allows you to define class methods in Ruby.
Digging into ActiveRecord new method, I found that you can call scope_attributes and you'll get a hash with all the attributes that are scoped.
def self.own_create_method
attributes = scope_attributes
# attributes["user_id"] would be the user_id that is scoped by
...
end
Not sure if this is a good practice though...

Scoping a class method to current_user

I'm working on implementing a tagging system and I'm having problem querying for tagged objects with a scope.
For example, I would like to find all the user's items with a certain tag. With a class method I can currently find all the objects:
def self.tagged_with(name)
Tag.find_by_name(name).items
end
However, this has a problem. If I were to do something like: current_user.items.tagged_with(name) won't this existing method return ALL the items and not just items owned by the current_user? I suppose this is a simply querying issue but I can't figure out how to change a class method into something called on a collection. I have tried going the opposite way, to get a the collection through the tags, something like... tag.items.where(:user_id => current_user.id) but in this case, it's a many-to-many relationship and I haven't been able to get on thumb on this either.
What's the proper way to restrict a query like this?
Create an association on your User class that points to your Tag class.
class User < ActiveRecord::Base
has_many :tags
end
Then you can do:
current_user.tags.where(...)
If you don't already have an association in place, you'll need to create a migration to have the tags table reference your users table with a foreign key.
I think this will help you:
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
So, basically you can define your method tagged_with directly into the association!
This example is took from the documentations ActiveRecord::Associations

Resources