Nested Resources id param - ruby-on-rails

I am trying to build Nested Resources Route
First I generate user it's work will and create a user category controller and create item controller but I have a problem when I create an item
First category controller
class Api::V1::CategoriesController < ApplicationController
before_action :authenticate_with_token!, only: [:create, :update, :destroy]
before_action :set_category, only: [:show, :update, :destroy]
# GET /categories
def index
#categories = current_user.category.all
render json: #categories
end
# GET /categories/1
def show
render json: #category
end
# POST /categories
def create
#category = current_user.categories.new(category_params)
if #category.save
render json: #category, status: :created
else
render json: #category.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /categories/1
def update
if #category.update(category_params)
render json: #category
else
render json: #category.errors, status: :unprocessable_entity
end
end
# DELETE /categories/1
def destroy
#category.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_category
#category = current_user.category.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def category_params
params.require(:category).permit(:title)
end
end
second item controller
class Api::V1::ItemsController < ApplicationController
before_action :authenticate_with_token!, only: [:create, :update, :destroy]
before_action :set_item, only: [:show, :update, :destroy]
# GET /items
def index
#items = current_type.items.all
render json: #items
end
# GET /items/1
def show
render json: #item
end
# POST /items
def create
#item = current_type.item.new(item_params)
if #item.save
render json: #item, status: :created, location: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /items/1
def update
if #item.update(item_params)
render json: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
# DELETE /items/1
def destroy
#item.destroy
#item.image.purge
end
private
# Use callbacks to share common setup or constraints between actions.
def set_item
#item = current_type.item.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def item_params
params.require(:item).permit(:title, :price, :image)
end
end
Application controller
class ApplicationController < ActionController::API
def current_user
#current_user ||= User.find_by(auth_token: request.headers['Authorization'])
end
def authenticate_with_token!
render json: { errors: "Not authenticated" },status: :unauthorized unless user_signed_in?
end
def user_signed_in?
current_user.present?
end
def prepare_user
#user = User.find(params[:id])
end
def current_type
#current_type ||= Type.find(params[:id])
end
end
my routes
Rails.application.routes.draw do
devise_for :users
namespace :api do
namespace :v1 do
resources :sessions, :only => [:create, :destroy]
resources :users, :only => [:show, :create, :update, :destroy] do
resources :categories do
resources :items
end`
end
end
end
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
when I try to create the item
ActiveRecord::RecordNotFound (Couldn't find Type without an ID):

If you create an item you do a POST request to the path: /api/v1/users/:user_id/categories/:category_id/items
So the url wil look something like: /api/v1/users/1/categories/1/items
Then I guess the params that you receive are: user_id and category_id and you are missing the param ID that you are looking for.
You should check the logs for what params you receive and adjust your code to pick the right param.

Related

Couldnt find Id

Im getting this error in my my controller. It can't find the products id. Not to sure why it's getting an error.
class ProductsController < ApplicationController
before_action :set_product, only: [:index, :new, :create]
before_action :authenticate_user!, except: [:show]
def index
#products = current_user.products
end
def show
end
def new
#product = current_user.products.build
end
def edit
end
def create
#product = current_user.products.build(product_params)
if #product.save
redirect_to listing_products_path(#product), notice: "Saved..."
else
flash[:alert] = "Something went wrong..."
render :new
end
end
def update
if #product.update(product_params)
flash[:notice] = "Saved..."
else
flash[:notice] = "Something went wrong..."
end
redirect_back(fallback_location: request.referer)
end
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_product
#product = Product.find(params[:id])
end
def product_params
params.require(:product).permit(:description, :features, :listing, :location, :photo_upload, :pricing)
end
end
I a user has to be signed in order to create a product. In my models i have a products belongs_to user and User has_many products
You are trying to load the product with an id from the params before your index action. But index routes usually do not provide any params[:id].
To fix this bug just change
before_action :set_product, only: [:index, :new, :create]
to
before_action :set_product, only: [:show, :edit, :update]
Id No need for index action because this display list of All records change
before_action :set_product, only: [:index, :new, :create]
To
before_action :set_product, only: [:show, :edit, :update]

Users getting set as Guest and Registered when using Roles

My Users are Still getting set to Guest regardless of me setting their roles as Registered once registered. I'm really wondering why? Below is an image
class ApplicationController < ActionController::Base
include SessionsHelper
before_filter { |c| Authorization.current_user = c.current_user }
helper :all
protect_from_forgery
protected
def permission_denied
flash[:error] = "Sorry, you are not allowed to access that page."
redirect_to root_url
end
end
User Controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
#user.role == ["registered"]
if #user.save
flash[:success] = "Welcome to the Sample App!"
log_in #user
redirect_to #user
# Handle a successful save.
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, :username, :id, :role)
end
end
USER MODEL
class User < ActiveRecord::Base
has_many :roles, :through => :interviews, :source => :user
before_create :set_default_role
ROLES = %w[ admin creator registered guest ]
def role_symbols
roles.map do |role|
role.name.underscore.to_sym
end
end
private
def set_default_role
self.role = 'registered'
end
end
Authorization rules
authorization do
role :admin do
has_permission_on [:interviews, :exhibitions, :posts, :comments, :abouts, :products, :available_works], :to => [:index, :new, :show, :new, :create, :edit, :update, :destroy]
end
role :creator do
has_permission_on :interviews, :to => [:index, :show, :new, :create, :edit, :update, :destroy]
has_permission_on :comments, :to => [:edit, :update, :create, :destroy]
end
role :guest do
has_permission_on [:interviews, :exhibitions, :posts, :abouts, :products, :available_works, :home], :to => [:index, :show]
has_permission_on [:users], :to => [:new, :show, :edit, :create, :update, :destroy, :index]
end
role :registered do
has_permission_on [:interviews, :exhibitions, :posts, :abouts, :products, :available_works, :home], :to => [:index, :show]
has_permission_on [:sessions, :users], :to => [:new, :show, :edit, :create, :update, :destroy, :index]
has_permission_on :comments, :to => [:edit, :update, :create]
end
end
Interview Controller
class InterviewsController < ApplicationController
before_action :set_interview, only: [:show, :edit, :update, :destroy]
filter_resource_access
# GET /interviews
# GET /interviews.json
def index
if params[:tag]
#interviews = Interview.all.tagged_with(params[:tag])
else
#interviews = Interview.all
end
end
# GET /interviews/1
# GET /interviews/1.json
def show
end
# GET /interviews/new
def new
#interview = Interview.new
end
# GET /interviews/1/edit
def edit
#interview = Interview.find(params[:id])
end
# POST /interviews
# POST /interviews.json
def create
#interview = Interview.new(interview_params)
respond_to do |format|
if #interview.save
format.html { redirect_to #interview, notice: 'Interview was successfully created.' }
format.json { render :show, status: :created, location: #interview }
else
format.html { render :new }
format.json { render json: #interview.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /interviews/1
# PATCH/PUT /interviews/1.json
def update
respond_to do |format|
if #interview.update(interview_params)
format.html { redirect_to #interview, notice: 'Interview was successfully updated.' }
format.json { render :show, status: :ok, location: #interview }
else
format.html { render :edit }
format.json { render json: #interview.errors, status: :unprocessable_entity }
end
end
end
# DELETE /interviews/1
# DELETE /interviews/1.json
def destroy
#interview.destroy
respond_to do |format|
format.html { redirect_to interviews_url, notice: 'Interview was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_interview
#interview = Interview.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def interview_params
params.require(:interview).permit(:title, :description, :tags, :image, :taggings, :tag_list)
end
end

Conditional sideloaded data in ActiveRecord::Serializer(based on controller actions)

I don't have so much experience in rails and building an application.i was trying to building an API using ActiveModel::Serializers.
What is the best way to side load data conditionally for particular event action?
Do I have to do it by sending query params with each call or i can set includes: true only for specific action or any another suggestion?
class EventsController < ApplicationController
before_filter :authenticate_user!, :except => [:index, :show]
before_filter :locate_collection, :only => :index
skip_before_filter :verify_authenticity_token, :only => [:create, :index, :show]
before_action :set_event, only: [:show, :edit, :update, :destroy]
# GET /events
def index
if params.has_key?("mode") and params[:mode] == "owned"
own_event
else
# #events = Event.all
render json: #events
end
end
# GET /events/1
def show
render json: #event
end
# GET /events/new
def new
#event = Event.new
end
# GET /events/1/edit
def edit
end
# POST /events
# POST /events.json
def create
#event = Event.new(event_params)
#event.creator_user_id = current_user.id
if #event.save
render json: #event
else
render json: #event.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /events/1
# PATCH/PUT /events/1.json
def update
if #event.update(event_params)
render json: #event
else
render json: #event.errors, status: :unprocessable_entity
end
end
# DELETE /events/1
# DELETE /events/1.json
def destroy
aa = #event.destroy
render json: aa
end
def own_event
#events = Event.where(creator_user_id: current_user.id)
if #events.count > 0
render json: #events
else
render json: []
end
# else
# render json: {error: 1, message: "Events not found."}, status: 404
# end
end
def locate_collection
if (params.has_key?("filter"))
#events = EventPolicy::Scope.new(current_user, Event).resolve(filtering_params)
# #event_orders = EventOrder.filter(filtering_params)
else
#events = policy_scope(Event)
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_event
#event = Event.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def filtering_params
params.slice(:event_type_id)
end
end
**My Event serializer**
It includes data for multiple association listed below. i don't want to show all the data with event call.
class EventSerializer < ActiveModel::Serializer
attributes :id, :event_name, :event_start_date, :event_end_date, :creator_user_id, :event_proposal_id, :daily_start_time, :daily_start_time, :payment_category, :total_capacity, :contact_email, :description, :graced_by, :contact_details, :video_url, :demand_draft_instructions, :status, :cannonical_event_id, :website, :event_type_id, :additional_details, :event_start_time, :event_end_time, :committee_id
embed :ids
has_one :address, include: true
has_many :tickets, include: true
has_many :event_cost_estimations, include: true
has_many :event_seating_category_associations, include: true
has_many :event_awarenesses, include: true
has_one :pandal_detail, include: true
has_one :bhandara_detail, include: true
has_many :event_tax_type_associations, include: true
has_many :event_team_details, include: true
has_one :event_type, include: true
# has_many :event_registration_center_associations, include: true
has_many :registration_centers, include: true
# has_many :event_registrations, include: true
has_many :event_orders, include: true
has_one :venue_type
end
In event serializer i have includes :true for sideloaded data and I want to show sideloaded data(includes: true) of registration_centers and tickets only for index action.
What can I do for this?
Considering that you are using AMS version 0.8.x,
You can create a base_serializer.rb like this and extend the same.
class BaseSerializer < ActiveModel::Serializer
def include_associations!
if #options[:embed]
embed = #options[:embed].split(',').map!(&:to_sym)
embed.each do |assoc|
include! assoc if _associations.keys.include?(assoc)
end
end
end
end
class EventSerializer < BaseSerializer
and the index method in EventsController can be written as
# GET /events?embed=registration_centers,tickets
def index
if params.has_key?("mode") and params[:mode] == "owned"
own_event
else
# #events = Event.all
render json: #events, embed: params[:embed]
end
end
I just showed you that, the associations that are needed, can be added in url params. You can think of some smart way to send the params such that you don't need to add them in the url request.

Create a post as a user and assign it to a group

In my rails application I have made three models, User, Group and Post.
I am trying to make it so that #post.group is equal to #group.id
The post is posted from a render inside of the group#show controller and then redirects itself to the post#create controller. How do I set the #post.group to #group.id?
GroupsController
class GroupsController < ApplicationController
before_action :set_group, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#groups = Group.all
respond_with(#groups)
end
def show
#group_id = #group.id
respond_with(#group)
end
def new
#group = Group.new
respond_with(#group)
end
def edit
end
def create
#group = Group.new(group_params)
#group.save
respond_with(#group)
end
def update
#group.update(group_params)
respond_with(#group)
end
def destroy
#group.destroy
respond_with(#group)
end
private
def set_group
#group = Group.find(params[:id])
end
def group_params
params.require(:group).permit(:name, :description, :motto, :usercount, :group, :id, :groupid)
end
end
PostsController
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#posts = Post.all
respond_with(#posts)
end
def show
respond_with(#post)
end
def new
#post = Post.new
respond_with(#post)
end
def edit
end
def create
#post.group = #group_id
#post = Post.new(post_params)
#post.save
respond_with(#post)
end
def update
#post.update(post_params)
respond_with(#post)
end
def destroy
#post.destroy
respond_with(#post)
end
private
def set_post
#post = Post.find(params[:id])
end
def set_group
#group = Group.find(params[:id])
end
def post_params
params.require(:post).permit(:body, :group, :id)
end
def group_params
params.require(:group).permit(:id, :groupid, :group)
end
end
PostModel
class Post < ActiveRecord::Base
belongs_to :group
end
GroupsModel
class Group < ActiveRecord::Base
has_many :posts
end
The current code that I have returns the error undefined methodgroup=' for nil:NilClass`
Any help is appreciated!
You should start from creating Post instance, because you don't do this. I would do it like this:
def create
#post = #group.posts.build(post_params)
if #post.save
# handle success
else
# handle error
end
end
What's more, you should set before_action :set_group in your PostsController.

Display posts of users you follow?

I would like to display posts by users that I follow on a stream page.
I have a post controller which displays ALL posts from any users. Again I only want posts from users I follow to be displayed on the stream page. I created a new controller called 'Stream'. I am having a difficult time trying to get the users posts who I follow to display on my stream index.
Thank you in advance.
Stream Controller
class StreamController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#posts = Post.where(follower_id: current_user.id, followed_id: current_user.id)
end
end
Stream Index
<div class="page-header">
<center><strong><h1> Stream Page </h1></strong></center>
</div>
Post Controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#posts = Post.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 40)
end
def show
end
def new
#post = current_user.posts.build
end
def edit
end
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
def correct_user
#post = current_user.posts.find_by(id: params[:id])
redirect_to posts_path, notice: "Not authorized to edit this post" if #post.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:description, :image)
end
end
Users Controller
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :admin_user, only: :destroy
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
def index
#users = User.paginate(page: params[:page], :per_page => 20)
end
def show
#user = User.find(params[:id])
if #user
#posts = #user.posts.order("updated_at DESC")
render actions: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Your account has been deleted."
redirect_to root_path
end
def correct_user
#user = User.find(params[:id])
redirect_to root_path
end
def admin_user
redirect_to root_path unless current_user.admin?
end
end
Relationships Controller
class RelationshipsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
Migration
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :followed_id
t.timestamps
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
Relationship
I'd personally use a scope with an association:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :posts
has_many :subscribed, class_name: "Relationship", foreign_key: "followed_id"
has_many :followers, class_name: "Relationship", foreign_key: "follower_id"
end
#app/models/post.rb
Class Post < ActiveRecord::Base
belongs_to :user
scope :subscribed, ->(followers) { where user_id: followers }
end
#app/models/relationship.rb
Class Relationship < ActiveRecord::Base
#fields id | user_id | follower_id | created_at | updated_at
belongs_to :user
end
This will give you the ability to call the following:
#app/controllers/stream_controller.rb
Class StreamController < ApplicationController
def index
#posts = Post.subscribed current_user.followers
end
end
--
Alternative
An alternative would be as follows:
#app/views/stream/index.html.erb
<% current_user.subscribed.each do |followed| %>
<% followed.posts.each do |post| %>
<%= post.title %>
<% end %>
<% end %>
--
Whilst I'm not sure if this will work out of the gate, it's the way I'd create the functionality you're seeking. Essentially, you have to be able to pass the "related" users to your query call, which will then return the objects belonging to the followed relation
The post variable in the stream controller needs to be an instance variable #posts to be available in the view, and to match the #posts instance variable you have in the view.
I guess it should look #posts = Post.subscribed #user.following instead of "current_user.followers". This should solve you problem of displaying people who are following you instead of showing users you follow.

Resources