undefined method `sub_categories' for nil:NilClass - ruby-on-rails

I'm trying to make a category system for my blog but I've hit this error. Each blog_category can have multiple sub_categories by having parent_id pointing to the id of the main category. Some sub_categories and main blog_categories don't have anything in them. How would I prevent this NoMethodError from hitting?
BlogCategoriesController:
class BlogCategoriesController < ApplicationController
def index
#category = BlogCategory.find_by_id(params[:id])
#sub_category = #category.sub_categories.first
#posts = #subcategory.posts
end
private
def cat_params
params.require(:blog_category).permit(:name, :parent_id, :sub_category)
end
end
BlogCategory Model:
class BlogCategory < ApplicationRecord
has_many :posts
# This is called a self referential relation. This is where records in a table may point to other records in the same table.
has_many :sub_categories, class_name: "BlogCategory", foreign_key: :parent_id
# This is a scope to load the top level categories and eager-load their posts, subcategories, and the subcategories' posts too.
scope :top_level, -> { where(parent_id: nil).includes :posts, sub_categories: :posts }
end
The Posts point to the blog category via t.integer "blog_category_id" in the post table and has a belongs_to :blog_category in the Post model.

You can add a validation
def index
#category = BlogCategory.find_by_id(params[:id])
unless #category.nil?
#sub_category = #category.sub_categories.first
#posts = #subcategory.posts
end
end

Related

How to display all subcategory's posts instead of ".first" | Ruby on Rails

I have a blog with subcategories/main categories and on the main category, I want it to list the posts from all of its child categories. I got it working with using the method .first but I just don't know how to handle this in the way I need it to.
BlogCategory Model:
class BlogCategory < ApplicationRecord
extend FriendlyId
friendly_id :name, use: :slugged
has_many :posts
# This is called a self referential relation. This is where records in a table may point to other records in the same table.
has_many :sub_categories, class_name: "BlogCategory", foreign_key: :parent_id
belongs_to :parent, class_name: 'BlogCategory', foreign_key: :parent_id
# This is a scope to load the top level categories and eager-load their posts, subcategories, and the subcategories' posts too.
scope :top_level, -> { where(parent_id: nil).includes :posts, sub_categories: :posts }
def should_generate_new_friendly_id?
slug.nil? || name_changed?
end
end
blog_categories Controller:
def show
#cat = BlogCategory.friendly.find(params[:id])
#category = #cat.parent
#posts = #cat.posts
#sub_category = #cat.sub_categories.first
unless #sub_category.nil?
#relatives = #sub_category.posts
end
end
private
def cat_params
params.require(:blog_category).permit(:name, :parent_id, :sub_category)
end
def main_cat
#cat = BlogCategory.parent_id.nil?
end
Post Model: belongs_to :blog_category
I have tried a few configurations of .all .each and seen if .collection worked, but these didn't seem to fix my problem.
Thank you I do appreciate it.
You can add a has many association in your Category model like this
has_many :sub_category_posts, through: :sub_categories, source: :posts
In your controller
#relatives = #cat.sub_category_posts
I guess you want all the posts. If a post belongs to a category, that category will be a child of another category, and eventually, you'll have the main category, so, you could do something like:
#posts = Post.where.not(blog_category: nil)
If you have many main categories, one per blog, you need to implement a recursive method.
You could also use https://github.com/collectiveidea/awesome_nested_set and do something like:
#cat.descendants # array of all children, children's children, etc., excluding self
https://github.com/collectiveidea/awesome_nested_set/wiki/Awesome-nested-set-cheat-sheet

Left outer join a nested select in Rails

I'm trying to set up a classic 'like' model for Posts on a blog, where users can create one Like for any Post. I have the following models:
class Post < ApplicationRecord
belongs_to :user
has_many :likes
end
class User < ApplicationRecord
has_many :posts
has_many :likes
end
class Like < ApplicationRecord
belongs_to :user
belongs_to :post, counter_cache: true
end
In my controller I monitor the currently logged in user, current_user.
I would like to add a column to my Posts model that indicates whether or not current_user has liked each Post.
I tried adding a method to the Posts model that looks for likes:
class Post < ApplicationRecord
belongs_to :user
has_many :likes
def user_liked
!likes.empty?
end
end
And using includes in the controller method.
#posts = Post.includes(likes: { user: current_user }).where(safe_params).order(order)
render json: #posts
However I get the following error:
ArgumentError (#<User id: 1, username: "pete", ... > was not recognized for preload):
app/controllers/posts_controller.rb:51:in `index'
I'm using Rails API 5.0.
Update:
To clarify, I'm looking for the Rails equivalent of this SQL statement:
SELECT *
FROM Posts
LEFT OUTER JOIN
(SELECT *
FROM Likes
WHERE Likes.user_id = current_user.id) AS MyLikes
ON Posts.id = MyLikes.post_id
The problem is includes takes the name of associations as parameters (like :user, :likes, :posts) as parameter and does not include any instances(In your case current_user)
You could try the following instead.
#posts = Post.includes(likes: :user).where(safe_params).order(order)
If you want to check if posts belong to current_user or not (in haml view for example)
- #posts.each do |post|
- if post.likes && post.likes.any? { |like| like.user_id == current_user.id }
%span You have commented on this post

Rails has to and belongs to many correct rails association?

I'm setting up my rails association for an app and I'm not sure if my associations are correct for my use case. The use case is: A product can be added once by a user. Once created other users can then add the same product to their own "feed" within the app. I want to be able to do User.products to list all of a users products. And for products I want to be able to do something like Product.where(id: 2).users to list all of the users that have added the product. I'm currently using a has_and_belongs_to_many association but I think that this is incorrect for what I am trying to achieve?
User model: has_and_belongs_to_many :products
Product model: has_and_belongs_to_many :users
add_index "products_users", ["product_id"], name: "index_products_users_on_product_id"
add_index "products_users", ["user_id"], name: "index_products_users_on_user_id"
Do this:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :created_products, class_name: "Product", foreign_key: :user_id #-> created product
has_and_belongs_to_many :products #-> list of products
end
#app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :user #-> created the product
has_and_belongs_to_many :users #-> list of users
end
You'll need to add the appropriate foreign_key to your User model (user_id in the Product model for the belongs_to :user association) --
--
If your has_and_belongs_to_many relationship is working already, the above should be sufficient.
If not, you need to look up this documentation to see how it works, and then create a join table called products_users (which is populated with the appropriate data):
$ rails g migration CreateProductsUsers
#db/migrate/create_products_users______.rb
class CreateProductsUsers < ActiveRecord::Migration
def change
create_table :products_users, id: false do |t|
t.references :product
t.references :user
end
end
end
$ rake db:migrate
It will allow you to create a single product for a user (IE the Product object will have a direct association with the user who created it). The Product and User models will also be joined with the habtm relationship.
In your controllers, you could use the following:
#config/routes.rb
resources :products #-> url.com/products
scope "profile" do
resources :products, only: :index #-> url.com/profile/products
end
This will allow you to use the following:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
before_action :product, only: :edit
def index
#products = current_user.products #-> if you're using Devise
end
def edit
#product = current_user.created_products.find params[:id]
end
def new
#product = current_user.created_products.new
end
def create
#product = current_user.created_products.new product_params
#product.save
end
private
def product
redirect_to root_path, notice: "This is not your product" unless current_user.products.exists? params[:id]
end
def product_params
params.require(:product).permit(:x, :y, :z)
end
end
To be able using has_and_belongs_to_many create association, you must create one temperator table container 2 column are product_id, user_id
you can refer
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association

Ruby on Rails relationship model

In Ruby on Rails 4, how do you create a many-to-many relationship inside a relationship model for a friends list such as Facebook using the has_many :through ... syntax ?? I'm a newbie and currently learning Ruby on Rails 4. I have looked at this link.
But still have a hard time grasping it.
you will need a join table that references both sides of the relations
let us say you have an relation Post and another relation Category with a many to many relationship between them you need a join table to be able to represent the relationship.
migration for a join table would be
class CreateCategoriesPosts < ActiveRecord::Migration
def change
create_table :categories_posts do |t|
t.integer :category_id
t.integer :post_id
t.timestamps
end
add_index :categories_posts, [:category_id, :post_id]
end
end
and in the models/post.rb
Class Post < ActiveRecord::Base
has_and_belongs_to_many :categories
end
and in the models/category.rb
Class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
more here:
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
I think #RAF pretty much nailed it. But to use the OP's example:
class User < ActiveRecord::Base
has_and_belongs_to_many :users_list
end
class UsersList < ActiveRecord::Base
has_and_belongs_to_many :users
end
Although at first it might seem like a User should have only one list of friends (UsersList), that might not always be the case. Think of types within the UserList model, such as: 'close friends', 'work friends', 'all friends' for example.
My advice: dig into the Rails guides. This is a concept worth learning and truly understanding (which I'm still doing :).
many-to_many relationships are a simple concept, but complex when using the database because of the way databases work. A person could have 1 to N different friends, which means that a single entry for a database would need a dynamic amount of memory for each entry, which in the db world is a no-no. So instead of creating a list of friends you would have to make a table that represents the links between friends, for example:
friendship.rb
class Friendship < ActiveRecord::Base
belongs_to :friend, foreign_key: 'friend_A' # this entry has a field called 'friend_A'
belongs_to :friend, foreign_key: 'friend_B' # this entry has a field called 'friend_B'
end
These links will represent your network of friends. However, as the two previous answers have mentioned, Rails has some nifty magic, "has_and_belongs_to_many", which will do this for you.
NOTICE: The problem here is that in my StatusesController, in the index action, the #relationship object only gets the statuses of all your friends, but does not get your own statuses. Is there a better way of approaching this? I am trying to create a view to view all statuses of users that are your friends, and your own statuses too, and so far, I can't seem to figure out how to order it chronologically, even if in my status model, i included "default_scope -> { order(created_at: :desc) } ". Any advice would be deeply appreciated
class User < ActiveRecord::Base
has_many :relationships
has_many :friends, :through => :relationships
has_many :inverse_relationships, class_name: 'Relationship', foreign_key: 'friend_id'
has_many :inverse_friends, through: 'inverse_relationships', :source => :user end
#
class Relationship < ActiveRecord::Base
# before_save...
belongs_to :user
belongs_to :friend, class_name: 'User'
end
#
class RelationshipsController < ApplicationController
def friend_request
user_id = current_user.id
friend_id = params[:id]
if Relationship.where( user_id: user_id, friend_id: friend_id, accepted: false).blank?
Relationship.create(user_id: user_id, friend_id: friend_id, accepted: false)
redirect_to user_path(params[:id])
else
redirect_to user_path(params[:id])
end
end
def friend_request_accept
# accepting a friend request is done by the recipient of the friend request.
# thus the current user is identified by to_id.
relationship = Relationship.where(user_id: params[:id], friend_id: current_user.id).first
if Relationship.exists?(relationship) and relationship.accepted == false
relationship.update_attributes(accepted: true)
end
redirect_to relationships_path
end
def friend_request_reject
relationship = Relationship.where(user_id: params[:id], friend_id: current_user.id).first
relationship.destroy
redirect_to relationships_path
end
################################
def index
#relationships_pending = Relationship.where(friend_id: current_user.id, accepted: false)
end
end
#
class StatusesController < ApplicationController
def index
#status = Status.new
#relationship = Relationship.where('friend_id = ? OR user_id = ?', current_user.id, current_user.id).
where( accepted: true)
end
def new
#status = Status.new
end
end
#

Couldn't find [model] without an ID with has_many through association

I have 3 models - Mom, Dad and Kid. The Mom and Dad only belong to each other through the Kid, so the associations are like this:
class Kid < ActiveRecord::Base
belongs_to :mom
belongs_to :dad
end
class Mom < ActiveRecord::Base
has_many :kids
has_many :dads, through: :kids
end
class Dad < ActiveRecord::Base
has_many :kids
has_many :moms, through: :kids
end
I'm trying to route to a Dads' moms by searching for any mom and not just the one's through the kids of the Dad:
http://localhost:3000/dads/superdad/moms
resources :dads do
resources :kids
resources :moms
end
In my Moms controller I tried to find the ID of "superdad":
def index
#dad = Dad.find(params[:id])
if params[:q].present?
#moms = Mom.search(params[:q], page: params[:page], per_page: 25)
else
#moms = Mom.none
end
end
But run into this error:
Couldn't find Dad without an ID
# line 8 #dad = Dad.find(params[:id])
Is it possible to use #dad in this way when the Mom has no direct id towards it? What do you suggest I do? I need to get to #dad.name (and more) on the Mom's index page.
Use this:
def index
#dad = Dad.find(params[:dad_id])
if params[:q].present?
#moms = Mom.search(params[:q], page: params[:page], per_page: 25)
else
#moms = Mom.none
end
end
use params[:dad_id] instead of params[:id]. The reason is that the route generated for index action of MomsController would be:
dad_moms GET /dads/:dad_id/moms(.:format) moms#index
params[:dad_id] would give you dad_id as superdad from http://localhost:3000/dads/superdad/moms. In your case, you are looking for params[:id] which does not exist. Hence, the error.

Resources