【问题标题】:If :a has_many :b and :b has_many :c, can we render :c via :a with JSON / Rails如果 :a has_many :b 和 :b has_many :c,我们可以通过 :a 使用 JSON / Rails 渲染 :c
【发布时间】:2016-04-02 04:23:59
【问题描述】:

有3个嵌套模型(见底部的模型文件)

  • :用户
  • :测验,
  • :问题

是否可以通过user 渲染questions

可以通过user 渲染quizzes。但是我不知道为什么每个测验的问题也不包括在内...

  1. {{user}} 呈现该用户的测验

{"id":1,"email":"example@example.com","username":"jim","quizzes":[{"id":1,"name":"quiz1", "user_id":1},{"id":2,"name":"quiz2","user_id":1},]}

  1. {{user.quizzes}} 只呈现测验,而不是那些测验的问题

[{"id":1,"name":"quiz1","user_id":1},{"id":2,"name":"quiz2","user_id":1}]

  1. 另外,{{user.questions}} 将呈现一个空白数组 []

所以usersquestions 之间缺少连接,或者它只能渲染低一级的对象?

user.rb

class User < ActiveRecord::Base
  has_many :questions
  has_many :quizzes 

  def as_json(options = {})
    super(options.merge(include: [:quizzes, :questions]))
  end
end

quiz.rb

class Quiz < ActiveRecord::Base
  belongs_to :user
  has_many :questions

    def as_json(options = {})
        super(options.merge(include: [:user, :questions]))
    end    
end

question.rb

class Question < ActiveRecord::Base
    belongs_to :user    
    belongs_to :quiz

    def as_json(options = {})
        super(options.merge(include: [:user, :quiz]))
    end
end

请记住,我对数据库的经验并不丰富。任何建议将不胜感激。

==============

更新

==============

下面添加了架构。

ActiveRecord::Schema.define do

  create_table "questions", force: :cascade do |t|
    t.string   "ask"
    t.string   "answer"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer  "user_id"
    t.integer  "quiz_id"
    t.integer  "easy_count"
    t.integer  "diff_count"
  end

  add_index "questions", ["quiz_id"], name: "index_questions_on_quiz_id"
  add_index "questions", ["user_id"], name: "index_questions_on_user_id"

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

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

  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"
    t.string   "username"
  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
  add_index "users", ["username"], name: "index_users_on_username", unique: true

end

【问题讨论】:

  • 是否有任何特殊原因需要将User 直接连接到Question,而不是通过Quiz 间接连接?
  • 我正在尝试在site.io/users/:id 页面上呈现汇总问题数据,每个测验。所以我可以做类似ng-repeat="quiz in quizzes" 的事情,然后渲染{{quiz.questions}},但这也不会渲染任何东西......

标签: ruby-on-rails angularjs json


【解决方案1】:

虽然我不是 100% 确定,但我认为您没有正确创建关系。我猜user.questions 给了你一个空白数组,因为在你的数据库中,questions 只与quiz 相关。如果可以的话,你能提供你的数据库模式吗?

==================

更新

我完全按照你的模式创建了一个数据库,下面是我运行User.first.as_json后得到的示例结果

{"id"=>1, "email"=>"", "encrypted_password"=>"", "reset_password_token"=>nil, "reset_password_sent_at"=>nil, "remember_created_at"=>nil, "sign_in_count"=>0, "current_sign_in_at"=>nil, "last_sign_in_at"=>nil, "current_sign_in_ip"=>nil, "last_sign_in_ip"=>nil, "created_at"=>Sat, 02 Apr 2016 05:42:33 UTC +00:00, "updated_at"=>Sat, 02 Apr 2016 05:42:33 UTC +00:00, "username"=>nil, "quizzes"=>[{"id"=>1, "name"=>nil, "user_id"=>1, "created_at"=>Sat, 02 Apr 2016 05:42:42 UTC +00:00, "updated_at"=>Sat, 02 Apr 2016 05:42:42 UTC +00:00}], "questions"=>[{"id"=>1, "ask"=>nil, "answer"=>nil, "created_at"=>Sat, 02 Apr 2016 05:43:11 UTC +00:00, "updated_at"=>Sat, 02 Apr 2016 05:43:11 UTC +00:00, "user_id"=>1, "quiz_id"=>1, "easy_count"=>nil, "diff_count"=>nil}]}

更新 2

一个快速修复将是

class User < ActiveRecord::Base
  has_many :questions
  has_many :quizzes

  def as_json(options = {})
    super(options.merge(include: [quizzes: {include: :questions}]))
  end
end

但再次如您所见,使用 include 并覆盖 as_json 非常难看。我建议你最终切换到 Jbuilder 或active_model_serializers

【讨论】:

  • 我已经添加了架构。
  • 我试图创建一个数据库,完全复制粘贴您的架构,但我可以毫无问题地获取问题,我怀疑您的数据库有问题,您可以尝试运行rake db:drop db:create db:migrate,并且再试一次?看user.questions工作与否
  • 正如马特所说,您可能想使用ActiveModelSerializersJbuilder 为嵌套模型呈现日期,这比使用include 更干净
  • 经过一些小改动后,user.questions 可以工作,但我需要的更像是quiz.questions for quiz in user.quizzes,所以我可以在测验级别提取汇总的问题数据。
  • 一个快速修复方法是将super(options.merge(include: [:quizzes, :questions])) 更改为super(options.merge(include: [quizzes: {include: :questions}]))。但是随着你的应用程序的增长,这个解决方案以后会变得很难处理。
【解决方案2】:

在您的情况下,您需要使用 has_many through,因为您的问题与测验相关联,而不是与用户相关联。 实际上,您所有的麻烦都来自您将 user_id 用于提问和测验的事实。 假设您的问题属于属于用户的测验,您应该只将 user_id 放在测验中。

没有数据重复,您可以确保问题不属于与问题不属于同一用户的测验(!)

并使用委托方法访问用户ID。

这里有一个改进:

class User < ActiveRecord::Base
  has_many :quizzes 
  has_many :questions, through: quizzes

  def as_json(options = {})
    super(options.merge(include: [:quizzes, :questions]))
  end
end

class Quizz < ActiveRecord::Base
  has_many :questions
  belongs_to :user
end

class Question < ActiveRecord::Base
  belongs_to :quizz
  delegate :user, to: :quizz
end

【讨论】:

  • 这让我可以做user.questions 但我希望为quiz in user.quizzes 做更多类似quiz.questions 的事情,以在quiz 级别上显示聚合的question 数据。这可能吗?
  • 当然你可以完全按照你说的去做! user.quizzes.includes(:questions).each do |quizz| quizz.questions end。不要忘记包含,它可以防止 N+1 请求行为(可能会导致性能成本高昂)
【解决方案3】:

我建议使用更强大的解决方案,例如 ActiveModelSerializers

class UserSerializer < ActiveModel::Serializer
  attributes :id, :email
  has_many :quizzes, serializer: QuizSerializer
end

class QuizSerializer < ActiveModel::Serializer
  attributes :name
  has_many :questions, serializer: QuestionSerializer
end

class QuestionSerializer < ActiveModel::Serializer
  attributes :ask, :answer
end

class UserController < ApplicationController
  def show
    render json: user, serializer: UserSerializer
  end
end

【讨论】:

    【解决方案4】:

    正如 Yacine 所指出的,has_many :through 可能是一个更好的解决方案,因此您在 quizzesquestions 表中都没有重复的 user_id

    如果您确实进行了更改,您还可以从questions 表中删除user_id 列。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-03
      • 2015-11-28
      • 1970-01-01
      • 2011-08-01
      • 2015-06-12
      • 1970-01-01
      • 2023-03-03
      相关资源
      最近更新 更多