I'm having a problem creating a nested model with establish_connection using another database.
class Restaurant < ActiveRecord::Base
establish_connection :'f7-api'
has_many :sections, dependent: :destroy
has_many :items, through: :sections
accepts_nested_attributes_for :sections, allow_destroy: true
end
class Section < ActiveRecord::Base
establish_connection :'f7-api'
belongs_to :restaurant
has_many :items, dependent: :destroy
has_many :options, through: :items
accepts_nested_attributes_for :items, allow_destroy: true
end
-
PG::ForeignKeyViolation: ERROR: insert or update on table "sections"
violates foreign key constraint "fk_rails_14e0e2a999" DETAIL: Key
(restaurant_id)=(3) is not present in table "restaurants". : INSERT INTO
"sections" ("title_input_id", "restaurant_id", "created_at",
"updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"
The form parameters from the action are (formatted):
{
"utf8"=>"✓", "authenticity_token"=>"FLe24nnI3fITIS4bpMBDjJ0Ne+F0S3Rh9HgjYIqotR3CpbT/gHa0c3iQi0yUtiCQNdNBYi0ANN75fqiZU6japw==",
"restaurant"=>{
"name"=>"asd", "business_name"=>"", "cnpj"=>"", "description"=>"",
"url"=>"", "phone"=>"", "mobile"=>"", "website"=>"",
"sections_attributes"=>{
"1463797768730"=>{"title"=>"", "_destroy"=>"false"}
}
},
"commit"=>"Save Restaurant"
}
restaurants_controller
# POST /restaurants
# POST /restaurants.json
def create
#restaurant = Restaurant.new(restaurant_params)
respond_to do |format|
if #restaurant.save
format.html { redirect_to #restaurant, notice: 'Restaurant was successfully created.' }
format.json { render :show, status: :created, location: #restaurant }
else
format.html { render :new }
format.json { render json: #restaurant.errors, status: :unprocessable_entity }
end
end
end
def restaurant_params
params.require(:restaurant).permit(
:id,
:name,
:business_name,
:cnpj,
:description,
:phone,
:mobile,
:website,
:user_id,
:street,
:complement,
:number,
:lat,
:lng,
:zip_code,
:neighborhood_id,
:city_id,
:state_id,
:country_id,
photos: [:id, :image, :main],
open_hours: [:id, :weekday, :opens_at, :closes_at],
cuisine_ids: [],
category_ids: [],
sections_attributes: [
:id,
:title,
:restaurant_id,
:_destroy,
items_attributes: [
:id,
:title,
{:description => []},
:section_id,
:price_cents,
:not_equal,
:_destroy,
options_attributes: [
:id,
{:description => []},
:item_id,
:price_cents,
:_destroy
]
]
]
)
end
The solution is to remove foreign_key references in postgres database
I dont know why estabilh_connection is breaking this relationship.
Related
I wanted to create a three dropdown selects category>subcategory>susubcategory
I have followed this method here https://www.youtube.com/watch?v=ap551f2a_d0 with some changes to suit my needs. everything seemed fine and each dropdown gets populated based on the selection of the dropdown before but when I select any value in the dropdowns it does not keep it selected it just goes back to the default -Please select-! I am not sure where I went wrong.
note: I only have one module (categories for the categories and using the parant_id for the subcategories)
I would really appreciate it if someone can point me in the right direction. Thank you
my categories table
create_table :categories do |t|
t.string "name", limit: 255
t.text "description", limit: 65535
t.references "parent_id", limit: 4
t.boolean "important", default: false
t.integer "position", limit: 4, default: 0
t.timestamps
end
in modules category.rb
class Category < ApplicationRecord
validates :name, presence: true, uniqueness: true
has_many :jobs
belongs_to :parent_category, foreign_key: :parent_id, class_name: 'Category'
has_many :subcategories, foreign_key: :parent_id, class_name: 'Category'
has_many :subsubcategories, foreign_key: :parent_id, class_name: 'Category'
end
in modules job.rb
class Job < ApplicationRecord
validates :title,:category_id, :description, presence: true
validates :category, :presence => true
belongs_to :user
belongs_to :category , -> { order("name") }
end
dropdown_controller.js
import { Controller } from "#hotwired/stimulus";
// connect to data-controller="dropdown"
export default class extends Controller {
submit () {
this.element.requestSubmit();
}
}
in my jobs_controller.rb
before_action :authenticate_user!, except: [:index, :show]
before_action :set_categories
def index
#jobs = Job.all
end
def new
#categories = Category.where(parent_id: nil)
#subcategory = #subcategory&.category || []
#subcategories = #subcategories&.subcategories || []
#subsubcategories = #subsubcategories&.subsubcategories || []
#job = Job.new
end
def create
#job = Job.new(job_params.merge({ user: current_user }))
if #job.save
format.html { redirect_to root_path, notice: "Job was successfully created." }
else
render :new, status: :unprocessable_entity
end
end
private
def job_params
params.require(:job).permit(:title, :description,:category_id).merge(user: current_user)
end
def set_categories
#category = Category.find_by(id: params[:category].presence)
#categories = Category.find_by(id: params[:category].presence)
#subcategories = Category.find_by(id: params[:category].presence)
#subsubcategories = Category.find_by(id: params[:subcategories].presence)
end
end
views/jobs/new.html.erb
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories, "id" , "name", #category&.id), prompt: "Select a subcategories category" %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories, "id" , "name", #category&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>
I think that you miss update select_tag after request successfully.
you create a new file views/jobs/new.turbo_stream.erb
Ex: The select_tag has category_id
<%= turbo_stream.update "category_id" do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<% end %>
Make sure select_tag selected value with #category&.id (ex: #category&.id = 1)
I done it but i have 2 select_tags.
https://drive.google.com/file/d/1MBY3T0_O2TAE6zFUywDsEoIOjnxr1hMy/view?usp=share_link
P/S: Contact me if you wanna know anymore.
This was the solution to the problem:
my categories table
create_table :categories do |t|
t.string "name", limit: 255
t.text "description", limit: 65535
t.integer "parent_id", limit: 4
t.boolean "important", default: false
t.integer "position", limit: 4, default: 0
t.timestamps
end
in modules category.rb
class Category < ApplicationRecord
validates :name, presence: true
has_many :jobs
belongs_to :parent, foreign_key: :parent_id, class_name: 'Category' , :optional => true
has_many :subcategories, foreign_key: :parent_id, class_name: 'Category'
has_many :subsubcategories, foreign_key: :parent_id, class_name: 'Category'
end
in modules job.rb
class Job < ApplicationRecord
validates :title,:category_id, :description, presence: true
validates :category, :presence => true
belongs_to :user
belongs_to :category , -> { order("name") }
belongs_to :subcategories, class_name: "Category"
belongs_to :subsubcategories, class_name: "Category"
end
dropdown_controller.js
import { Controller } from "#hotwired/stimulus";
// connect to data-controller="dropdown"
export default class extends Controller {
submit () {
this.element.requestSubmit();
}
}
in my jobs_controller.rb
class JobsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
before_action :set_categories
def index
#jobs = Job.all
end
def new
#category = Category.find_by(id: params[:category])
#categories = Category.where(parent_id: nil)
#subcategories = #category.subcategories if #category
#subsubcategories = Category.where(:parent_id => params[:subcategories])
#job = Job.new
end
def create
#job = Job.new(job_params.merge({ user: current_user }))
if #job.save
format.html { redirect_to root_path, notice: "Job was successfully created." }
else
render :new, status: :unprocessable_entity
end
end
private
def job_params
params.require(:job).permit(:title, :description,:category_id).merge(user: current_user)
end
def set_categories
#category = Category.find_by(id: params[:category].presence)
#subcategory = Category.find_by(id: params[:subcategories])
#subsubcategory = Category.find_by(id: params[:subsubcategories])
end
end
views/jobs/new.html.erb
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories || [], "id" , "name", #subcategory&.id), prompt: "Select a subcategories category" %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>
So i have two models : Internship and Review. I want the Review to be a nested_attributs of Internship. So that i can create an internship with a review.
My problem is that the review form integrated in the new form internship, doesn't find the id of the internship. It raise the errors 'Review internship must exist'
Internship.rb
has_many :reviews, foreign_key: "review_internship_id"
has_many :review_users, foreign_key: 'review_user_id', class_name:"User", through: :reviews
accepts_nested_attributes_for :reviews, allow_destroy: true
validates_associated :reviews
Review.rb
belongs_to :review_user, class_name: "User"
belongs_to :review_internship, class_name: "Internship"
Internship_controller
def new
#internship = Internship.new
#internship.reviews.new
end
def create
#internship = Internship.new(internship_params)
#internship.user = current_user
#internship.reviews.first.review_user_id = current_user.id
respond_to do |format|
if #internship.save
format.html { redirect_to #internship, notice: 'Expérience crée avec succès' }
format.json { render :show, status: :created, location: #internship }
else
format.html { render :new }
format.json { render json: #internship.errors, status: :unprocessable_entity }
end
end
end
private
def internship_params
params.require(:internship).permit(:adress, :zipcode, :city, :specialty, :organization, :population, :cursus, :title, :duration, :description, :region, :remuneration, :user_id, reviews_attributes: [:title, :notation, :description, review_internship_id: params[:internship_id], review_user_id: current_user.id])
end
The new form in internship new
<%= form_with(model: internship, local: true) do |form| %>
....
<!--NESTED ATTRIBUTS REVIEW-->
<%= form.fields_for :reviews do |p| %>
<%= p.label :titre %>
<%= p.text_field :title, class:"form-control" %>
<%= p.label :note %>
<%= p.number_field :notation, min: 1, max: 5, class:"form-control" %>
<%= p.label :description %>
<%= p.text_area :description, class:"form-control" %>
<% end %>
...
<% end %>
So this is what i've tried in Internship controller
#intership.reviews.review_internship_id = #internship.id
So that it can find the id of the internship. The error is "Review Internship must exist".
It did the same with "Review User", which was solved with #internship.reviews.first.review_user_id = current_user.id
Do you have any idea where the problem is, and how i can find the internship id with another way. I've also tried the params[:id]
Thanks for your help
:
You don't really need a user_id foreign key on reviews since it can get there through the interview:
class Review
belongs_to :internship
has_one :user, through: :interview
end
class Internship
belongs_to :user
has_many :reviews
end
class User
has_many :internships
has_many :reviews, through: :internships
end
And you definitely don't ever need to be manually assigning parent ids for nested records.
class IntershipsController < ApplicationController
def new
#internship = Internship.new
#internship.reviews.new
end
def create
#internship = Internship.new(internship_params) do |i|
i.user = current_user
end
respond_to do |format|
if #internship.save
format.html { redirect_to #internship, notice: 'Expérience crée avec succès' }
format.json { render :show, status: :created, location: #internship }
else
format.html { render :new }
format.json { render json: #internship.errors, status: :unprocessable_entity }
end
end
end
private
def internship_params
# use some line-breaks!
params.require(:internship)
.permit(
:adress, :zipcode, :city, :specialty,
:organization, :population, :cursus,
:title, :duration, :description,
:region, :remuneration, :user_id,
reviews_attributes: [
:id, :title, :notation, :description
]
)
end
end
I have following associations and the related controller, in my form I am adding every field as it should be. But I still get an error Ratings item can't be blank when I try to create an Item. I am using Rails 4.0 . I did searched extensively for this but could not still find what I am doing wrong. Thankyou!
class Item < ActiveRecord::Base
has_many :ratings, dependent: :destroy
accepts_nested_attributes_for :ratings, :allow_destroy => true
validates :name , :length => { minimum: 3 }
validates :category , :length => { minimum: 3 }
end
class Ratings < ActiveRecord::Base
belongs_to :user
belongs_to :item
default_scope -> { order('created_at DESC') }
validates :user_id, :presence => true
validates :item_id, :presence => true
validates_numericality_of :rating, :greater_than_or_equal_to => 0
validates_numericality_of :rating, :less_than_or_equal_to => 5
end
class ItemsController < ApplicationController
before_action :set_item, only: [:show]
before_action :user_signed_in?, only: :create
def create
#item = Item.new
#rating = #item.ratings.build
#rating.comment = params[:item][:ratings_attributes][:comment]
#rating.rating = params[:item][:ratings_attributes][:rating]
#rating.user_id = current_user.id
#item.name = params[:item][:name]
#item.url = params[:item][:url]
#item.full_address = params[:item][:full_address]
#item.city = params[:item][:city]
#item.country = params[:item][:country]
#item.category = params[:item][:category]
respond_to do |format|
if #item.save
#TODO create rating here (First rating of an Item)
flash[:success] = "Welcome to inmyopnion"
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render action: 'show', status: :created, location: #item }
else
format.html { render action: 'new' }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
def new
#item = Item.new
end
def show
end
def destroy
end
private
def set_item
#item = Item.find(params[:id])
end
def item_params
params.require(:item).permit(:name, :url, :full_address, :city, :country, :category, :ratings_attributes => [:rating, :comment])
end
def user_signed_in?
#TODO: should display should sign in to rate an item
redirect_to(root_url) unless signed_in?
end
end
Simplify your controller! Since you are allowing nested_attributes this should be sufficient:
#item = Item.create(params[:item])
The problem might be caused by #rating object not being saved.
I got it working by commenting the below given line in
class Ratings < ActiveRecord::Base
validates :item_id, :presence => true
but my association rspec test fails and saves a Ratings without an item_id.
Rest of the code is similar to what I posted as
#item = Item.create(params[:item])
gives ActiveModel::ForbiddenAttributesError
Alright much playing with the code and docs of nested_attributes finally a working program that validates association too. These are the changes (marked in between ** .... **) listed below
class Item < ActiveRecord::Base
has_many :ratings, dependent: :destroy, **inverse_of: :item**
accepts_nested_attributes_for :ratings, :allow_destroy => true
validates :name , :length => { minimum: 3 }
validates :category , :length => { minimum: 3 }
end
class Ratings < ActiveRecord::Base
belongs_to :user
belongs_to :item, **inverse_of: :ratings**
default_scope -> { order('created_at DESC') }
validates :user_id, :presence => true
validates_presence_of :item
validates_numericality_of :rating, :greater_than_or_equal_to => 0
validates_numericality_of :rating, :less_than_or_equal_to => 5
end
Still not able to create one from #item = Item.create(params[:item]) which still gives an gives
ActiveModel::ForbiddenAttributesError as suggested by #BroiSatse and also the docs of nested_attributes that should not be the case
the problem might be in
class ItemsController < ApplicationController
def item_params
params.require(:item).permit(:name, :url, :full_address, :city, :country, :category, :ratings_attributes => [:rating, :comment])
end
will work on to resolve that too and post an answer if I find a solution.
I have two models, Trips and Locations. A trip has a origin and a destination which are both references to Location:
class Trip < ActiveRecord::Base
attr_accessible :name, :destination, :origin, :start_datetime, :transportation, :trip_type, :destination_attributes, :origin_attributes
enum_attr :trip_type, %w(Hitchhiker Driver)
has_one :origin, :class_name => 'Location' , :primary_key => :origin, :foreign_key => :id
has_one :destination, :class_name => 'Location' , :primary_key => :destination, :foreign_key => :id
accepts_nested_attributes_for :origin, :allow_destroy => true
accepts_nested_attributes_for :destination, :allow_destroy => true
belongs_to :user
validates_presence_of :name, :destination, :origin, :start_datetime, :transportation, :trip_type
end
class Location < ActiveRecord::Base
attr_accessible :address, :latitude, :longitude
geocoded_by :address
before_validation :geocode
validates_presence_of :address
validate :geocoding_was_found
def geocoding_was_found
errors.add(:address, 'is not valid') if latitude.nil? || longitude.nil?
end
end
When calling create in the controller, it saves all of the records (two location records and the trips record) but does not save the association.
def create
#trip = Trip.new(params[:trip])
#trip.user = current_user
respond_to do |format|
if #trip.save
format.html { redirect_to #trip, notice: 'Trip was successfully created.' }
format.json { render json: #trip, status: :created, location: #trip }
else
format.html { render action: "new" }
format.json { render json: #trip.errors, status: :unprocessable_entity }
end
end
end
I am using nested forms so the data for location is being passed in through origin_attributes and destination_attributes. My guess is it is because the two fields are called origin and destination instead of _id.
The button_to is not passing the correct id to the line_item. In the log below you see the bike_id change from the correct '86' to the incorrect '1' (which coincidentally is my user_id). Any help would be appreciated. Below is the error from my development.log, then the code from my view and controllers. Thanks.
development.log
Started POST "/line_items?bike_id=86" for 127.0.0.1 at 2011-08-01 18:09:52 -0400
DEPRECATION WARNING: Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead. (called from <class:ApplicationController> at /Users/willdennis/rails_projects/spinlister/app/controllers/application_controller.rb:8)
Processing by LineItemsController#create as HTML
Parameters: {"authenticity_token"=>"5GYQqvf7U5awhLrZ9Aw910ETf2kqOk3PI315jkjEfMU=", "bike_id"=>"86"}
[1m[35mCart Load (0.6ms)[0m SELECT "carts".* FROM "carts" WHERE ("carts"."id" = 8) LIMIT 1
[1m[36mBike Load (1.2ms)[0m [1mSELECT "bikes".* FROM "bikes" WHERE ("bikes"."id" = 86) ORDER BY bikes.created_at DESC LIMIT 1[0m
[1m[35mSQL (0.5ms)[0m INSERT INTO "line_items" ("bike_id", "cart_id", "created_at", "updated_at") VALUES (1, 8, '2011-08-01 22:09:53.208978', '2011-08-01 22:09:53.208978')
[1m[36mCart Load (1.5ms)[0m [1mSELECT "carts".* FROM "carts" WHERE ("carts"."id" = 8) LIMIT 1[0m
Redirected to http://localhost:3000/carts/8
Completed 302 Found in 251ms
line_items_controller.rb
def create
#cart = current_cart
#bike = Bike.find(params[:bike_id])
#line_item = #cart.line_items.build(:bike_id => #bike)
respond_to do |format|
if #line_item.save
format.html { redirect_to(#line_item.cart,
:notice => 'Line item was successfully created.') }
format.xml { render :xml => #line_item,
:status => :created, :location => #line_item }
else
format.html { render :action => "new" }
format.xml { render :xml => #line_item.errors,
:status => :unprocessable_entity }
end
end
end
views/bikes/show
<%= button_to "Rent this Bicycle!", line_items_path(:bike_id => #bike), {:id => "rentthisbike"} %>
bike.rb
class Bike < ActiveRecord::Base
belongs_to :user
has_many :line_items
attr_accessible :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood, :bike_id
end
line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :bike
belongs_to :cart
accepts_nested_attributes_for :bike, :cart
attr_accessible :bike_id, :bike, :cart, :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
cart.rb
class Cart < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :line_items
attr_accessible :bike_id, :line_items, :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
Can you try this code and post the #### Line item attributes entry from your log file along with the params hash
I think this may be related to your current_cart method but I'm not sure
line_items_controller.rb
def create
#bike = Bike.find(params[:bike_id])
#line_item = current_cart.line_items.build
#line_item.bike = #bike
logger.debug("#### Line item attributes = #{#line_item.inspect}")
respond_to do |format|
if #line_item.save
format.html { redirect_to(#line_item.cart,
:notice => 'Line item was successfully created.') }
format.xml { render :xml => #line_item,
:status => :created, :location => #line_item }
else
format.html { render :action => "new" }
format.xml { render :xml => #line_item.errors,
:status => :unprocessable_entity }
end
end
end
Update.
Your previous code was fine except for this line
#line_item = #cart.line_items.build(:bike_id => #bike)
You were supplying the whole class as the value for the bike ID instead of the id of the bike. I know this is inconsistent with passing form parameters but that's just the way it is.