在Rails

时间:2017-08-16 19:58:50

标签: ruby-on-rails arrays postgresql validation unique

我的交易模型根据数量(整数列)生成一系列唯一的票号,并​​带有以下控制器逻辑:

@transaction.quantity.times.uniq { @transaction.ticket_numbers << rand(100000..999999) }

但是,这只能确保数组中的数字是唯一的。

我需要一个数据库验证来检查所有Transaction.ticket_numbers数组,以确保每个值(票号)在所有数组中都是唯一的。

这是schema.rb中的Transaction表:

  create_table "transactions", force: :cascade do |t|
    t.string "payee"
    t.integer "quantity"
    t.decimal "debt", precision: 8, scale: 2
    t.string "email"
    t.string "ministry"
    t.integer "status"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "ticket_numbers", default: [], array: true
  end

来自transaction.rb的交易模型:

require 'csv'

class Transaction < ApplicationRecord
  belongs_to :user

  validates :email, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, :on => [:create, :update] }
  validates :payee, presence: true
  validates :quantity, numericality: { greater_than_or_equal_to: 1 }
  validates :debt, numericality: { greater_than_or_equal_to: 0 }

  def self.to_csv
    attributes = %w{payee email ministry quantity debt status ticket_numbers}
    CSV.generate(headers: true) do |csv|
      csv << attributes
      all.each do |transaction|
        csv << attributes.map{ |attr| transaction.send(attr) }
      end
    end
  end
end

交易控制器:

class TransactionsController < ApplicationController
  before_action :load_transaction, only: [:edit, :update, :destroy]

  def create
    @transaction = Transaction.new(transaction_params)
    if @transaction.save
      @transaction.quantity.times.uniq { @transaction.ticket_numbers << rand(100000..999999) }
    end
    @user = @transaction.user
    @transactions = @user.transactions
    respond_to do |format|
      if @transaction.save && @transaction.status == 1
        UserMailer.payment_confirmation(@transaction).deliver_later
        format.html { redirect_to user_url(@user), notice:'Transaction added & ticket sent' }
        format.json { render json: @user, status: :created, location: @user }
      elsif @transaction.save && @transaction.status != 1
        format.html { redirect_to user_url(@user), notice:'Transaction added' }
        format.json { render json: @user, status: :created, location: @user }
      else
        format.html { render 'users/show' }
        format.json { render json: @transaction.errors, status: :unprocessable_entity }
      end
    end
  end

  def edit
    @user = current_user
  end

  def update
    update_ticket_numbers
    respond_to do |format|
      if @transaction.update_attributes(transaction_params) && @transaction.status == 1
        UserMailer.payment_confirmation(@transaction).deliver_later
        format.html { redirect_to user_url(current_user), notice: 'Transaction info updated & confirmation email sent to payee' }
        format.json { render json: current_user, status: :created, location: current_user }
      elsif @transaction.update_attributes(transaction_params) && @transaction.status != 1
        format.html { redirect_to user_url(current_user), notice: 'Transaction info updated' }
        format.json { render json: current_user, status: :created, location: current_user }
      else
        format.html { render :edit }
        format.json { render json: @transaction.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @transaction.destroy
    redirect_to user_url(current_user), notice: 'Transaction deleted'
  end

  private

  def load_transaction
    @transaction = Transaction.find(params[:id])
  end

  def transaction_params
    params.require(:transaction).permit(:payee, :email, :ministry, :debt,
    :quantity, :status, :user_id, :ticket_numbers)
  end

  def update_ticket_numbers
    if @transaction.update_attributes(transaction_params)
      if @transaction.ticket_numbers.length < @transaction.quantity
        i = @transaction.quantity - @transaction.ticket_numbers.length
        i.times.uniq { @transaction.ticket_numbers << rand(100000..999999) }
      elsif @transaction.ticket_numbers.length > @transaction.quantity
        i = @transaction.ticket_numbers.length - @transaction.quantity
        i.times { @transaction.ticket_numbers.pop }
      end
    end
  end

1 个答案:

答案 0 :(得分:0)

您可以向模型添加自定义票证编号验证。您可以了解有关它们的更多信息here

class Transaction < ApplicationRecord
  validate :ticket_number_uniqueness

  def ticket_number_uniqueness
    # First check all new ticket numbers are unique from each other
    errors.add(:ticket_number, "is not unique") unless ticket_numbers == ticket_numbers.uniq
    
    # Next check against other ticket numbers
    ticket_numbers.each do |ticket|
      if ::Transaction.where("ticket_numbers @> '{?}'", ticket).exists?
        errors.add(:ticket_number, "is not unique")
      end
    end
  end
end

注意:这是利用Postgres包含数组方法@>的优势。您可以了解有关数组方法here

的更多信息