Ассоциации DataMapper «NoMethodError»

Я застрял с проблемой и не могу понять, где проблема. Поскольку я относительно новичок, надеюсь, вы сможете мне помочь :).

Прежде всего, я использую padrino с гемом datamapper.

У меня есть модель «Поездка и проживание» с отношением «один ко многим».

Как выглядят модели: Trip.rb

class Trip
  include DataMapper::Resource

  # Associations
  has n, :accommodations

  property :id, Serial, :key => true
  # many more properties
end

Размещение.rb

class Accommodation
  include DataMapper::Resource

  has n, :appartments
  belongs_to :trip, :child_key => [:trip_id]

  # property <name>, <type>
  property :id, Serial
end

Основываясь на документации dm, я предполагаю, что ассоциация действительна

Я хочу добиться следующего поведения приложения:

Пользователь может выбрать конкретную поездку и добавить или удалить размещение.

Вот так выглядят мои контроллеры: trips.rb

HhtBackoffice.controllers :trips do
  get :index do
    @trips = Trip.all
    render 'trips/index'
  end

  get :show, :with => :id do
    @trip = Trip.get(params[:id])
    set_current_trip(Trip.get(params[:id]))
    render 'trips/show'
  end

  get :new do
    @trip = Trip.new
    render 'trips/new'
  end

  post :create do
    @trip = Trip.new(params[:trip])
    if @trip.save
      flash[:notice] = 'Neue Reise wurde erfolgreich hinzugef&uuml;gt'
      redirect url(:trips, :edit, :id => @trip.id)
    else 
      render 'trips/new'
    end
  end
..........
more code
........
end

accommodations.rb

HhtBackoffice.controllers :accommodations do
  get :index do
    @accommodations = Accommodation.all
    render 'accommodations/index'
  end

  get :new do
    @trip = current_trip
    @accommodation = @trip.accommodations.new  # <-- This function is not known according to the error
    render 'accommodations/new'
  end

  post :create do
    @trip = current_trip
    @accommodation = @trip.accommodations.new(params[:accommodation])
    if @accommodation.save
      flash[:notice] = 'Neue Reise wurde erfolgreich hinzugef&uuml;gt'
      redirect url(:accommodations, :edit, :id => @accommodation.id)
    else 
      render 'accommodations/new'
    end
  end
.... more code here ....
end

Как видите, я использую две вспомогательные функции. set_current_trip (при вызове представления show), чтобы отслеживать текущую поездку. И current_trip, чтобы получить current_trip.

Это мое представление trips/show.haml

.block
    .secondary-navigation
        %ul.wat-cf
            %li.first.active=link_to pat(:list), url(:trips, :index)
            %li=link_to pat(:new), url(:trips, :new)
    .content
        %h2.title
            
        .inner
            [email protected] do |acco|
                %ud
                    %li= acco.id
                    %li= acco.name
            =button_to pat(:new), url(:accommodations, :new), :method => :get, :class => :button_to

        .actions-bar.wat-cf
            .actions="&nbsp;"

Это представление размещение/новое

.block
  .secondary-navigation
    %ul.wat-cf
      %li.first=link_to pat(:list), url(:accommodations, :index)
      %li.active=link_to pat(:new), url(:accommodations, :new)
  .content
    %h2.title
      =pat(:new)
      =mt(:accommodation)
    .inner
      -form_for :accommodation, url(:accommodations, :create), :class => :form do |f|
        =partial "accommodations/form", :locals => { :f => f }

И это ОШИБКА

#<NoMethodError: undefined method `accommodations' for nil:NilClass>

/home/paddy/Projects/hht_backoffice/app/controllers/accommodations.rb in block (2 levels) in <top (required)>
    @trip.accommodations.new
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in call
            proc { |a,p| unbound_method.bind(a).call }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in block in route
            proc { |a,p| unbound_method.bind(a).call }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in []
        halt_response     = catch(:halt) { route_eval { @route.dest[self, @block_params] } }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in block (3 levels) in process_destination_path
        halt_response     = catch(:halt) { route_eval { @route.dest[self, @block_params] } }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in route_eval
      throw :halt, yield
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in block (2 levels) in process_destination_path
        halt_response     = catch(:halt) { route_eval { @route.dest[self, @block_params] } }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in catch
        halt_response     = catch(:halt) { route_eval { @route.dest[self, @block_params] } }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in block in process_destination_path
        halt_response     = catch(:halt) { route_eval { @route.dest[self, @block_params] } }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in instance_eval
    Thread.current['padrino.instance'].instance_eval do
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in process_destination_path
    Thread.current['padrino.instance'].instance_eval do
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/http_router-0.10.2/lib/http_router/node/root.rb in []
      end
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/http_router-0.10.2/lib/http_router.rb in block in call
    response = catch(:success) { @root[request] }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/http_router-0.10.2/lib/http_router.rb in catch
    response = catch(:success) { @root[request] }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/http_router-0.10.2/lib/http_router.rb in call
    response = catch(:success) { @root[request] }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in route!
          if base.compiled_router and match = base.compiled_router.call(@request.env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/application/routing.rb in dispatch!
          route!
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in block in call!
      invoke { dispatch! }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in block in invoke
      res = catch(:halt) { yield }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in catch
      res = catch(:halt) { yield }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in invoke
      res = catch(:halt) { yield }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in call!
      invoke { dispatch! }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in call
      dup.call!(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sass-3.2.5/lib/sass/plugin/rack.rb in call
        @app.call(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/rack-1.5.1/lib/rack/head.rb in call
    status, headers, body = @app.call(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/rack-1.5.1/lib/rack/methodoverride.rb in call
      @app.call(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/reloader.rb in call
        @app.call(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/logger.rb in call
        status, header, body = @app.call(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/showexceptions.rb in call
      @app.call(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/rack-1.5.1/lib/rack/session/abstract/id.rb in context
          status, headers, body = app.call(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/rack-1.5.1/lib/rack/session/abstract/id.rb in call
          context(env)
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in block in call
        synchronize { prototype.call(env) }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in synchronize
          yield
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/sinatra-1.3.4/lib/sinatra/base.rb in call
        synchronize { prototype.call(env) }
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/router.rb in block in call
        return app.call(
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/router.rb in each
      @mapping.each do |host, path, match, app|
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/padrino-core-0.10.7/lib/padrino-core/router.rb in call
      @mapping.each do |host, path, match, app|
/home/paddy/.rvm/gems/ruby-1.9.3-p374/gems/rack-1.5.1/lib/rack/handler/webrick.rb in service
        status, headers, body = @app.call(env)
/home/paddy/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/webrick/httpserver.rb in service
      si.service(req, res)
/home/paddy/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/webrick/httpserver.rb in run
          server.service(req, res)
/home/paddy/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/webrick/server.rb in block in start_thread

У меня нет проблем, если я делаю то же самое через консоль padrino:

irb(main):001:0> @trip = Trip.get(1)
  DEBUG - (0.000101) SET sql_auto_is_null = 0
  DEBUG - (0.000127) SET SESSION sql_mode = 'ANSI,NO_BACKSLASH_ESCAPES,NO_DIR_IN_CREATE,NO_ENGINE_SUBSTITUTION,NO_UNSIGNED_SUBTRACTION,TRADITIONAL'
  DEBUG - (0.000092) SELECT `id`, `name`, `type`, `starts_at`, `ends_at`, `no_of_free_places`, `no_of_guests`, `raceday`, `created_at`, `updated_at` FROM `trips` WHERE `id` = 1 LIMIT 1
=> #<Trip @id=1 @name="Rofl" @type="Wettkampfreise" @starts_at=nil @ends_at=nil @no_of_free_places=nil @no_of_guests=nil @raceday=nil @created_at=#<DateTime: 2013-01-31T15:00:16+01:00 ((2456324j,50416s,0n),+3600s,2299161j)> @updated_at=#<DateTime: 2013-01-31T15:16:40+01:00 ((2456324j,51400s,0n),+3600s,2299161j)>>

irb(main):02:0> @trip.accommodations.new
=> #<Accommodation @id=nil @name=nil @no_of_appartments=nil @location=nil @capacity=nil @trip_id=1>

Любые намеки на решение этой проблемы?

Хорошо, у меня есть предположение. Мой объект current_trip равен нулю. Это мои вспомогательные методы:

helper/trips.rb

  def set_current_trip(trip=nil)
    @current_trip = trip
  end

  def current_trip 
    @current_trip 
  end

Заранее спасибо!

Редактировать 2

Вот мои маршруты. Может быть, вложение - хороший способ заставить его работать, я хочу позвонить

=button_to pat(:new), url(:accommodations, :new), :method => :get, :class => :button_to

в моем trips/show.haml

Но я получаю ошибку

route mapping for url(:accommodations_new) could not be found!

Почему это? По моим маршрутам я должен добраться до /trips/:trip_id/accommodations/new

МАРШРУТЫ

URL                           REQUEST  PATH
    (:accommodations, :index)        GET    /trips/:trip_id/accommodations
    (:accommodations, :new)          GET    /trips/:trip_id/accommodations/new
    (:accommodations, :create)      POST    /trips/:trip_id/accommodations/create
    (:accommodations, :edit)         GET    /trips/:trip_id/accommodations/edit/:id
    (:accommodations, :update)       PUT    /trips/:trip_id/accommodations/update/:id
    (:accommodations, :destroy)    DELETE   /trips/:trip_id/accommodations/destroy/:id
    (:base, :index)                 GET    /
    (:trips, :index)                GET    /trips
    (:trips, :show)                 GET    /trips/show/:id
    (:trips, :new)                  GET    /trips/new
    (:trips, :create)              POST    /trips/create
    (:trips, :edit)                 GET    /trips/edit/:id
    (:trips, :update)               PUT    /trips/update/:id
    (:trips, :destroy)            DELETE   /trips/destroy/:id

person schnika    schedule 31.01.2013    source источник


Ответы (1)


current_trip возвращает ноль. Во-первых, убедитесь, что @trip не равно nil, когда вы вызываете один из его методов, потому что что, если поездка еще не установлена?

  get :new do
    @trip = current_trip
    if @trip
      @accommodation = @trip.accommodations.new  
      render 'accommodations/new'
    else
      # trip is nil. Call an error or something.
    end
  end

Во-вторых, я не уверен, что ваш вспомогательный метод когда-либо вернет поездку, потому что @current_trip не был инициализирован. Для чего предназначен current_trip? Это что-то, что вы хотите сохранить для пользователя между сеансами? Если это так, вы можете сохранить current_trip в файле cookie.

def set_current_trip(trip_id = 0)

  response.set_cookie 'current_trip', :value => trip_id, :path => '/', :expires => Time.now + 60 * 60 * 24 * 365 * 20

end

def current_trip 
  trip_id = request.cookies["current_trip"]
  current_trip = Trip.get trip_id
end
person AlexQueue    schedule 31.01.2013
comment
Хорошо, спасибо за быстрый ответ. Как и ожидалось, current_trip не возвращает поездку (условие else выдает ошибку). Однако я не думаю, что работа с файлами cookie является правильным решением моей проблемы. Я хочу, чтобы это было похоже на следующее: Выберите поездку (например, /trips/show/1). Оттуда добавьте проживание в поездку. Поскольку я не знаю, как отслеживать предыдущую выбранную поездку, я подумал о сохранении current_trip с помощью вспомогательного метода при вызове /trips/show/:id. Можешь проследовать за мной? Или есть способ добиться той же функциональности проще? - person schnika; 01.02.2013
comment
Вы не можете сохранить вспомогательный метод таким образом. Переменные на самом деле не сохраняются от страницы к странице. Просто для уточнения: пользователь переходит в /trips/1, чтобы выбрать поездку 1, а затем в /accommodations (или что-то подобное), чтобы добавить в эту поездку жилье? Вы можете передать поездку в URL-адресе или в качестве параметра (поэтому отправьте их в /accommodations?trip_id=1) или сохранить выбранную поездку в виде файла cookie. - person AlexQueue; 01.02.2013
comment
Хорошо, я пытаюсь сделать следующий вызов: '=button_to pat(:new), url(:accommodations, :new, :id =› @trip.id), :method =› :get, :class =› :button_to ', а затем просто скажите '@trip = Trip.get(params[:id])' в моем контроллере размещения. Но мой объект @trip по-прежнему равен нулю... - person schnika; 01.02.2013
comment
Моя проблема заключалась в глубоком непонимании того, как передавать объекты или создавать дочерние объекты. Теперь в моих представлениях о поездке есть кнопки для добавления жилья. Я передаю trip_id контроллеру размещения и создаю там '@trip.accommodations.new(...)' Теперь он работает отлично. Однако я отметил ваш ответ и проголосовал за него, потому что это решение вышеуказанной ошибки. Спасибо за помощь! - person schnika; 01.02.2013