Automatically set child.parent_id when parent.children<< child? - ruby-on-rails

How to do that with ActiveRecord? My code:
p = Product.create
l = Label.create
p.labels.add << l
But I get l.parent == nil
create_table "labels", :force => true do |t|
t.integer "product_id"
end

Some code from you would be appropriate, since what you're asking should be working automatically.
class Parent < ActiveRecord::Base
has_many :children
end
class Child < ActiveRecord::Base
belongs_to :parent
end
If your code doesn't resemble that, then please post a more specific question.
Edit:
Does your Product model have any validations in it? They aren't going to pass your code above, and you aren't checking the return value from create, so you'll never know.

You could use acts_as_tree for this: http://github.com/rails/acts_as_tree

Related

scope_method to return user_id with most reviews

I have a schema that has reviews for my website.
create_table "reviews", force: :cascade do |t|
t.integer "stars"
t.string "title"
t.string "content"
t.integer "turtle_id"
t.integer "user_id"
end
I am trying to write a scope_method that returns the user_id with the most reviews. I understand I have to write a sql query that counts up multiple instances of user_id and then returns that user_id. But I am lost at doing so.
This is what I have so far.
scope :most_reviews, -> {
where("select count(*) c from reviews group by user_id order by c desc limit 1")
}
I then call it from my controller as so:
def index
#most_reviewed = Review.most_reviews
puts #most_reviewed
end
How can i properly write this scope_method to return the user_id in reviews with the most reviews?(Otherwise occurrences)? Also this needs to be a scope_method.
If your end goal is actually the user you're starting on the wrong end.
class User < ApplicationRecord
has_many :reviews
def self.most_reviews
select('users.*', 'COUNT(reviews.*) AS reviews_count')
.joins(:reviews)
.group(:id)
.order(reviews_count: :desc)
end
end
scope is just syntactic sugar to define one-liner class methods. There is nothing actually special about the methods it declares and using it for cases where the code does not fit in a one-liner just hurts readability.
You can also setup a counter cache which will let you simplify this down to:
class Review < ApplicationRecord
belongs_to :user, counter_cache: true
end
class User < ApplicationRecord
has_many :reviews
scope :most_reviews, ->{ order(reviews_count: :desc) }
end
Which is something that actually fits in a scope one-liner.
Try this
Controller:
def index
#most_reviewed = Review.most_reviews.first
puts #most_reviewed
end
Scope:
scope :most_reviews, -> { select("users.id, COUNT(reviews.user_id) AS reviews_count")
.joins(:reviews)
.group(:id)
.order(reviews_count: :desc)
}

How do I create a method to find out if a user is part of a group?

I am having a hard time creating a method to find out if a user is part of a group. There is a model for User, Group, and Membership. Below are two methods (of the many that I attempted but to no success).
How can I create a method to find out if a user is a member of a group? (I would like the method to produce a true or false result.)
def member?(group_1)
if Membership.where(user_id: self.id, group_id: group_1.id)
return true
else
return false
end
end
def membership?(group)
Membership.where(user_id: self.id, group_id: group.id)
end
Here are the attributes of the three different models:
create_table "groups", force: :cascade do |t|
t.string "name"
end
create_table "memberships", force: :cascade do |t|
t.integer "user_id"
t.integer "group_id"
end
create_table "users", force: :cascade do |t|
t.string "email"
t.string "first_name"
t.string "last_name"
end
Here is the code in each respective model:
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships, dependent: :destroy
has_many :groups, through: :memberships
end
class Group < ActiveRecord::Base
has_many :memberships, dependent: :destroy
has_many :users, through: :memberships
end
In regards to you question create a method to find out if a user is a member of a group, This should work fine
In user.rb define an instance method
def membership?(group)
memberships.find_by(group: group).present?
end
This should work for you:
def member?(group)
groups.include?(group)
end
Your current membership? method is returning an array. If you append .any? to the end of it, it will return true or false.
def membership?(group)
Membership.where(user_id: self.id, group_id: group.id).any?
end
EDIT: dhouty's answer is the most precise - leaving this here just to explain why your original method wasn't working
You can do this in two clean ways. (You may either or both use the following)
1) app/models/user.rb
class User < ActiveRecord::Base
# ...
# ...
def member_of_group?(group)
groups.exists?(group)
end
end
2) app/models/groups.rb
class Group < ActiveRecord::Base
# ...
# ...
def has_user_member?(user)
users.exists?(user)
end
end
Then you can just use the methods like the following:
# If checking if a User object is member of a #group
#user.member_of_group?(#group)
=> returns true or false
# If checking if a Group object has a member #user
#group.has_user_member?(#user)
=> returns true or false
not the answer, but just saying:
def member?(group_1)
if Membership.where(user_id: self.id, group_id: group_1.id)
return true
else
return false
end
end
Membership.where will return [] unless it finds any data. For that, [] does equal true , that means - your true block will always be called! If you wanna stick with that where logic, you need to ask if the array is containing any data. [].any?
you dont need to write return since ruby is always returning the last value of anything.
// Edit towards your request by comment:
ya, you can go .where().any? but imageine the .where() will return a thousand datasets and you just need to know if there is just one...
We would call this "bad code".
The better solution would be to use find_by. Find_by returns the first matching element.
Membership.find_by(user_id: id, group_id. group_1.id)
the good thing about find_by is the fact, that it will return nil if nothing was found. That means you can do this
def member?(group_1)
!!Membership.find_by(user_id: id, group_id: group_1.id)
end
Find_by will return nil or the object, with !! you make the object to an expression of true

Specific model association case in rails

I have 3 tables: proposals, items/proposals (items is nested inside proposals) and invoices.
I want to create invoices for those items in the proposals that got approved. How would the associations for these look like? Also, how would I set up the invoices form to choose only those items that got approved by the client?
Consider creating two different line items models for Proposal and Invoice.
class Proposal < ActiveRecord::Base
has_many :proposal_line_items
end
class ProposalLineItem < ActiveRecord::Base
belongs_to :proposal
end
class Invoice < ActiveRecord::Base
has_many :invoice_line_items
end
class InvoiceLineItem < ActiveRecord::Base
belongs_to :invoice
end
You can consider having an "approved" attribute in proposal line items. In the invoice form, you can show proposal line items approved by the client.
The suggestion of having separate line items for Proposal and Invoice is based on ERP data modeling principles to maintain the integrity of Invoice.
Update
For example here are the sample migrations for the models suggested
class CreateProposalLineItems < ActiveRecord::Migration
def change
create_table :proposal_line_items do |t|
t.references :proposal, index: true, foreign_key: true
t.string :name
t.integer :approved
t.timestamps null: false
end
end
end
class CreateProposals < ActiveRecord::Migration
def change
create_table :proposals do |t|
t.string :name
t.timestamps null: false
end
end
end
class InvoicesController < ActionController
def new
#approved_items = Proposal.find(params[:proposal_id]).proposal_line_items.where(:approved => 1)
end
end
You can iterate over the #approved_items in your view and display it to users.
V

How to set a models attribute based on a parent model?

I want to set an attribute on a child model when its parent is changed
Here is an example:
create_table "children", :force => true do |t|
t.integer "parent_id"
t.string "parent_type"
t.integer "foo_id"
end
create_table "fathers", :force => true do |t|
t.integer "foo_id"
end
create_table "mothers", :force => true do |t|
t.integer "foo_id"
end
create_table "foos", :force => true do |t|
end
class Foo < ActiveRecord::Base
end
class Child < ActiveRecord::Base
belongs_to :parent, :polymorphic => true
belongs_to :foo
end
class Father < ActiveRecord::Base
belongs_to :foo
end
class Mother < ActiveRecord::Base
belongs_to :foo
end
Now, when I execute the following, I want child.foo_id to be set from parent:
foo = Foo.new {|foo| foo.id = 1}
parent = Father.new {|father| father.foo = foo}
child = Child.new
child.parent = parent
I need foo_id to be set right away, not in a before_validation callback or anything like that.
This is a simplified example, in the real case I have many more polymorphic types. I know this can be accomplished with an after_add callback on a has_many association on Father and Mother, but I'd rather not have to add a has_many association if possible since that requires me to add code in many more places. Is there a way to do this?
I don't clearly understand what you want to achieve.
May be this
parent = Parent.new(foo_id=>123456)
child = Child.new(:parent=>parent,:foo_id=>parent.foo_id)
if parent.save
child.save
end
or
parent = Parent.new(foo_id=>123456)
if parent.save
Child.create(:parent=>parent,:foo_id=>parent.foo_id)
end
not sure if this would work but maybe you could overwrite the setter for parent in the Child model
def parent=(p)
self.foo_id = p.foo_id
super(p)
end

Rails - Model association seems to be saving, but not loaded during find()?

In one model, I have this:
class Game < ActiveRecord::Base
has_one :turn
attr_accessor :turn
attr_accessible :turn
default_scope :include => :turn
def Game.new_game
turn = Turn.create count: 1, phase: 'upkeep', player: 1
game = Game.create turn: turn
game
end
end
class Turn < ActiveRecord::Base
belongs_to :game
end
Later, in a controller, I have this:
respond_with Game.find(params[:id])
But for some reason, the returned game has a turn_id that is nil and no associated turn object.
Why isn't the association being saved properly, or not returning properly with find()?
In my migration, I think I've setup the association correctly:
create_table :games do |t|
t.timestamps
end
def change
create_table :turns do |t|
t.string :phase
t.integer :count
t.references :game
t.timestamps
end
end
You seemed to have got messed up on associations
This is what i Think as per the understanding of scenario.
The associations should be like
class Game < ActiveRecord::Base
has_one :turn
#.....
end
class Turn < ActiveRecord::Base
belongs_to :game
#.....
end
and migrations like
create_table :games do |t|
#add some columns
t.timestamps
end
create_table :turns do |t|
t.references :game
#add some columns
t.timestamps
end
now to add new game and turn
game = Game.create
turn = game.turn.create count: 1, phase: 'upkeep', player: 1
game.tun.create will automatically create a turn record with game_id = game.id and other supplied args.
problem with your migration is game is referring turn which instead should be opposite .
Find more on associations here
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://guides.rubyonrails.org/association_basics.html
Something is inverted.
Since Turn has the belongs_to statement, Turn should contain game_id, and not the other way around.
So you can't access a Game turn_id because the field does not exists. It will be always nil and if ou remove the has_one statement it will raise an exception.

Resources