When I'm going to index action it outputs whole data from my model, as I understood when I'm going to show action it supposed to output only object depends on id param, but in my case it output the same data as it were in index action.
My active model serializer:
class CategorySerializer < ApplicationSerializer
attributes :id, :name, :alias, :parent_category_id, :position, :menu, :status
has_many :subcategories
has_many :products
end
Controller:
module API
module Store
class CategoriesController < ApplicationController
def index
#categories = Category.all
if params[:name]
#categories = Category.find_by(name: params[:name])
end
puts #categories
render json: #categories
end
def show
#category = Category.find(params[:id])
render json: #category
end
end
end
end
Related
I am new in Ruby on Rails. I am making a Rails API using Rails 5.1, active record serializer, doorkeeper and devise gem.
I have an Order table and it has many products. The relation between order and product is many-to-many.
Order model:
class Order < ApplicationRecord
validates_presence_of :brute, :net
has_and_belongs_to_many :products
end
Product model:
class Product < ApplicationRecord
belongs_to :category
validates_presence_of :name, :price
validates_uniqueness_of :name
has_and_belongs_to_many :orders
end
I have a join table named orders_products.
Order serializer:
class OrderSerializer < ActiveModel::Serializer
attributes :id, :discount, :brute, :net, :payed, :payed_at, :products
def products
object.products.map do |product|
ProductSerializer.new(product, scope: scope, root: false, event: object)
end
end
end
Product serializer:
class ProductSerializer < ActiveModel::Serializer
attributes :id, :name, :price, :description
has_one :category
end
Order controller:
module Api
class OrdersController < ApiController
before_action :set_order, only: [:show, :update, :destroy]
# GET /api/orders
def index
#orders = Order.all
render json: #orders
end
# GET /api/orders/1
def show
render json: #order
end
# POST /api/orders
def create
#order = Order.new(order_params)
if #order.save
render json: #order, status: :created, location: api_order_url(#order)
else
render json: #order.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /api/orders/1
def update
if #order.present?
if #order.update(order_params)
render json: #order
else
render json: #order.errors, status: :unprocessable_entity
end
end
end
# DELETE /api/orders/1
def destroy
#order.destroy if #order.present?
end
private
# Use callbacks to share common setup or constraints between actions.
def set_order
#order = Order.find(params[:id])
rescue ActiveRecord::RecordNotFound
Rails.logger.error{ 'Order record is not found' }
nil
end
# Only allow a trusted parameter "white list" through.
def order_params
params.require(:order).permit(:discount, :brute, :net, :payed, :payed_at, product_ids: [])
end
end
end
When I post some order json data from API generator app like Postman/Insomnia, Order is being saved in orders table but no data saved in orders_products join table.
My request(POST http://localhost:3000/api/orders) of order json:
{
"discount": 110,
"brute": 100,
"net": 200,
"payed": null,
"payed_at": null,
"product_ids": [3]
}
I try to find the solution but I failed.
Finally I have solved in your problem.Just add an attribute in your model.
Order Model:
class Order < ApplicationRecord
attribute :product_ids
validates_presence_of :brute, :net
has_and_belongs_to_many :products
end
Order Serializer:
class OrderSerializer < ActiveModel::Serializer
attributes :id, :discount, :brute, :net, :payed, :payed_at
has_many :products
end
And create method in your order api:
# POST /api/orders
def create
#order = Order.new(order_params)
if #order.save
# Find products
#products = Product.where(id: order_params[:product_ids])
# Create join table records
#products.each { |product| product.orders << #order }
render json: #order, status: :created, location: api_order_url(#order)
else
render json: #order.errors, status: :unprocessable_entity
end
end
I have tested in locally and it works! Happy Programming :)
As far as I know, Rails doesn't automatically handle creating the join records when given a list of ids. Therefore when you're calling #order = Order.new(order_params) and expecting it to know how to handle product_ids: [3], it's just ignoring it.
If you modify your create endpoint with the below, you should see the join records being created.
# POST /api/orders
def create
#order = Order.new(order_params)
if #order.save
# Find products
#products = Product.where(id: order_params[:product_ids])
# Create join table records
#products.each { |product| product.orders << order }
render json: #order, status: :created, location: api_order_url(#order)
else
render json: #order.errors, status: :unprocessable_entity
end
end
This is just one possible solution that doesn't do any error checking. Depending how secure and robust your application needs to be you may need to create a service that wraps this and handles validating that products are found before creating the order and associating the records.
EDIT: OrderSerializer
Once you've verified that the join table records are being created properly. Check that your serializers are working, they have great documentation. I believe you can swap out your current products method in the OrderSerializer with this:
class OrderSerializer < ActiveModel::Serializer
attributes :id, :discount, :brute, :net, :payed, :payed_at, :products
def products
object.products.map do |product|
ProductSerializer.new(product).serializable_hash
end
end
end
I'm using Rails 5, gem 'active_model_serializers' and I have this situation:
controllers/leagues_controller.rb:
class LeaguesController < ApplicationController
before_action :authenticate_user
before_action :set_league, only: [:show, :update, :destroy]
# GET /leagues
def index
#leagues = League.all
render json: #leagues
end
# GET /leagues/1
def show
render json: #league, include: ['teams'] # Here I need some way to order
end
private
# Use callbacks to share common setup or constraints between actions.
def set_league
#league = League.find(params[:id])
end
models/league.rb:
class League < ApplicationRecord
has_many :teams
end
serializers/league_serializer.rb:
class LeagueSerializer < ActiveModel::Serializer
attributes :id, :name, :address, :teams_num
has_many :teams
has_many :menus, serializer: ProductSerializer
end
I need to order teams by "goals" or other attribute in the team model. How to do that? And where? I can't do this below?
def show
render json: #league, include: ['teams'], :order => 'goals DESC'
end
What is the logic? I have to order in my serializer? In my model?
Why I can't use something like this? It gives to me "undefined method includes". Why?
def show
#league = #league.includes(:team).order(goals: :desc)
render json: #league
end
I want to the category I create in relation with the post I want to create. I don't want to add a gem or anything else I think we can do it with has_manyand belongs_to
I create two tables Posts and Category and I want to choose a category in a collection and this is written in the post new#view I want to create and on the post show#view and post index#view.
Models for posts is :
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :category
TAGS = ["Design", "Mode", "Tendance", "Life-Style", "Tradition", "Gastronomie", "Insolite", "Technologie"]
validates :tag, inclusion: { in: Post::TAGS, allow_nil: false }
mount_uploader :cover, ImageUploader
end
and categories is foreign keys for posts here is the model
class Category < ActiveRecord::Base
has_many :posts
NAMES = ["JAPON", "CHINE", "INDE"]
validates :name, inclusion: { in: Category::NAMES, allow_nil: false }
end
Posts Controllers are here
class PostsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show]
before_action :find_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all
end
def show
# #alert_message = "Vous lisez #{#post.title}"
end
def new
# if current_user and current_user.admin?
#post = Post.new
# else
# redirect_to posts_path
# end
end
def create
# if current_user and current_user.admin?
#post = current_user.posts.new(post_params)
##post = current_user.posts.new(post_params)
if #post.save
redirect_to #post
else
render :new
end
# else
# render 'shared/404.html.erb'
# end
end
def edit
end
def update
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
def destroy
#post.destroy
redirect_to :back
end
private
def find_post
#post = Post.find(params[:id])
end
# def set_category
# #post_category = Category.find(params[:category_id])
# end
def post_params
params.require(:post).permit(:title, :subtitle, :introduction, :body, :cover, :tag, :category_id)
end
end
and categories_controller are here
class CategoriesController < ApplicationController
before_action :set_category, only: [:show, :new, :create, :destroy]
def show
#category = Category.find(params[:id])
end
def index
#categories = Category.all
end
def create
#category = Category.new(category_params)
if #category.save
redirect_to #post
else
render :new
end
end
def new
#category = Category.new
end
def edit
end
def update
end
def destroy
#category = Category.find(params[:id])
#category.destroy
redirect_to post_path(#post)
end
private
# def set_post
# #post = Post.find(params[:post_id])
# end
# def set_category
# #category = Category.find(params[:category_id])
# end
def set_category
if params[:id].present?
#category = Category.find(params[:id])
else
#category = Category.new
end
end
# def find_category
# #category = Category.find(params[:id])
# end
def category_params
params.require(:category).permit(:name, :description)
end
end
Please could you show the right way to add a category I choose in collection and I show in post new#view show#view index#view.
Thank you for your help.
If a category can belong to more than one post, I would recommend using a has_may_through relationship for your data model.
class Post
has_many :post_categories
has_many :categories, through: post_categories
accepts_nested_attributes_for :categories
end
class Category
has_many :post_categories
has_many :posts, through: post_categories
end
class PostCategory
belongs_to :posts
belongs_to :categories
end
You will need to create a migration to add the 'through' table, PostCategory, which will consist of a post_id and a category_id.
In the controller
def new
#post = Post.new
#post.categories.build
end
def post_params
params.require(:post).permit(:title, :subtitle, :introduction, :body, :cover, :tag, category_ids: [])
end
In your form, you can use fields_for to build the form for categories.
If you set this all up, rails will handle the creation of the category when the post is created. Then you will be able to call category.posts to get all the posts with that category and you can call post.categories to get all the categories assigned to the post.
Assigning a Post to a Category can be accomplished in the create method of your PostsController. You are already passing the category_id to the controller via the params.
def create
#post = current_user.posts.new(post_params)
#category = Category.find(params[:category_id])
if #post.save && (#category.posts << #post)
redirect_to #post
else
render :new
end
end
When you are trying to show a post in a view, you should be able to access that category directly.
<%= post.category.name %>
If you allow some posts to be created without a category, you can simply not show anything, or show a "No Category" message.
# Don't show anything if the post doesn't belong to a category
<%= post.category.name if post.category.present? %>
# Show a "No Categories" message (this uses the ternary operator)
<%= post.category.present? ? post.category.name : "No Category" %>
How do I add parameters to methods for rendering the current place in favorites?
I tried this:
class Place < ActiveRecord::Base
has_and_belongs_to_many :users
def in_fav(user)
if user.places.include?Place.find(id)
return true
else
return false
end
end
end
class User < ActiveRecord::Base
has_and_belongs_to_many :places
end
class PlacesController < ApplicationController
places = Place.all
user = User.first
render json: {desc:true, status:1; data: places}.to_json(:methods => :in_fav(user))
end
I find same problem here
attr_accessor :current_user
def is_favorited_by_user?(user=nil)
user ||= current_user
end
#drops.current_user = current_user
render :json => #drops.to_json(:methods => :is_favorited_by_user?)
I don't understand current_user - it's assocciations? and how to use method current_user for collection #drops
I have recently been advised that for my current rails app relationships I should use the gem nested set. ( My previous thread / question here) I currently have 3 models,
Categories has_many Subcategories
Subcategories belongs_to Categories, and has_many products.
Product belongs_to Subcategory. I wanted to display it something like this
+Category
----Subcategory
--------Product
--------Product
----Subcategory
--------Product
--------Product
+Category
----Subcategory
--------Product
--------Product
So if I were to do this in nested_set, how would I set this up in my Models? Would I remove my subcategory and product models, and just add acts_as_nested_set in the Category model? and once I have the model taken care of, what will I update my controllers actions with, to be able to create nodes in the nested set I create?
I guess just help me understand how I can do the CRUD, create, read, update, and destroying of this nested_set list.
Here is some code I have already
Categories Controller:
class CategoriesController < ApplicationController
def new
#category = Category.new
#count = Category.count
end
def create
#category = Category.new(params[:category])
if #category.save
redirect_to products_path, :notice => "Category created! Woo Hoo!"
else
render "new"
end
end
def edit
#category = Category.find(params[:id])
end
def destroy
#category = Category.find(params[:id])
#category.destroy
flash[:notice] = "Category has been obliterated!"
redirect_to products_path
end
def update
#category = Category.find(params[:id])
if #category.update_attributes(params[:category])
flash[:notice] = "Changed it for ya!"
redirect_to products_path
else
flash[:alert] = "Category has not been updated."
render :action => "edit"
end
end
def show
#category = Category.find(params[:id])
end
def index
#categories = Category.all
end
end
Category Model:
class Category < ActiveRecord::Base
acts_as_nested_set
has_many :subcategories
validates_uniqueness_of :position
scope :position, order("position asc")
end
Subcategory Model:
class Subcategory < ActiveRecord::Base
belongs_to :category
has_many :products
scope :position, order("position asc")
end
And finally, Product Model:
class Product < ActiveRecord::Base
belongs_to :subcategory
has_many :products
scope :position, order("position asc")
end
Any help would be very appreciated.
I would go with a Category and a Product like so:
class Product > ActiveRecord::Base
belongs_to :category
end
class Category > ActiveRecord::Base
has_many :products
acts_as_nested_set
end
class CategoryController < ApplicationController
def create
#category = params[:id] ? Category.find(params[:id]).children.new(params[:category]) : Category.new(params[:category])
if #category.save
redirect_to products_path, :notice => "Category created! Woo Hoo!"
else
render "new"
end
end
def new
#category = params[:id] ? Category.find(params[:id]).children.new : Category.new
end
def index
#categories = params[:id] ? Category.find(params[:id]).children : Category.all
end
end
#config/routes.rb your categories resource could be something like..
resources :categories do
resources :children, :controller => :categories,
:only => [:index, :new, :create]
end
this way is the most flexible, as you can put your products in any a category at any level.