Rails nested models and virtual attribute initialization - ruby-on-rails

I have a problem understanding how are attributes "sent" to nested model(s), and if is possible to do this for model with virtual attrubute too. I have three models:
class User < ActiveRecord::Base
...
has_and_belongs_to_many :clearancegoods
has_many :clearanceitems, through: :user_clearanceitems_descriptions
has_many :user_clearanceitems_descriptions
...
end
class Clearanceitem < ActiveRecord::Base
...
has_many :users, through: :user_clearanceitems_descriptions
has_many :user_clearanceitems_descriptions
accepts_nested_attributes_for :user_clearanceitems_descriptions
...
def user_id
#user_id
end
def user_id=(val)
#user_id = val
end
end
class UserClearanceitemsDescription < ActiveRecord::Base
belongs_to :user
belongs_to :clearanceitem
end
in console:
desc = User.find(5).user_clearanceitems_descriptions.new
desc.user_id
### result is 5
item = User.find(5).clearanceitems.new
item.user_id
### result in nil

If Clearanceitem can have many users, then it can't have a single user_id, otherwise that attribute would have to have multiple values. If you're just trying to create Clearanceitems associated with the User, ActiveRecord will automagically create the associated join record:
User.find(5).clearanceitems.create
User.find(5).clearanceitems # Contains the Clearanceitem you just created
So just remove the user_id attribute from Clearanceitem.

Related

How can I implement a has_many :through association two ways?

I have a property model and a user model.
A user with the role of 'admin', which is represented by a column on the users table, can have many properties.
A user with a role of 'guest' can also belong to a property, which gives them access to that property.
How should I do this in Rails?
authorizations table -> user_id, property_id
class Authorization < ActiveRecord::Base
belongs_to :user
belongs_to :property
end
class User < ActiveRecord::Base
has_many :authorizations
has_many :properties, through: :authorizations
end
class Property < ActiveRecord::Base
has_many :authorizations
has_many :users, through: :authorizations
end
then you can do User.find(id).properties
First, you need a has_many :through association between your models User and Property. So, create a new table properties_users with columns user_id and propety_id. And do following changes to the models:
class PropertiesUser < ActiveRecord::Base
belongs_to :user
belongs_to :property
end
class User < ActiveRecord::Base
has_many :properties_users
has_many :properties, through: :properties_users
end
class Property < ActiveRecord::Base
has_many :properties_users
has_many :users, through: :properties_users
end
Now, we need to make sure that a guest user does not have more than one property. For that we can add a validation to model PropertiesUser like below:
class PropertiesUser < ActiveRecord::Base
validate :validate_property_count_for_guest
private
def validate_property_count_for_guest
return unless user && user.guest?
if user.properties.not(id: self.id).count >= 1
self.errors.add(:base, 'guest user cannot have more than one properties')
end
end
end
class User < ActiveRecord::Base
def guest?
# return `true` if user is guest
end
end
Finally, to access a guest user's property, define a dedicated method in model User:
class User < ActiveRecord::Base
def property
# Raise error if `property` is called on non-guest users
raise 'user has multiple properties' unless guest?
properties.first
end
end
Now, you can fetch a guest user's property by running:
user = User.first
user.guest?
=> true
user.property
=> <#Property 1> # A record of Property

Polymorphic has_many through Associations in Ruby on Rails

I have the following problem,
A user can have several professions, more than 10. For example, a user may be a doctor, teacher, and N. Each profession has its own attributes.
I could do, Doctor belongs_to User, but if I want to know all the professions of this user I will have to check each row of the User table.
I created the following code
class User < ApplicationRecord
has_many :jobables
end
class Job < ApplicationRecord
belongs_to :user
belongs_to :jobable
end
class Jobable < ApplicationRecord
has_one :job
end
class Medic < Jobable
end
class Programmer < Jobable
end
But I do not know if that would be the best answer
I would think that it would be much easier to do something like:
class User < ApplicationRecord
has_many :user_professions
has_many :professions, through: :user_professions
end
# == Schema Information
#
# Table name: professions
#
# id :integer not null, primary key
# name :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Profession < ApplicationRecord
has_many :user_professions
has_many :users, through: :user_professions
end
class UserProfession < ApplicationRecord
belongs_to :user
belongs_to :profession
end
You could then create logic to ensure that a Profession is only assigned to a User once.
Then, you could simply do:
#user.professions
And get all the Professions for a User.
You could also do:
#profession.users
And get all the Users that belong to the Profession.
Based on the edit to your question, you could do something like:
class UserProfession < ApplicationRecord
belongs_to :user
belongs_to :profession
belongs_to :profession_detail, polymorphic: true
end
In which case you might have something like:
class DoctorDetail < ApplicationRecord
end
And you could do something like:
#user.professional_detail_for(:doctor)
Of course, you would need to implement the professional_detail_for method on the User model which might look something like:
class User < ApplicationRecord
has_many :user_professions
has_many :professions, through: :user_professions
def professional_detail_for(profession_type)
user_profession_for(profession_for(profession_type)).try(:profession_detail)
end
private
def profession_for(profession_type)
Profession.find_by(name: profession_type.to_s)
end
def user_profession_for(profession)
user_professions.find_by(profession: profession)
end
end
That's a little rough, but I imagine you get the idea.

Rails 4 find parent associations which are missing certain child associations

I am trying to query for which parent (category) records DON'T have ha child record (post) with a given property (author.id).
class Category < ActiveRecord::Base
has_many :posts, dependent: :destroy
end
class Post < ActiveRecord::Base
belong_to :category
end
class Author < ActiveRecord::Base
has_many :posts, dependent: :destroy
end
I am trying to find which categories an author has NEVER posted in. The idea is to get an array of categories that do not have posts by a GIVEN author, they can have OTHER posts...just not by the specific author
#author_unused_categories = #author.unused_categories
class Author < ActiveRecord::Base
def unused_categories
categories = Categories.all
author_used_categories = Post.where(author_id: self.id).select(:category).uniq ??????
end
end
categories = Author.includes(posts: :category).select('category.id AS category_id').map(&:category_id)
unused_categories = Category.where.not(id: categories)

Rails .where statement with nested resources

I'm struggling with a .where statement in an index action.
In my Deals controller, i'd like to list all the deals where the bank of the current_user is participating.
Below are my models :
class User < ActiveRecord::Base
belongs_to :bank
end
class Deal < ActiveRecord::Base
has_many :pools
end
class Pool < ActiveRecord::Base
belongs_to :deal
has_many :participating_banks, dependent: :destroy
has_many :banks, through: :participating_banks
end
class ParticipatingBank < ActiveRecord::Base
belongs_to :pool
belongs_to :bank
end
Here is my Deals Controller Index action :
def index
#deals = Deal.all
end
I don't find any way to say : 'I only want to see a deal if this deal has, at least, one pool where the current_user.bank has been added'.
Any idea?
Many thanks :)
You should do inner join and query joined table for id. You can easily do it in Rails by:
def index
#deals = Deal.joins(pools: :banks).where(banks: { id: current_user.bank_id })
end

Rails: Querying through 2 join table associations

I have a quite complicated relation between models and are now frustrated by a SQL Query to retrieve some objects.
given a Product model connected to a category model via a has_many :through association and a joint table categorization.
Also a User model connected to this category model via a has_many :through association and a joint table *category_friendship*.
I am now facing the problem to retrieve all products, which are within the categories of the array user.category_ids. However, I can't just not manage to write the WHERE statement properly.
I tried this:
u = User.first
uc = u.category_ids
Product.where("category_id IN (?)", uc)
However this won't work, as it doesn't have a category_id in the product table directly. But how can I change this to use the joint table categorizations?
I'm giving you the model details, maybe you find it helpful for answering my question:
Product.rb
class Product < ActiveRecord::Base
belongs_to :category
def self.from_users_or_categories_followed_by(user)
cf = user.category_ids
uf = user.friend_ids
where("user_id IN (?)", uf) # Products out of friend_ids (uf) works fine, but how to extend to categories (cf) with an OR clause?
end
Category.rb
class Category < ActiveRecord::Base
has_many :categorizations
has_many :products, through: :categorizations
has_many :category_friendships
has_many :users, through: :category_friendships
Categorization.rb
class Categorization < ActiveRecord::Base
belongs_to :category
belongs_to :product
Category_friendship.rb
class CategoryFriendship < ActiveRecord::Base
belongs_to :user
belongs_to :category
User.rb
class User < ActiveRecord::Base
has_many :category_friendships
has_many :categories, through: :category_friendships
def feed
Product.from_users_or_categories_followed_by(self) #this should aggregate the Products
end
If you need more details to answer, please feel free to ask!
Looking at the associations you have defined and simplifying things. Doing a bit refactoring in what we have to achieve.
Product.rb
class Product < ActiveRecord::Base
belongs_to :category
end
User.rb
class User < ActiveRecord::Base
has_many :categories, through: :category_friendships
scope :all_data , includes(:categories => [:products])
def get_categories
categories
end
def feed
all_products = Array.new
get_categories.collect {|category| category.get_products }.uniq
end
end
Category.rb
class Category < ActiveRecord::Base
has_many :users, through: :category_friendships
has_many :products
def get_products
products
end
end
NO NEED OF CREATING CATEGORY_FRIENDSHIP MODEL ONLY A JOIN TABLE IS NEEDED WITH NAME CATEGORIES_FRIENSHIPS WHICH WILL JUST HAVE USER_ID AND CATEGORY_ID
USAGE: UPDATED
Controller
class UserController < ApplicationController
def index
#all_user_data = User.all_data
end
end
view index.html.erb
<% for user in #all_user_data %>
<% for products in user.feed %>
<% for product in products %>
<%= product.name %>
end
end
end
I've upvoted Ankits answer but I realized there is a more elegant way of handeling this:
given:
u = User.first
uc = u.category_ids
then I can retrieve the products out of the categories by using:
products = Product.joins(:categories).where('category_id IN (?)', uc)

Resources