mengakses relasi di penginisialisasi, mongoid

Saya memigrasikan proyek dari MongoMapper ke Mongoid. Saya memiliki dua model, Graph dan Point dengan relasi 1-N yang direferensikan, jadi sebuah titik belongs_to :graph, dan setiap grafik has_many :points. Saya telah mengganti penginisialisasi dalam model Titik karena saya perlu mereferensikan beberapa keadaan dari contoh grafik tempat titik tersebut berada untuk menginisialisasi titik dengan benar.

Namun tampaknya relasi tersebut tidak diinisialisasi bahkan setelah memanggil super di penginisialisasi. Bagaimana cara mengatur status Poin dengan benar?

Berikut adalah versi sederhana dari dua kelas saya dan penginisialisasi:

class Graph
  include Mongoid::Document

  has_many :points,  dependent: :delete

  field :type,       type: String
  field :timezone,   type: String

  # ...etc...
end

class Point
  include Mongoid::Document

  belongs_to :graph

  field :date,    type: Date
  field :value,   type: Float
  field :urtext,  type: String  # as originally entered by user
  field :nextday, type: Date

  attr_accessible :urtext

  validates :value, presence: true
  validates :date,  presence: true

  def initialize(params, options={})
    super(params, options)
    parse_urtext
  end

  def parse_urtext(ur)
    # for the sake of argument, imagine this is doing some critical calculation
    # that requires info derived from current state of the graph at creation
    # and which is related to parsing the urtext

    self.date, self.value = urtext.split(",")
    self.nextday = Time.use_zone(self.graph.timezone){ Time.zone.now.to_date + 1 }
  end
end

Saat saya mencoba membuat titik baru yang terkait dengan grafik, saya mendapatkan kesalahan nihil dari penginisialisasi.

# in a console:
> g = Graph.first
> p = g.points.build( urtext: "2015-11-30,1" )
  NoMethodError: undefined method `timezone' for nil:NilClass
      from /rails_root/app/models/point.rb:34:in `parse_urtext'
      from /rails_root/app/models/point.rb:22:in `initialize'
      from /.../.rvm/gems/ruby-2.2.0/gems/mongoid-3.1.7/lib/mongoid/factory.rb:23:in `new'
      ...

Adakah yang bisa menjelaskan mengapa ini tidak berhasil? Saya bisa menulis metode build_point pada model Graph dan selalu membuat Points seperti itu, tapi kemudian saya kehilangan manfaat mengikuti konvensi Rails. Panggilan balik sepertinya juga tidak memberi saya perilaku yang saya inginkan. (Validasi adalah hal pertama yang dijalankan, dan saya ingin menyelesaikan inisialisasi status sebelum validasi).

Rel 3.2.22, Mongoid 3.1.7


person Bee    schedule 30.11.2015    source sumber


Jawaban (1)


Anda salah melakukan hal ini. Mengganti initialize saat menggunakan O[RD]M hampir selalu merupakan ide yang buruk.

Pendekatan yang lebih umum adalah dengan menggunakan kait siklus hidup standar untuk mengurai berbagai hal. Misalnya, Anda dapat menggunakan before_validation:

class Point
  include Mongoid::Document

  belongs_to :graph

  field :date,    type: Date
  field :value,   type: Float
  field :urtext,  type: String
  field :nextday, type: Date

  before_validation :parse_urtext, :if => :urtext_changed?

  validates :value, presence: true
  validates :date,  presence: true

private

  def parse_urtext
    self.date, self.value = self.urtext.split(",")
    self.nextday = Time.use_zone(self.graph.timezone){ Time.zone.now.to_date + 1 }
  end

end

Dalam beberapa kasus, Anda dapat mengganti metode urtext mutator:

def urtext=(v)
  ...
end

tapi itu tidak akan berhasil di sini. Mengatakan g.points.build( urtext: "2015-11-30,1" ) akan memanggil #urtext= tetapi belum tentu sebelum #graph_id= dipanggil sehingga Anda dapat (dan dalam kasus ini sebenarnya akan) memiliki self.graph.nil? ketika #urtext= dipanggil selama g.points.build(...).

Saya cenderung menggunakan before_validation hooks untuk hal semacam ini dan kemudian validates hooks untuk memastikan panggilan before_validation melakukan hal yang benar.

person mu is too short    schedule 02.12.2015
comment
Saya mulai mencoba menggunakan panggilan balik siklus hidup, tetapi pada dasarnya memiliki kondisi balapan karena saya memasukkan kode saya ke dalam panggilan balik after_create dengan alasan bahwa status grafik/relasi diharapkan akan diinisialisasi pada saat itu. Saya tidak pernah berhasil mencapai callback pembuatan, karena sasarannya tidak valid, dan sasaran tersebut tidak valid karena saya tidak pernah berhasil menyelesaikan seluruh pembuatan... Saya harus memikirkannya lebih dalam lagi untuk melihatnya jika saya dapat menggunakan kait before_validation untuk melakukan ini. - person Bee; 03.12.2015