简体   繁体   中英

Update multiple records in the DB, one time per record in Rails

I have a code that is updating user records in a DB, but is updating the record of each user, the number of times there are users in the database, example, I have 3 users => medic1, medic2, medic4 in the SQL you can see that it is updating them 3 times when it should be 1 time per user.

What my code should do is extract the users with the nested attribute period_end_date from the DB and save them in a hash, which will query the Openpay API about the status of the subscription; in the response hash I receive the data of each user and depending on the 'status' I update the values in the database with the results of the response_hash, this should be per user. I can not see what I'm doing wrong, can you help me achieve it?

Here is the code, rake premium_users:get_users (lib/tasks/premium_users.rake):

namespace :premium_users do
  desc 'Get the user id & data in a hash'
  task get_users: :environment do 
    users = Medic.includes(:payment_methods).where.not payment_methods: { period_end_date: nil }
    request_hash = {}

    # Get users in a hash 
    users.each do |user|
      period_end_date = user.payment_methods.last.period_end_date

      if Date.today - period_end_date <= 172_800
        openpay_customer_id = user.payment_methods.last.openpay_customer_id
        openpay_subscription_id = user.payment_methods.last.openpay_subscription_id
        medic_id = user.payment_methods.last.medic_id
        user_hash = {}
        user_hash[medic_id] = [openpay_customer_id, openpay_subscription_id]
      else
        user_hash = {}
      end

      request_hash.merge!(user_hash) # If no users is empty => {}

      if request_hash.empty?
        puts 'No User to check!'
      else
        # Request for subscription status to Openpay
        request_hash.each_value do |value|
          openpay_customer_id = value[0]
          openpay_subscription_id = value[1]

          # Openpay API connection
          @openpay = OpenpayApi.new(ENV['MERCHANT_ID_OPENPAY'], ENV['SECRET_KEY_OPENPAY'])
          @subscriptions = @openpay.create(:subscriptions)
          response_hash = @subscriptions.get(openpay_subscription_id, openpay_customer_id)

          # Extract values from response
          @charge_date, @creation_date, @current_period_number, @period_end_date, @status, @trial_end_date = response_hash.values_at('charge_date', 'creation_date', 'current_period_number', 'period_end_date', 'status', 'trial_end_date')

          case @status
          when 'past_due'
            @premium = false
            @cancelled = Time.now
          when 'unpaid'
            @premium = false
            @cancelled = Time.now
          when 'cancelled'
            @premium = false
            @cancelled = Time.now
          else
            @premium = true
            @cancelled = nil
          end

        end

        # Update user payment record with response from Openpay
        @payment_method = PaymentMethod.update(
          premium: @premium,
          charge_date: @charge_date,
          creation_date: @creation_date,
          current_period_number: @current_period_number,
          period_end_date: @period_end_date,
          status: @status,
          trial_end_date: @trial_end_date,
          cancelled_on: @cancelled
        )

        puts "User #{user.email} update, Complete!"

      end
    end
  end
end

The SQL:

PaymentMethod Load (0.4ms)  SELECT `payment_methods`.* FROM `payment_methods`
   (0.1ms)  BEGIN
   SQL (0.3ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:15:13', `updated_at` = '2018-06-15 21:01:19' WHERE `payment_methods`.`id` = 1
   (11.9ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:15:13', `updated_at` = '2018-06-15 21:01:19' WHERE `payment_methods`.`id` = 2
   (2.7ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.2ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:15:13', `updated_at` = '2018-06-15 21:01:19' WHERE `payment_methods`.`id` = 3
   (2.6ms)  COMMIT
User medic1@mail.com update, Complete!

  PaymentMethod Load (0.3ms)  SELECT `payment_methods`.* FROM `payment_methods`
   (0.1ms)  BEGIN
   SQL (0.3ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:16:24', `updated_at` = '2018-06-15 21:01:20' WHERE `payment_methods`.`id` = 1
   (11.9ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:16:24', `updated_at` = '2018-06-15 21:01:20' WHERE `payment_methods`.`id` = 2
   (2.7ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:16:24', `updated_at` = '2018-06-15 21:01:20' WHERE `payment_methods`.`id` = 3
   (2.5ms)  COMMIT
User medic2@mail.com update, Complete!

  PaymentMethod Load (0.3ms)  SELECT `payment_methods`.* FROM `payment_methods`
   (0.1ms)  BEGIN
  SQL (0.2ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:18:30', `updated_at` = '2018-06-15 21:01:22' WHERE `payment_methods`.`id` = 1
   (2.6ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.2ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:18:30', `updated_at` = '2018-06-15 21:01:22' WHERE `payment_methods`.`id` = 2
   (2.6ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `payment_methods` SET `creation_date` = '2018-06-15 19:18:30', `updated_at` = '2018-06-15 21:01:22' WHERE `payment_methods`.`id` = 3
   (2.6ms)  COMMIT
User medic4@mail.com update, Complete!

Models:

class Medic < ApplicationRecord
  has_many :payment_methods, dependent: :destroy
end

class PaymentMethod < ApplicationRecord
  belongs_to :medic, optional: true 
end

Thanks for your answers, they finally helped me to realize that I have to update each object, so I did it by going through PaymentMethod.where(id: @subscription_id, medic_id: @medic_id).update()

namespace :premium_users do
  desc 'Get the user id & data in a hash'
  task get_users: :environment do 
    # Get the user and association payment_method where period_end_date not nil, means that this user has a payment_method
    users = Medic.includes(:payment_methods).where.not payment_methods: { period_end_date: nil }
    request_hash = {}

    users.each do |user|
      # Set to compare period end date with date today 
      period_end_date = user.payment_methods.last.period_end_date

      # Create a user hash if period_end_date is 2 days from date today
      if Date.today - period_end_date <= 172_800
        medic_id, subscription_id, openpay_customer_id, openpay_subscription_id = user.payment_methods.last.medic_id, user.payment_methods.last.id, user.payment_methods.last.openpay_customer_id, user.payment_methods.last.openpay_subscription_id
        user_hash = {}
        user_hash[medic_id] = [subscription_id, openpay_customer_id, openpay_subscription_id]
      else
        user_hash = {} 
      end

      request_hash.merge!(user_hash) # If no users is empty => {}

      if request_hash.empty?
        puts 'No User to check!'
      else
        request_hash.each do |key, value|
          @medic_id, @subscription_id, openpay_customer_id, openpay_subscription_id = key, value[0], value[1], value[2]

          # Openpay API connection and request
          @openpay = OpenpayApi.new(ENV['MERCHANT_ID_OPENPAY'], ENV['SECRET_KEY_OPENPAY'])
          @subscriptions = @openpay.create(:subscriptions)
          # @subscriptions.get(subscription_id,customer_id)
          response_hash = @subscriptions.get(openpay_subscription_id, openpay_customer_id)

          # Split values
          @charge_date, @creation_date, @current_period_number, @period_end_date, @status, @trial_end_date = response_hash.values_at('charge_date', 'creation_date', 'current_period_number', 'period_end_date', 'status', 'trial_end_date')

          # Status conditions
          case @status
          when 'past_due'
            @premium = false
            @cancelled = Time.now
          when 'unpaid'
            @premium = false
            @cancelled = Time.now
          when 'cancelled'
            @premium = false
            @cancelled = Time.now
          else
            @premium = true
            @cancelled = nil
          end
        end

        # Update each subscription with data from API response
        @payment_method = PaymentMethod.where(id: @subscription_id, medic_id: @medic_id).update(
          premium: @premium,
          charge_date: @charge_date,
          creation_date: @creation_date,
          current_period_number: @current_period_number,
          period_end_date: @period_end_date,
          status: @status,
          trial_end_date: @trial_end_date,
          cancelled_on: @cancelled
        )

        puts "User #{@medic_id} update, Complete!"
      end
    end
  end
end

Result:

PaymentMethod Load (0.5ms)  SELECT `payment_methods`.* FROM `payment_methods` WHERE `payment_methods`.`id` = 1 AND `payment_methods`.`medic_id` = 1
   (0.2ms)  BEGIN
  SQL (0.4ms)  UPDATE `payment_methods` SET `premium` = 1, `current_period_number` = 0, `status` = 'trial', `updated_at` = '2018-06-20 17:40:20' WHERE `payment_methods`.`id` = 1
   (12.1ms)  COMMIT
User 1 update, Complete!
  PaymentMethod Load (0.5ms)  SELECT `payment_methods`.* FROM `payment_methods` WHERE `payment_methods`.`id` = 2 AND `payment_methods`.`medic_id` = 3
   (0.2ms)  BEGIN
  SQL (0.4ms)  UPDATE `payment_methods` SET `premium` = 1, `status` = 'trial', `cancelled_on` = NULL, `updated_at` = '2018-06-20 17:40:22' WHERE `payment_methods`.`id` = 2
   (11.8ms)  COMMIT
User 3 update, Complete!
  0.040000   0.010000   0.050000 (  1.741126)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM