ActiveRecord :: StatementInvalid(PG :: SyntaxError:ERROR:语法错误位于或接近"."

Hen*_*nry 1 postgresql activerecord ruby-on-rails

我不确定为什么我的查询在localhost上运行但在服务器上失败.当我尝试创建一个路由到QuizzesController #new的测验时会发生这种情况

# GET /quizzes/new
  def new
    @quiz = current_user.quizzes.new
  end
Run Code Online (Sandbox Code Playgroud)

这是查询:

SELECT COUNT(*) FROM "questions" INNER JOIN "question_categories" ON "question_categories"."question_id" = "questions"."id" WHERE "questions"."deleted_at" IS NULL AND (`question_categories`.`category_id` IN (87,1))

(1.0ms)  ROLLBACK
Completed 500 Internal Server Error in 58ms (ActiveRecord: 13.4ms)
Run Code Online (Sandbox Code Playgroud)

我得到了一个错误.

ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR:  syntax error at or near "." LINE 1: ...s"."deleted_at" IS NULL AND (`question_categories`.`category...
Run Code Online (Sandbox Code Playgroud)

quiz.rb在创建之前我会运行build_parts,它应该随机抓取问题并将它们放入测验中.class Quiz <ActiveRecord :: Base belongs_to:user belongs_to:subject has_many:quiz_categories has_many:categories,through :: quiz_categories has_many:quiz_parts

  accepts_nested_attributes_for :categories
  accepts_nested_attributes_for :quiz_parts

  validates :user, :subject, :number_of_questions, presence: true
  validates :number_of_questions, numericality: { only_integer: true, greater_than_or_equal_to: 1 }

  before_create :build_parts
  before_save :set_completed_at, if: -> { completeness == 100.00 }

  def completeness
    answerable_quiz_parts = 0
    quiz_parts.each do |q_part|
      answerable_quiz_parts += 1 if q_part.answerable.answers.present?
    end
    quiz_parts.joins(:choice).count.to_f * 100 / answerable_quiz_parts
  end

  def score
    quiz_parts.joins(:choice).where('choices.correct = ?', true).count { |qp| qp.choice.correct? }
  end

  private

  # select random questions
  def build_parts
    category_ids = self.categories.map(&:id)
    question_pool = Question.joins(:question_categories).where('`question_categories`.`category_id` IN (?)', category_ids)

    #self.number_of_questions = [number_of_questions, question_pool.size].min

    puts question_pool.size

    if number_of_questions > question_pool.size
      errors.add(:number_of_questions, 'is too high. Please select a lower question count or increase category selections')
      return false
    end

    number_of_questions.times do |i|
      question_pool.inspect
      self.quiz_parts << question_pool[i].quiz_parts.new
      question_pool[i].question_parts.each do |question_part|
        self.quiz_parts << question_part.quiz_parts.new
      end
    end
  end

  def set_completed_at
    self.completed_at = Time.zone.now
  end

end
Run Code Online (Sandbox Code Playgroud)

quizzes_controller.rb

class QuizzesController < ApplicationController
  before_action :authenticate_user!
  before_action :set_quiz, only: [:show, :edit, :update, :destroy]

  # GET /quizzes
  # GET /quizzes.json
  def index
    @quizzes = current_user.quizzes.order(created_at: :desc)
  end

  # GET /quizzes/1
  # GET /quizzes/1.json
  def show
  end

  # GET /quizzes/new
  def new
    @quiz = current_user.quizzes.new
  end

  # GET /quizzes/1/edit
  def edit

  end

  # POST /quizzes
  # POST /quizzes.json
  def create
    @quiz = current_user.quizzes.new(quiz_create_params)
    respond_to do |format|
      if @quiz.save
        format.html { redirect_to edit_quiz_path(@quiz), notice: 'Quiz was successfully created.' }
        format.json { render :show, status: :created, location: @quiz }
      else
        format.html { render :new }
        format.json { render json: @quiz.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /quizzes/1
  # PATCH/PUT /quizzes/1.json
  def update
    respond_to do |format|
      if @quiz.update(quiz_update_params)
        format.html { redirect_to @quiz, notice: 'Quiz was successfully updated.' }
        format.json { render :show, status: :ok, location: @quiz }
      else
        format.html { render :edit }
        format.json { render json: @quiz.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /quizzes/1
  # DELETE /quizzes/1.json
  def destroy
    @quiz.destroy
    respond_to do |format|
      format.html { redirect_to quizzes_url, notice: 'Quiz was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
  # Use callbacks to share common setup or constraints between actions.
  def set_quiz
    @quiz = current_user.quizzes.find(params[:id])
  end

  # For quiz setup
  def quiz_create_params
    params.require(:quiz).permit(:subject_id, :number_of_questions, category_ids: [])
  end

  # For quiz answering
  def quiz_update_params
    params.require(:quiz).permit(quiz_parts_attributes: [:id, choice_attributes: [:id, :content, :answer_id, :_destroy]])
  end
end
Run Code Online (Sandbox Code Playgroud)

schema.rb:

ActiveRecord::Schema.define(version: 20150726180000) do

  create_table "admins", force: :cascade do |t|
    t.string   "email"
    t.string   "encrypted_password",     default: "", null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,  null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.string   "confirmation_token"
    t.datetime "confirmed_at"
    t.datetime "confirmation_sent_at"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "admins", ["confirmation_token"], name: "index_admins_on_confirmation_token", unique: true
  add_index "admins", ["email"], name: "index_admins_on_email", unique: true
  add_index "admins", ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true

  create_table "answers", force: :cascade do |t|
    t.integer  "number"
    t.text     "content"
    t.boolean  "correct",         default: false, null: false
    t.integer  "answerable_id"
    t.string   "answerable_type"
    t.datetime "created_at",                      null: false
    t.datetime "updated_at",                      null: false
  end

  add_index "answers", ["answerable_type", "answerable_id"], name: "index_answers_on_answerable_type_and_answerable_id"

  create_table "categories", force: :cascade do |t|
    t.string   "name"
    t.integer  "subject_id"
    t.integer  "category_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "categories", ["category_id"], name: "index_categories_on_category_id"
  add_index "categories", ["subject_id"], name: "index_categories_on_subject_id"

  create_table "choices", force: :cascade do |t|
    t.string   "content"
    t.integer  "quiz_part_id"
    t.integer  "answer_id"
    t.boolean  "correct"
    t.datetime "created_at",   null: false
    t.datetime "updated_at",   null: false
  end

  add_index "choices", ["answer_id"], name: "index_choices_on_answer_id"
  add_index "choices", ["quiz_part_id"], name: "index_choices_on_quiz_part_id"

  create_table "ckeditor_assets", force: :cascade do |t|
    t.string   "data_file_name",               null: false
    t.string   "data_content_type"
    t.integer  "data_file_size"
    t.integer  "assetable_id"
    t.string   "assetable_type",    limit: 30
    t.string   "type",              limit: 30
    t.integer  "width"
    t.integer  "height"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "ckeditor_assets", ["assetable_type", "assetable_id"], name: "idx_ckeditor_assetable"
  add_index "ckeditor_assets", ["assetable_type", "type", "assetable_id"], name: "idx_ckeditor_assetable_type"

  create_table "levels", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "question_categories", force: :cascade do |t|
    t.integer  "question_id"
    t.integer  "category_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "question_categories", ["category_id"], name: "index_question_categories_on_category_id"
  add_index "question_categories", ["question_id"], name: "index_question_categories_on_question_id"

  create_table "question_parts", force: :cascade do |t|
    t.text     "content"
    t.string   "type"
    t.integer  "question_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
    t.datetime "deleted_at"
  end

  add_index "question_parts", ["deleted_at"], name: "index_question_parts_on_deleted_at"
  add_index "question_parts", ["question_id"], name: "index_question_parts_on_question_id"

  create_table "questions", force: :cascade do |t|
    t.text     "content"
    t.string   "type"
    t.integer  "level_id"
    t.integer  "subject_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "deleted_at"
    t.string   "source"
  end

  add_index "questions", ["deleted_at"], name: "index_questions_on_deleted_at"
  add_index "questions", ["level_id"], name: "index_questions_on_level_id"
  add_index "questions", ["subject_id"], name: "index_questions_on_subject_id"

  create_table "quiz_categories", force: :cascade do |t|
    t.integer  "category_id"
    t.integer  "quiz_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "quiz_categories", ["category_id"], name: "index_quiz_categories_on_category_id"
  add_index "quiz_categories", ["quiz_id"], name: "index_quiz_categories_on_quiz_id"

  create_table "quiz_parts", force: :cascade do |t|
    t.integer  "quiz_id"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
    t.integer  "answerable_id"
    t.string   "answerable_type"
  end

  add_index "quiz_parts", ["answerable_type", "answerable_id"], name: "index_quiz_parts_on_answerable_type_and_answerable_id"
  add_index "quiz_parts", ["quiz_id"], name: "index_quiz_parts_on_quiz_id"

  create_table "quizzes", force: :cascade do |t|
    t.integer  "user_id"
    t.datetime "completed_at"
    t.datetime "created_at",          null: false
    t.datetime "updated_at",          null: false
    t.integer  "subject_id"
    t.integer  "number_of_questions"
  end

  add_index "quizzes", ["subject_id"], name: "index_quizzes_on_subject_id"
  add_index "quizzes", ["user_id"], name: "index_quizzes_on_user_id"

  create_table "subjects", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "users", force: :cascade do |t|
    t.string   "email",                  default: "", null: false
    t.string   "encrypted_password",     default: "", null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,  null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true
  add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

end
Run Code Online (Sandbox Code Playgroud)

Зел*_*ный 5

我相信你使用的是错误的引号:

SELECT COUNT(*) ....... (`question_categories`.`category_id` IN (87,1))
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

" "而不是``

更新:

是的,我一直在您的测验模型中使用错误的引号:

def build_parts
  category_ids = self.categories.map(&:id)
  question_pool = Question.joins(:question_categories).where('`question_categories`.`category_id` IN (?)', category_ids)
                                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

修复它:

 def build_parts
   category_ids = self.categories.map(&:id)
   question_pool = Question.joins(:question_categories).where('"question_categories"."category_id" IN (?)', category_ids)
                                                               ^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)