action update only last object - ruby-on-rails

From last two days i try expect whats going on in my app. This is my problem:
My model:
class User
has_many :orders
---
class Order
belongs_to :user
has_and_belongs_to_many :contributors, class_name: 'User'
Situation:
User can create order, next for this order can add contributors(other users) with
<%= collection_check_boxes(:order, :contributor_ids, #prac, :id, :to_s) %>
After that in my custom action in OrdersController i have something like this:
#order.contributors.each do |u|
u.orders << #order
end
Here i want add this order to list of contributors(users) orders. Unfortunately this work only for last contributor(user). Below log from my console:
Started PATCH "/orders/282/zatwierdz" for 127.0.0.1 at 2014-07-01 08:48:59 +0200
Processing by OrdersController#zatwierdz as HTML
Parameters: {"authenticity_token"=>"hxWUnazKLoS9t8bVmOAMeIxdo/KYuO33bUeuQZYgeV
k=", "id"=>"282"}
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 32 OR
DER BY "users"."id" ASC LIMIT 1
Role Load (0.0ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = ? LIMI
T 1 [["id", 4]]
Order Load (0.0ms) SELECT "orders".* FROM "orders" WHERE "orders"."id" = ?
ORDER BY created_at DESC LIMIT 1 [["id", 282]]
OrderItem Exists (0.0ms) SELECT 1 AS one FROM "order_items" WHERE "order_it
ems"."order_id" = ? LIMIT 1 [["order_id", 282]]
(0.0ms) begin transaction
SQL (0.0ms) UPDATE "orders" SET "status" = ?, "updated_at" = ? WHERE "orders"
."id" = 282 [["status", 1], ["updated_at", "2014-07-01 06:48:59.486472"]]
(62.5ms) commit transaction
User Load (0.0ms) SELECT "users".* FROM "users" INNER JOIN "orders_users" ON
"users"."id" = "orders_users"."user_id" WHERE "orders_users"."order_id" = ? [["
order_id", 282]]
(0.0ms) begin transaction
(0.0ms) commit transaction
(0.0ms) begin transaction
SQL (0.0ms) UPDATE "orders" SET "updated_at" = ?, "user_id" = ? WHERE "orders
"."id" = 282 [["updated_at", "2014-07-01 06:48:59.564595"], ["user_id", 33]]
(78.1ms) commit transaction
(0.0ms) begin transaction
SQL (0.0ms) UPDATE "orders" SET "updated_at" = ?, "user_id" = ? WHERE "orders
"."id" = 282 [["updated_at", "2014-07-01 06:48:59.642719"], ["user_id", 35]]
(78.1ms) commit transaction
(0.0ms) begin transaction
SQL (0.0ms) UPDATE "orders" SET "updated_at" = ?, "user_id" = ? WHERE "orders
"."id" = 282 [["updated_at", "2014-07-01 06:48:59.720842"], ["user_id", 36]]
(109.4ms) commit transaction
(0.0ms) begin transaction
SQL (0.0ms) UPDATE "orders" SET "updated_at" = ?, "user_id" = ? WHERE "orders
"."id" = 282 [["updated_at", "2014-07-01 06:48:59.830215"], ["user_id", 37]]
(78.1ms) commit transaction
Redirected to http://localhost:3000/orders/282
Completed 302 Found in 437ms (ActiveRecord: 406.2ms)
I try use this action with callbacks and from model methods but nothing happened. Maybe better for this aprouch is to use other associations? Anny sugestions welcome.
Thx for help.

By doing:
#order.contributors.each do |u|
u.orders << #order
end
You are adding your order to the user.orders relationship, which you define as a many (Order) to one (User). The "has_many" orders on the User class is just the other side of the "belongs_to" user on the Order class. So yes, as per your model, an Order has one and only one user.
Now, if you want to be able to navigate your has_and_belongs_to_many relation from the User side, you need to define it there too.

Related

will_paginate deletes posts in a group when posts are paginated

I'm using will_paginate to paginate posts within a group.
A group might have many posts and I don't want to show all posts at once.
For some strange reason I noticed that a lot of posts are deleted from the database when I do #group.posts = paginated_posts as displayed in * groups_controller*.
I'm not doing save on #group, why are the posts deleted?
Tests first
69 it "Pagination – get one group and its posts" do
70 10.times { Fabricate(:post, group: #group, user: #user) }
71 puts "POST COUNT BEFORE #{#group.posts.count}"
72 # byebug
73 get group_path(#group, posts_per_page: 3)
74 puts "POST COUNT AFTER #{#group.posts.count}"
75 expect(response).to have_http_status(200)
76 expect(#group).to be_present
77 end
Output from the test
Groups
GET /group/:group_id?posts_per_page=10&posts_page=2
POST COUNT BEFORE 12
POST COUNT AFTER 3
groups_controller.rb
9 def show
10 paginated_posts = #group.posts.paginate(
11 page: params[:posts_page],
12 per_page: params[:posts_per_page] || 100000,
13 )
14 #group.posts = paginated_posts
15 render json: #group
16 end
The log
(0.3ms) RELEASE SAVEPOINT active_record_1
Started GET "/groups/33?posts_per_page=3" for 127.0.0.1 at 2018-09-18 12:52:13 +0000
Processing by GroupsController#show as HTML
Parameters: {"posts_per_page"=>"3", "id"=>"33"}
User Load (2.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", "78913bd2-4c72-4c73-ba75-421e154c830b"], ["LIMIT", 1]]
Group Load (1.4ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = $1 LIMIT $2 [["id", 33], ["LIMIT", 1]]
Post Load (2.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."group_id" = $1 LIMIT $2 OFFSET $3 [["group_id", 33], ["LIMIT", 3], ["OFFSET", 0]]
Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."group_id" = $1 [["group_id", 33]]
(0.4ms) SAVEPOINT active_record_1
Comment Load (0.5ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = $1 [["post_id", 156]]
PostImage Load (0.3ms) SELECT "post_images".* FROM "post_images" WHERE "post_images"."post_id" = $1 [["post_id", 156]]
Post Destroy (0.3ms) DELETE FROM "posts" WHERE "posts"."id" = $1 [["id", 156]]
Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = $1 [["post_id", 157]]
PostImage Load (0.3ms) SELECT "post_images".* FROM "post_images" WHERE "post_images"."post_id" = $1 [["post_id", 157]]
Post Destroy (0.3ms) DELETE FROM "posts" WHERE "posts"."id" = $1 [["id", 157]]
Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = $1 [["post_id", 158]]
#group.posts = ... doesn't work the way you think it does. If you have a one-to-many relationship and you assign an array of associated records to the existing group object, the records set and remove the group.id foreign key immediately. No save is required.
So when you do
#group.posts = paginated_posts
The paginated posts are assigned to the association and all other records in the association are removed from the association. Instantly. They're not necessarily deleted, but they no longer belong to #post
You may want to change your #to_json ... perhaps add
attr_accessor :page_of_posts
And then in your controller
#group.page_of_posts = paginated_posts
And ensure page_of_posts is what's rendered in the json. Others may suggest a better way.

Rails multi-level relation destroyed when updating

I have a four-level deep model structure: Domain > Subject > Device > Property
class Domain < ApplicationRecord
has_many :subjects
end
class Subject < ApplicationRecord
has_many :devices
belongs_to: :domain
end
class Device < ApplicationRecord
has_many :properties
belongs_to: :subject
end
class Property < ApplicationRecord
belongs_to :device
end
controller code
def update
result = #subject.update(parameters)
if result
render json: #subject
else
render_errors(#subject.errors)
end
end
#subject is retrieved as a before action, by querying the model tree from domain onwards, using domain_id and id parameters for domain and subject respectively. parameters is simply a hash of parameters, e.g. {name: :new_name}
When updating a Subject, the relation to domain is lost, i.e. domain_id is set to NUL by rails. The entire model tree below subject will also be disconnected from the parent domain as a result.
When removing has_many: :devices from the Subject model, everything works as expected. I just want to update a subject and preserve the relation to the parent domain. How would I achieve this with the model described above?
EDIT 1 - Added log of both situations.
Log with full relational model (that results in the bug)
Domain Load (0.5ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
CACHE (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
Subject Load (0.0ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."domain_id" = ? AND "subjects"."name" = ? LIMIT ? [["domain_id", 3], ["name", "s1"], ["LIMIT", 1]]
Subject Load (0.5ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
(0.0ms) begin transaction
Domain Load (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
SQL (7.1ms) UPDATE "subjects" SET "domain_id" = NULL WHERE "subjects"."domain_id" = ? AND "subjects"."id" = 5 [["domain_id", 3]]
Subject Exists (0.0ms) SELECT 1 AS one FROM "subjects" WHERE "subjects"."domain_id" = ? AND "subjects"."name" = ? LIMIT ? [["domain_id", 3], ["name", "s2"], ["LIMIT", 1]]
SQL (1.0ms) UPDATE "subjects" SET "name" = ?, "updated_at" = ? WHERE "subjects"."id" = ? [["name", "s2"], ["updated_at", "2017-07-31 08:46:38.171240"], ["id", 5]]
(7.5ms) commit transaction
Subject Load (0.0ms) SELECT "subjects".* FROM "subjects"
Completed 200 OK in 30ms (Views: 0.5ms | ActiveRecord: 16.6ms)
Log when removing belongs_to: :devices from Subject model
Domain Load (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
CACHE (0.0ms) SELECT "domains".* FROM "domains" WHERE "domains"."name" = ? LIMIT ? [["name", "Manatree"], ["LIMIT", 1]]
Subject Load (0.5ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."domain_id" = ? AND "subjects"."name" = ? LIMIT ? [["domain_id", 3], ["name", "s3"], ["LIMIT", 1]]
Subject Load (0.5ms) SELECT "subjects".* FROM "subjects" WHERE "subjects"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
(0.0ms) begin transaction
Domain Load (0.5ms) SELECT "domains".* FROM "domains" WHERE "domains"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
SQL (6.5ms) UPDATE "subjects" SET "name" = ?, "updated_at" = ? WHERE "subjects"."id" = ? [["name", "s4"], ["updated_at", "2017-07-31 08:48:57.962218"], ["id", 6]]
(7.0ms) commit transaction
Subject Load (0.0ms) SELECT "subjects".* FROM "subjects"
Completed 200 OK in 25ms (Views: 0.4ms | ActiveRecord: 15.1ms)
Edit 2 - There might be something wrong with the seed data...
domainA = Domain.create(name: :company)
s1 = domainA.subjects.create(name: :subject)
# domainA.save
d1 = s1.devices.create(name: :device)
# s1.save
p1 = d1.properties.create(name: :prop1, property_type: :double, value: 10.0)
p2 = d1.properties.create(name: :prop2, property_type: :string, value: :on)
p3 = d1.properties.create(name: :prop3, property_type: :string, value: :Lamp)
# d1.save
domainA.save
When removing has_many: :devices from the Subject model, everything
works as expected.
Going on with your models, Your Device model is flawed. You have belongs_to :device inside Device. Perhaps you should have belongs_to :subject as per the associations. This could have lead to your current problem. Try changing the Device model like so
class Device < ApplicationRecord
has_many :properties
belongs_to: :subject
end

Updating an attribute in Rails controller

I'm trying to update the shop_id of a user when they click an add button.
The controller method called is this:
def update_shop
#user = User.find(params[:id])
#shop = Shop.find(params[:shop_id])
#user.update_attribute(:shop_id, params[:shop_id])
flash[:success] = "Added Shop!"
redirect_to #shop
end
The server when the button is clicked reads:
Started POST "/updateshop?id=3&shop_id=1" for ::1 at 2015-08-31 05:50:52 -0500
Processing by UsersController#update_shop as HTML
Parameters: {"authenticity_token"=>"jRozldw1u3TrWhaL6CeJyw4Tm5V5S/IFEQQulRkuV1Ot85kmPOsMa2jH2L6m8EFDpy7Ygc9SMBvPLJCuosHXUg==", "id"=>"3", "shop_id"=>"1"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 3]]
Shop Load (0.1ms) SELECT "shops".* FROM "shops" WHERE "shops"."id" = ? LIMIT 1 [["id", 1]]
(0.1ms) begin transaction
SQL (0.3ms) UPDATE "users" SET "shop_id" = ?, "updated_at" = ? WHERE "users"."id" = ? [["shop_id", 1], ["updated_at", "2015-08-31 10:50:52.089826"], ["id", 3]]
(5.8ms) commit transaction
But it doesn't actually update User.shop_id
And sometimes it doesn't have the update line, and reads:
Started POST "/updateshop?id=3&shop_id=1" for ::1 at 2015-08-31 05:56:54 -0500
Processing by UsersController#update_shop as HTML
Parameters: {"authenticity_token"=>"D1ODlfDnhJmQ9NUfh+GL2JE747nJC2t4eqOziRGNCaUvuikmEDkzhhNpGyrJNkNQOAagrX8SqWakiw2yqmKJpA==", "id"=>"3", "shop_id"=>"1"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 3]]
Shop Load (0.1ms) SELECT "shops".* FROM "shops" WHERE "shops"."id" = ? LIMIT 1 [["id", 1]]
(0.1ms) begin transaction
(0.1ms) commit transaction
The parameters are passed correctly and I think update_attribute is correct, what's going wrong?
The value is already assigned, this is why it's not updated. I mean, there is no actual changes, this is why no UPDATE statements were issued.

Ruby on Rails: dependent object destroyed when transfered from guest user to registered user

Here is my problem:
I'm using Devise's guest_user, that contains a logging_in method to transfer guest_user parameters to the registered user when he logs in. So in my case, the user has_many periods, dependent: :destroy, so here is the logging_in method:
def logging_in
guest_periods = guest_user.periods.all
guest_periods.each do |p|
p.user_id = current_user.id
p.save!
end
current_user.latest_entry = guest_user.latest_entry
current_user.is_in_zone = guest_user.is_in_zone
current_user.save
end
However, when a guest_user logs in, his periods gets destroyed instead of being transfered. Here is the log:
Started GET "/" for ::1 at 2015-05-11 00:18:03 +0300
Processing by WelcomeController#index as HTML
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 24]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 23]]
Period Load (0.3ms) SELECT "periods".* FROM "periods" WHERE "periods"."user_id" = $1 [["user_id", 23]]
(0.2ms) BEGIN
CACHE (0.0ms) SELECT "periods".* FROM "periods" WHERE "periods"."user_id" = $1 [["user_id", 23]]
SQL (0.8ms) UPDATE "periods" SET "user_id" = $1, "updated_at" = $2 WHERE "periods"."id" = $3 [["user_id", 24], ["updated_at", "2015-05-10 21:18:03.863162"], ["id", 170]]
(0.9ms) COMMIT
(0.2ms) BEGIN
SQL (2.1ms) UPDATE "users" SET "is_in_zone" = $1, "latest_entry" = $2, "updated_at" = $3 WHERE "users"."id" = $4 [["is_in_zone", "t"], ["latest_entry", "2015-05-04"], ["updated_at", "2015-05-10 21:18:03.875572"], ["id", 24]]
(15.8ms) COMMIT
(0.5ms) BEGIN
SQL (0.3ms) DELETE FROM "periods" WHERE "periods"."id" = $1 [["id", 170]]
SQL (0.7ms) DELETE FROM "users" WHERE "users"."id" = $1 [["id", 23]]
(1.2ms) COMMIT
So we can see that the transfer is done, but then in the end, the periods are destroyed anyway. They should not be, as they are not belonging to the user to be destroyed any more.
Why is it happening?
Even though Period#user_id has changed, guest_user.periods is still loaded in memory and is what gets destroyed when you destroy the guest user. If you guest_user.reload, its associations will clear out and it becomes safe to destroy. You could also guest_user.periods(true) to force reload of just the periods.
Another option is:
guest_user.periods.update_all(user_id: current_user.id)
This executes a single query to perform the update, which will be nice if there are a lot of periods, and also doesn't load the guest_user.periods association, so it will load fresh during the destroy and find the correct empty set.

Failed to update email address using devise only in production environment

This one is driving me absolutely crazy. I tested updating user's email address on dev and staging environments, and it all worked fine. It updates unconfirmed_email field and sends out a confirmation email address.
However, only in production environment, it fails!
In dev/staging environment, I see the following statements when a user updates his/her email address.
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 LIMIT 1
Profile Load (0.3ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = 2 LIMIT 1
(0.1ms) BEGIN
User Exists (0.6ms) SELECT 1 FROM "users" WHERE ("users"."email" = 'aaaa#yahoo.com' AND "users"."id" != 2) LIMIT 1
(0.3ms) UPDATE "users" SET "unconfirmed_email" = 'aaaa#yahoo.com', "updated_at" = '2012-08-27 04:22:10.470329' WHERE "users"."id" = 2
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."confirmation_token" = 'jUcfXqMqDjeEsJ3TEKws' LIMIT 1
(0.3ms) UPDATE "users" SET "unconfirmed_email" = 'aaaa#yahoo.com', "updated_at" = '2012-08-27 04:22:10.470329', "confirmation_token" = 'jUcfXqMqDjeEsJ3TEKws', "confirmation_sent_at" = '2012-08-27 04:22:10.473264' WHERE "users"."id" = 2
However, in production, I see the following.
User Load (6.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = 5 LIMIT 1
Profile Load (2.1ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = 5 LIMIT 1
(1.2ms) BEGIN
User Exists (1.3ms) SELECT 1 FROM "users" WHERE ("users"."email" = 'aaaa#yahoo.com' AND "users"."id" != 5) LIMIT 1
(1.2ms) ROLLBACK
One more odd thing. I could update the email address manually in rails console.....
1.9.3p194 :005 > u.update_attributes(:email => "aaaa#yahoo.com")
(1.3ms) BEGIN
User Exists (1.4ms) SELECT 1 FROM "users" WHERE ("users"."email" = 'aaaa#yahoo.com' AND "users"."id" != 1) LIMIT 1
(1.4ms) UPDATE "users" SET "unconfirmed_email" = 'aaaa#yahoo.com', "updated_at" = '2012-08-27 05:05:26.337961' WHERE "users"."id" = 1
User Load (1.4ms) SELECT "users".* FROM "users" WHERE "users"."confirmation_token" = 'Yyg98zno81adJt4mp7pG' LIMIT 1
(1.4ms) UPDATE "users" SET "unconfirmed_email" = 'aaaa#yahoo.com', "updated_at" = '2012-08-27 05:05:26.337961', "confirmation_token" = 'Yyg98zno81adJt4mp7pG', "confirmation_sent_at" = '2012-08-27 05:05:26.342299' WHERE "users"."id" = 1
Profile Load (1.4ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = 1 LIMIT 1
(1.7ms) COMMIT
=> true
Any help would be much appreciated!
Thanks,
I was trying to figure out what the differences are between the two environments - dev/staging vs. production. Since staging and production have pretty much identical settings, the only I could think of was cacheing. So, I changed the update_attributes line to the following and it worked.
if #user.reload.update_attributes(:email => params[:email])

Resources