I have a Rails Api that feeds a Vue front end. I have two main models, Contacts and Outlets, with a many to many relationship via a join table ContactOutlets. I am trying to figure out how in the Contacts controller to add an association to an outlet.
I recognize, I could call the ContactOutlet create action separately, but it seems wasteful if Rails can handle this on the back end. I want vue to call contact#update once.
Contact Model:
class Contact < ApplicationRecord
has_many :contact_outlets
has_many :outlets, through: :contact_outlets
has_many :calls
validates_uniqueness_of :email
validates_uniqueness_of :name
end
Outlet Model:
class Outlet < ApplicationRecord
has_many :contact_outlets
has_many :contacts, through: :contact_outlets
has_many :calls
validates_uniqueness_of :website
end
ContactOutlet:
class ContactOutlet < ApplicationRecord
belongs_to :contact
belongs_to :outlet
validates_uniqueness_of :contact_id, :scope => :outlet_id
end
Contacts Controller:
class ContactsController < ApplicationController
before_action :set_contact, only: %i[ show update destroy ]
# GET /contacts
def index
#contacts = Contact.all
render json: #contacts, include: :outlets
end
# GET /contacts/1
def show
render json: #contact, include: :outlets
end
# POST /contacts
def create
#contact = Contact.new(contact_params)
if #contact.save
render json: #contact, status: :created, location: #contact
else
render json: #contact.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /contacts/1
def update
if #contact.update(contact_params)
render json: #contact, include: :outlets
else
render json: #contact.errors, status: :unprocessable_entity
end
end
# DELETE /contacts/1
def destroy
#contact.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_contact
#contact = Contact.find(params[:id])
end
# Only allow a list of trusted parameters through.
def contact_params
params.require(:contact).permit(:name, :email, :bio, :image_url)
end
end
Solved this. In case anyone else is looking the models above are fine. made some adjustments to the contact_params to allow access to the outlets array. Then fixed the update action. Full controller code below:
class ContactsController < ApplicationController
before_action :set_contact, only: %i[ show update destroy ]
# GET /contacts
def index
#contacts = Contact.all
render json: #contacts, include: :outlets
end
# GET /contacts/1
def show
render json: #contact, include: :outlets
end
# POST /contacts
def create
#contact = Contact.new(contact_params)
if #contact.save
render json: #contact, status: :created, location: #contact
else
render json: #contact.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /contacts/1
def update
if #contact.outlets
#contact.outlets.delete_all
end
if params[:outlets]
contactOutlets = params[:outlets]
contactOutlets.each do |outlet|
#contact.outlets << Outlet.find(outlet[:key])
end
end
if #contact.update(contact_params)
render json: #contact, include: :outlets
else
render json: #contact.errors, status: :unprocessable_entity
end
end
# DELETE /contacts/1
def destroy
#contact.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_contact
#contact = Contact.find(params[:id])
end
# Only allow a list of trusted parameters through.
def contact_params
params.require(:contact).permit(:name, :email, :bio, :image_url, outlet_ids:[])
end
end
Related
I'm new to Rails and continue getting this error. I've double checked my syntax and everything seems to be fine. I've included what I believe to be the pertinent files but let me know if you need more info.
controllers/users_controller.rb:
class UsersController < ApplicationController
def index
#users = User.all
render json: #users, status: :ok
end
def show
render json: #user
end
def create
#user = User.new(user_params)
#user.save
render json: #user, status: :created
end
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
def destroy
#user = User.where(id: params[:id]).first
if #user.destroy
head(:ok)
else
head(:unprocessable_entity)
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :email, :password)
end
end
models/user.rb
class User < ApplicationRecord
has_secure_password
validates :email, presence: true
has_many :reviews, :dependent => :destroy
has_many :albums
end
config/routes.rb
Rails.application.routes.draw do
root to: "static#home"
resources :users
get "*path", to: "fallback#index", constraints: ->(req) { !req.xhr? && req.format.html? }
end
The routes file is irrelevent here. It feels like the User model just cannot be found at all. Try just
User.new
in the console. If it can't be found perhaps the model is in the wrong location. e.g. is it in app/models/user.rb ? If it is what happens if you
require './app/models/user'
Anything like a syntax error, etc?
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 have a User object and an Orgs object that are associated through a HABTM join table. I want to send an email to the users when the Orgs object is updated AND the Org.approved value is set to true. I have an approved boolean on the Org.
I think I've gotten most of the way there but I need help with the step of actually sending the email.
Here's my code
class OrgMailer < ApplicationMailer
default from: 'myemail#example.co'
def org_approved(user, org)
#user = user
#orgs = User.orgs.all
#url = 'http://example.com/login'
mail(to: #user.email, subject: 'Your listing has been approved.')
end
end
User.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_and_belongs_to_many :orgs, join_table: :orgs_users
end
Org.rb
class Org < ApplicationRecord
has_and_belongs_to_many :users, join_table: :orgs_users
# after_update :send_approved_listing_email, only: [:update]
attachment :company_image
def send_approved_listing_email
OrgMailer.org_approved(i).deliver_now if org.approved === true
end
end
UPDATED: ADDED ORG_CONTROLLER
I've edited my code to look like the answer below but am now getting a new error: uninitialized constant Org::OrgsUser
It's caused when I hit the #org.users << #user line in the create action.
If I delete this line, I'm able to create an org but it's not associating properly.
org_controller.rb
class OrgsController < ApplicationController
before_action :set_org, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#orgs = Org.all
#tags = ActsAsTaggableOn::Tag.all
end
def show
end
def new
#org = Org.new
end
def contest
end
def edit
end
def create
#user = current_user
#org = Org.new(org_params)
#org.users << #user
respond_to do |format|
if #org.save
format.html { redirect_to thankyou_path, notice: 'Your listing was successfully created. Our team will approve your listing after review.' }
format.json { render :show, status: :created, location: #org }
else
format.html { render :new }
format.json { render json: #org.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #org.update(org_params)
format.html { redirect_to #org, notice: 'Listing was successfully updated.' }
format.json { render :show, status: :ok, location: #org }
else
format.html { render :edit }
format.json { render json: #org.errors, status: :unprocessable_entity }
end
end
end
def destroy
#org.destroy
respond_to do |format|
format.html { redirect_to orgs_url, notice: 'Listing was successfully destroyed.' }
format.json { head :no_content }
end
end
def tagged
if params[:tag].present?
#orgs = Org.tagged_with(params[:tag])
else
#orgs = Org.postall
end
end
private
def set_org
#org = Org.find(params[:id])
end
def org_params
params.require(:org).permit(:twitter, :linkedin, :facebook, :name, :offer, :offercode, :url, :descrption, :category, :approved, :company_image, :tag_list => [])
end
end
I'm using active admin for my admin panel and have a batch action to update any selected orgs and approve them. I think what I'm missing is that in the send_approved_listing_email method I need to iterate through the orgs and email each user when the org is approved.
Right now nothing happens on update so I'm sure I'm not doing this correctly. What am I missing? How should I write this?
I would create a model for the join table rather than using habtm. That way you can use a callback when the join object is saved:
class User < ApplicationRecord
has_many :orgs_users
has_many :orgs, through: :orgs_users
end
class Org < ApplicationRecord
has_many :orgs_users
has_many :users, through: :orgs_users
end
class OrgsUsers < ApplicationRecord
belongs_to :org
belongs_to :user
after_create :send_approved_listing_email
def send_approved_listing_email
OrgMailer.org_approved(user, org).deliver_now if org.approved === true
end
end
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.
I'm working on creating a basic survey app as I'm learning rails. I've setup a has_many through relationship between the surveys as the questions (as questions may be used in multiple surveys). I've been struggling with adding a question to a survey though. Any idea what I need to do to create a new surveytization when creating my new question (and thus adding the question to the survey)? I'm able to do it in the console but am struggling with translating that to the controllers/views/params - if you know of any good documentation about those I'd love to check them out to (but thus far haven't found much).
It seems to error out when I try to assign my #survey variable using the :survey_id in the params I'm sending to the Question controller.
I really appreciate your help!
Question.rb:
class Question < ActiveRecord::Base
has_many :answers, dependent: :delete_all
validates :title, presence: true
has_many :surveytizations
has_many :surveys, :through => :surveytizations
end
Survey.rb
class Survey < ActiveRecord::Base
has_many :surveytizations
has_many :questions, :through => :surveytizations
end
Surveytization.rb:
class Surveytization < ActiveRecord::Base
has_many :surveys
has_many :questions
validates :survey_id, presence: true
validates :question_id, presence:true
end
SurveyController.rb:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
before_action :set_question
# GET /surveys
# GET /surveys.json
def index
#surveys = Survey.all
end
# GET /surveys/1
# GET /surveys/1.json
def show
end
# GET /surveys/new
def new
#survey = Survey.new
end
# GET /surveys/1/edit
def edit
end
# POST /surveys
# POST /surveys.json
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render action: 'show', status: :created, location: #survey }
else
format.html { render action: 'new' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /surveys/1
# PATCH/PUT /surveys/1.json
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# DELETE /surveys/1
# DELETE /surveys/1.json
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url }
format.json { head :no_content }
end
end
def add_question(question)
surveytizations.create!(question_id: question.id)
end
def remove_question(question)
surveytizations.find_by(question_id: question.id).destroy
end
def find_question(question)
#question = surveytizations.find_by(question_id: question.id)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
def set_question
#question = Question.new
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:title)
end
end
Survey show.html.erb:
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #survey.title %>
</p>
<%= link_to 'Edit', edit_survey_path(#survey) %> |
<%= link_to 'Back', surveys_path %>
<%= link_to "Add Question", new_question_path(:survey_id => #survey.id)%>
QuestionController:
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
# GET /questions
# GET /questions.json
def index
#questions = Question.all
end
# GET /questions/1
# GET /questions/1.json
def show
#answers = #question.answers
end
# GET /questions/new
def new
#question = Question.new
#survey = Survey.find(:survey_id)
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.json
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render action: 'show', status: :created, location: #question }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: 'Question was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
format.html { redirect_to questions_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:title, :single_response, :surveytization)
end
end
One problem is your join relation should have belongs_to instead of has_many, to get the has_many through working:
class Surveytization < ActiveRecord::Base
belongs_to :survey
belongs_to :question
validates :survey_id, presence: true
validates :question_id, presence:true
end
Notice the :survey and :question are singular name in the belongs_to
To Add a question to a survey you can
# create new question or find existing question and store it in #question
#question
# get the survey into #survey
#survey
#survey.questions << #question
This will magically create the surveytization as well. Now that question will belong to that survey.
You Don't even have to call #survey.save! after.