Create action in controller is omitting reference field - ruby-on-rails

I have a problem with my rails application when i create a customer for an environment, the action itself if omitting the parameter environment_id in the insert statment.
This is my customer.rb model:
class Customer < ActiveRecord::Base
belongs_to :environment, inverse_of: :customers
has_many :all_services, class_name: 'Service'
has_many :services, inverse_of: :customer
has_paper_trail ignore: %i[created_at updated_at]
end
This is my environment.rb model:
class Environment < ActiveRecord::Base
has_many :all_customers, class_name: 'Customer', dependent: :destroy
has_many :customers, inverse_of: :environment
has_many :all_services, class_name: 'Service', dependent: :destroy
has_many :services, inverse_of: :environment
has_many :all_versions, class_name: 'Version', dependent: :destroy
has_many :versions, inverse_of: :environment
has_many :all_role_permissions, class_name: 'RolePermission', dependent: :destroy
has_many :role_permissions, inverse_of: :environment
has_paper_trail ignore: %i[created_at updated_at]
end
This is the customer_controller.rb create action:
def create
if customer_params.permitted?
render json: Customer.create!(customer_params), status: :ok
else
render json: { message: Api::V1::INVALID_PARAMETERS }, status: :bad_request
end
end
def customer_params
params.require(:customer).permit(:environment_id, :full_name, :document_type, :document_value, :customer_type)
end
And this is the server log:
Started POST "/api/v1/customers" for 127.0.0.1 at 2021-06-02 18:18:44 +0100
Processing by Api::V1::CustomersController#create as HTML
Parameters: {"full_name"=>"cvbcv", "document_type"=>"bcvbc", "document_value"=>"vbcvbcv", "customer_type"=>"bcvbcvb", "environment_id"=>1, "customer"=>{"full_name"=>"cvbcv", "document_type"=>"bcvbc", "document_value"=>"vbcvbcv", "customer_type"=>"bcvbcvb", "environment_id"=>1}}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."uid" = $1 LIMIT $2 [["uid", "test1#test.cl"], ["LIMIT", 1]]
(0.5ms) BEGIN
↳ app/services/api/customers.rb:20:in `create_customer'
Environment Load (0.5ms) SELECT "environments".* FROM "environments" WHERE "environments"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/services/api/customers.rb:20:in `create_customer'
Customer Create (1.4ms) INSERT INTO "customers" ("full_name", "document_type", "document_value", "customer_type", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["full_name", "cvbcv"], ["document_type", "bcvbc"], ["document_value", "vbcvbcv"], ["customer_type", "bcvbcvb"], ["created_at", "2021-06-02 17:18:44.378509"], ["updated_at", "2021-06-02 17:18:44.378509"]]
↳ app/services/api/customers.rb:20:in `create_customer'
(1.2ms) ROLLBACK
↳ app/services/api/customers.rb:20:in `create_customer'
Completed 500 Internal Server Error in 22ms (ActiveRecord: 4.3ms | Allocations: 6384)
ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR: null value in column "environment_id" violates not-null constraint
DETAIL: Failing row contains (28, cvbcv, bcvbc, vbcvbcv, bcvbcvb, null, 2021-06-02 17:18:44.378509, 2021-06-02 17:18:44.378509).
):
I tried everything i can possible know, i triple checked the migrations and schema.
I also checked active record gem version from previous projects and everything is ok.
One thing that might be relevant, this that the project have a front-end in Angular.

Rails validates for Environment presence in the database because Customer belongs_to to it.
Query below in your log is trying to load Environment with ID=1:
Environment Load (0.5ms) SELECT "environments".* FROM "environments" WHERE "environments"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
After this Rails try to INSERT new Customer without Environment reference because it seems that Environment with ID=1 doesn't exist in the database.

multiple ways of solving it.
problem: the environment_id is nil and already as not nil defined in the postgres DB.
you can add a required: true to the association, which rails then checks for existence. in your case a validation error would throw.
you can add a validates :enviroment_id, presence: true which would do the same
you merge the id into the params
example to always merge the environment_id into the params
def customer_params
params.require(:customer).permit(:full_name, :document_type, :document_value, :customer_type).merge(environment_id: GET_ME_THE_ENV_ID)
end

Related

ActiveAdmin: has_many associative records are commiting changes before parent object on patch failure

I'm having an issue with Rails ActiveAdmin (quite a novice myself still) where a has_many association (CampaignCountry) is deleting/adding records incorrectly when its select input field :countries is modified and when the parent object (Campaign) fails due to any validation errors:
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
CampaignCountry Destroy (0.3ms) DELETE FROM "campaign_countries" WHERE "campaign_countries"."campaign_id" = $1 AND "campaign_countries"."country_id" = $2 [["campaign_id", 53], ["country_id", 1]]
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
(0.6ms) COMMIT
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
(0.1ms) BEGIN
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
Campaign Exists (0.3ms) SELECT 1 AS one FROM "campaigns" WHERE "campaigns"."vanity_url" = $1 AND "campaigns"."id" != $2 LIMIT $3 [["vanity_url", "dummycampaign"], ["id", 53], ["LIMIT", 1]]
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
(0.1ms) ROLLBACK
as you can see, CampaignCountry delete is still occuring despite the parent objects eventual ROLLBACK in this example. Ideally, I want the CampaignCountry records to only update after the parent Campaign validations are run and it is found to be valid. Below are the association rules between them:
# Campaign model:
has_many :campaign_countries, dependent: :destroy
has_many :countries, through: :campaign_countries
# CampaignCountry model:
belongs_to :campaign, touch: true
belongs_to :country
# Campaign ActiveAdmin countries field:
permit_params ... country_ids: []
...
f.input :countries, as: :select, multiple: true, collection: Country.pluck(:alpha_code, :id)
Any help is appreciated in understanding why this is occuring as well as a potential fix. Let me know if supplemental information is needed.

Rails Eager loading not loading nested associative records though included

I have three classes as follows
Class User < ActiveRecord
has_many :addresses
end
Class Address < ActiveRecord
belongs_to :country
end
Class Country < ActiveRecord
has_many :addresses
end
I am trying to eager load all the nested associative records by the following command
User.includes(addresses: :country)
But with this on rails console, only User and addresses get loaded but not the country. I am not sure what I am missing.
You need to use a hash to declare eager loading of nested resources, e.g.
User.includes(addresses: [:country])
instead of
User.includes(addresses: :country)
See the docs
Here's a full working example (Rails 6)
class Person < ApplicationRecord
has_many :projects
end
class Company
has_many :projects
end
class Project < ApplicationRecord
belongs_to :person
belongs_to :company
end
Then, on the console:
ActiveRecord::Base.logger = Logger.new(STDOUT)
me = Person.includes(projects: [:company]).find 4
Person Load (0.3ms) SELECT "people".* FROM "people" WHERE "people"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
Project Load (0.4ms) SELECT "projects".* FROM "projects" WHERE "projects"."person_id" = $1 [["person_id", 4]]
Company Load (0.5ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" IN ($1, $2, $3, $4, $5 ...)
me.projects.first.company.name
=> "my first project's customer name"
Note that there is no query to the database between asking for the name of the customer of my first project and the production of the return value. If there would have been one, the query would have been printed by the logger that pipes it output to STDOUT.

can't write unknown attribute "parent_id", has_one and belongs_to

i'm using rails 4 and was wondering if anyone could find what's wrong in my code.
I have project model and I created a team model that has a belongs_to - has_one relation with project.
project model:
class CrmProject < ActiveRecord::Base
has_one :crm_team
team model
class CrmTeam < ActiveRecord::Base
belongs_to :crm_project
accepts_nested_attributes_for :crm_project
belongs_to :crm_section
belongs_to :manager, class_name: "User"
has_many :users
accepts_nested_attributes_for :users
end
When submitting the form to create a new team i get this error:
ActiveModel::MissingAttributeError in CrmTeamsController#create
can't write unknown attribute `crm_team_id`
and log from server :
Parameters: {"crm_team"=>{"crm_project"=>"5", "manager"=>"3", "user_ids"=>["", "2"]}, "co
mmit"=>"Create Crm team"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1
[["id", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
(0.1ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'email#gmail.com' AND "users"."
id" != 2) LIMIT 1
SQL (0.2ms) INSERT INTO "crm_teams" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2020-03-1
1 07:29:19.735115"], ["updated_at", "2020-03-11 07:29:19.735115"]]
(0.2ms) rollback transaction
Completed 500 Internal Server Error in 9ms (ActiveRecord: 1.0ms)
When you use a has_one relation like this
has_one :crm_team
Rails expects you to add crm_team_id to your crm_project, in order to understand which crm_team is related with crm_project object. Adding it going to solve your problem.
In my opinion defining opposite like this much better in logic. Because in future these teams can have multiple projects.
class CrmProject < ActiveRecord::Base
belongs_to :crm_team
class CrmTeam < ActiveRecord::Base
***has_one/has_many(pick one)*** :crm_project
accepts_nested_attributes_for :crm_project
belongs_to :crm_section
belongs_to :manager, class_name: "User"
has_many :users
accepts_nested_attributes_for :users
end

CounterCache not working for polymorphic association

I am implementing counter_cache concept to my Forums app. It is working fine for one model that has a simple belongs_to association but not working for a polymorphic association.
My app structure is like this. I have 3 models, Forum, Post and Comment.
class Forum < ApplicationRecord
has_many :posts
end
Post Model:
class Post < ApplicationRecord
belongs_to :forum, counter_cache: true
has_many :comments, as: :parent
end
Comment Model:
class Comment < ApplicationRecord
belongs_to :parent,polymorphic: true, counter_cache: true
has_many :comments, as: :parent
end
My Comments Model is basically a polymorphic one, so a comment can belong to a post or a comment can belong to another comment (in this way it will be considered a reply of a comment)
I have a posts_count field in Forum model which is working fine and auto-incrementing and decrementing is working.
I also have a comments_count field in Post model.
Whenever a new comment is created, the comments_count field is incremented in the associated Post.
But when I try to create a comment whose parent (polymorphic association) is another comment (so basically a reply of a comment), I hit an error:
Started POST "/comments" for 103.255.4.86 at 2018-10-18 20:48:39 +0000
Cannot render console from 103.255.4.86! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by CommentsController#create as JS
Parameters: {"utf8"=>"✓", "comment"=>{"body"=>"testing a reply", "parent_id"=>"812", "parent_type"=>"Comment"}}
Post Load (0.8ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = $1 LIMIT $2 [["id", 7], ["LIMIT", 1]]
(0.4ms) BEGIN
Comment Load (1.6ms) SELECT "comments".* FROM "comments" WHERE "comments"."id" = $1 LIMIT $2 [["id", 812], ["LIMIT", 1]]
SQL (0.9ms) INSERT INTO "comments" ("body", "parent_type", "parent_id", "owner_type", "owner_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["body", "testing a reply"], ["parent_type", "Comment"], ["parent_id", 812], ["owner_type", "User"], ["owner_id", 46], ["created_at", "2018-10-18 20:48:39.141170"], ["updated_at", "2018-10-18 20:48:39.141170"]]
(0.4ms) ROLLBACK
Completed 500 in 43ms (ActiveRecord: 7.2ms)
ActiveModel::MissingAttributeError (can't write unknown attribute `comments_count`):
What am I missing here? Any hint would be really appreciated, Thanks!!
I have got it working by removing counter_cache: true from Comment model and defining my own methods for increment and decrement of the counter in Comment model. So here is my final Comment model:
class Comment < ApplicationRecord
belongs_to :parent,polymorphic: true,touch: true
has_many :comments,dependent: :destroy,as: :parent
after_create_commit { self.parent_post.increment!(:answers_count,1) }
after_destroy { self.parent_post.decrement!(:answers_count,1) }
def parent_post
(self.parent if parent_type == "Post") || self.parent.parent
end
end
If anyone comes up with another answer please post it here. Thanks.
The counter_culture counter cache gem supports polymorphic associations natively: https://github.com/magnusvk/counter_culture

Records Won't Insert into Join Table for has_many :through Association

I have 2 models: User and Collective, along with their join table, Membership. When a user creates a new collective, I want to simultaneously create a new membership between the two. Right now I have my create function partially working, in that the collective 'new' form adds a collective record to my db but no membership record is added.
User Model
class User < ActiveRecord::Base
has_many :collectives, :through => :memberships
has_many :memberships
end
Collective Model
class Collective < ActiveRecord::Base
has_many :users, :through => :memberships
has_many :memberships
end
Membership Model
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :collective
validates :user_id, presence: true
validates :collective_id, presence: true
end
Collective Controller
def new
#collective = Collective.new if user_signed_in?
end
def create
#collective = Collective.new(collective_params)
if #collective.save
current_user.memberships.create(collective_id: #collective)
flash[:success] = "Collective created!"
redirect_to collective_url(#collective)
else
render 'new'
end
end
private
def collective_params
params.require(:collective).permit(:name, :description, :location, :num_of_users, :num_of_projects)
end
end
Log
Started POST "/collectives" for 127.0.0.1 at 2014-11-06 10:49:39 -0500
Processing by CollectivesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Dh/rG1N6ulrJSIiEaAgudnaltjxnKwCw5sdUQxG9qnE=", "collective"=>{"name"=>"Honda Civic", "location"=>"Cars", "description"=>"Blaahhhhh"}, "commit"=>"Create Collective"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "collectives" ("created_at", "description", "location", "name", "num_of_projects", "num_of_users", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["created_at", "2014-11-06 15:49:40.003338"], ["description", "Blaahhhhh"], ["location", "Cars"], ["name", "Honda Civic"], ["num_of_projects", 0], ["num_of_users", 1], ["updated_at", "2014-11-06 15:49:40.003338"]]
(145.3ms) commit transaction
(0.0ms) begin transaction
(0.1ms) commit transaction
Redirected to http://localhost:3000/collectives/7
Completed 302 Found in 208ms (ActiveRecord: 146.7ms)
It seems like the line current_user.memberships.create(collective_id: #collective) is just being ignored for some reason given that the redirect happens fine. Help would be much appreciated. Thanks in advance!
I would say the problem is with passing instance object instead of id. It should be:
if #collective.save
current_user.memberships.create(collective_id: #collective.id)
flash[:success] = "Collective created!"
redirect_to collective_url(#collective)
else
render 'new'
end
Since create method only returns true/false (false in this case because of validation) and does not raise any exception, it gets redirected into show action.

Resources