2

I'm having trouble with embeds in a mongoid4-based rails 4 app. I've been looking for an answer everywhere for the past 2 days. So here is the code.

This is a church management app, with a Service model, embedding a team and a band. Each team/band has several roles such as "presidence", "communion" that refer to a user.

My models :

class Service
  include Mongoid::Document
  ...
  embeds_one :team, autobuild: true
  embeds_one :band, autobuild: true
  ...
  accepts_nested_attributes_for :team, :band
end

class Team
  include Mongoid::Document  

  embedded_in :service

  has_one :presidence, :class_name => 'User', autosave: true
  has_one :message, :class_name => 'User', autosave: true
  ...
end

class Band
  include Mongoid::Document
  has_one :lead, :class_name => 'User', autosave: true
  has_one :guitar, :class_name => 'User', autosave: true
  ...
  embedded_in :service
end

class User
  include Mongoid::Document
  embeds_one :profile

  belongs_to :team, :inverse_of => :presidence
  belongs_to :team, :inverse_of => :message

  belongs_to :band, :inverse_of => :lead
  belongs_to :band, :inverse_of => :guitar

  def fullname
    "#{profile.firstname} #{profile.lastname}"
  end

  def self.find_by_talent(talent)
    self.where("profile.talents.name" => talent)
  end
end

The services controller :

# POST /services
# POST /services.json
def create
  @service = Service.new(service_params)
  respond_to do |format|
    if @service.save
      format.html { redirect_to @service, notice: 'Service was successfully created.' }
      format.json { render action: 'show', status: :created, location: @service }
    else
      format.html { render action: 'new' }
      format.json { render json: @service.errors, status: :unprocessable_entity }
    end
  end
end

...

def service_params
  params.require(:service).permit(:date, :time, :place, :name, :theme, :team => [:id, :precedence, :message ], :band => [:id, :lead, :guitar ])
end

And the form in _form.html.erb :

<%= form_for(@service) do |f| %>
  ...
  <%= f.fields_for @service.team  do |tf| %>
    <%= tf.collection_select :presidence, User.find_by_talent(:presidence), :_id, :fullname, {:include_blank => "select a person"} %>
    <%= tf.collection_select :message, User.find_by_talent(:message), :id, :fullname, {:include_blank => "select a person"} %>
  <% end %>
  <%= f.fields_for @service.band  do |bf| %>
    <%= bf.collection_select :lead, User.find_by_talent(:lead), :id, :fullname, {:include_blank => "select a person"} %>
    <%= bf.collection_select :guitar, User.find_by_talent(:guitar), :id, :fullname, {:include_blank => "select a person"} %>
  <% end %>
  ...
<% end %>

When creating a service, everything seems to run fine, but this is what I get in the console :

2.0.0-p195 :001 > s = Service.last
 => #<Service _id: 52ea18834d61631e7e020000, date: "2014-02-02", time: "10:00", place: "Where it's at", name: "My great name", theme: "The service's theme"> 
2.0.0-p195 :002 > s.team
 => #<Team _id: 52ea18834d61631e7e030000, > 
2.0.0-p195 :003 > s.team.presidence
 => nil 

Why is s.team.presidence not created ? s.team looks weird, too, with an ending comma...

Here is the content of my rails log :

Started POST "/services" for 127.0.0.1 at 2014-01-30 10:16:51 +0100
Processing by ServicesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ph6lbdHC2FbiANn/fGSzHWprenj3fWKXM40Hrsc5+AM=", "service"=>{"date"=>"2014-02-02", "name"=>"My great name", "theme"=>"The service's theme", "time"=>"10:00", "place"=>"Where it's at", "team"=>{"presidence"=>"52ea18324d61631e81010000", "message"=>"52ea18324d61631e81010000"}, "band"=>{"lead"=>"52ea18324d61631e81010000", "guitar"=>"52ea18324d61631e81010000"}}, "commit"=>"Create Service"}
  MOPED: 127.0.0.1:27017 COMMAND      database=admin command={:ismaster=>1} runtime: 0.6610ms
  MOPED: 127.0.0.1:27017 UPDATE       database=service_boot_camp_development collection=users selector={"band_id"=>BSON::ObjectId('52ea18834d61631e7e010000'), "_id"=>{"$nin"=>[]}} update={"$set"=>{"band_id"=>nil}} flags=[:multi]
                         COMMAND      database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 0.5800ms
  MOPED: 127.0.0.1:27017 INSERT       database=service_boot_camp_development collection=services documents=[{"_id"=>BSON::ObjectId('52ea18834d61631e7e020000'), "date"=>"2014-02-02", "time"=>"10:00", "place"=>"Where it's at", "name"=>"My great name", "theme"=>"The service's theme", "team"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e030000')}, "band"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e010000')}}] flags=[]
                         COMMAND      database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 2.7460ms

I guess I'm doing something wrong, but I have no clue if it is in the database model or in the form... or anything else...

MoskitoHero
  • 253
  • 2
  • 12

1 Answers1

2

You will not be able to do it this way. When you create an embedded document, its _id and all of its data are embedded directly within the parent document. This is in contrast to an association, where the document with the belongs_to gets a foreign key which points to its associated parent document. So here, your User documents each have a team_id and band_id, but when the database tries to get the documents, it can't find them, since you can't query directly for embedded documents; you need the parent document first. For more, see the Mongoid documentation.

Another potential issue is that you have multiple belongs_to definitions in the User models. This will also cause an issue, because for each one of those, Mongoid will attempt to create a team_id and band_id. You should name them separately and specify a class name; perhaps names like :presiding_team and :message_team, :lead_band and :guitar_band, etc. This answer should show you what that would look like.

I would recommend making the Team and Band separate referenced documents instead of embedded documents, since you won't be able to reference users effectively while they're embedded.

Hope this helps.

Community
  • 1
  • 1
take
  • 319
  • 3
  • 8