Parent association for embedded document using MongoMapper - mongomapper

If I have:
class Post
include MongoMapper::Document
has_many :comments
end
If I do:
class Comment
include MongoMapper::EmbeddedDocument
belongs_to :post # relevant part
end
Does that create an association using _root_document/_parent_document, or do I have to add the (redundant) key :post_id?

You do not need post_id or belongs_to :post. Instead, you can use embedded_in :post. This will make a read method for _parent_reference named post so you can say comment.post instead of comment._parent_reference.
class Comment
include MongoMapper::EmbeddedDocument
embedded_in :post
end

You do need the post_id key.
Here's how I tested this (with the classes as in the question):
> post = Post.new
=> #<Post _id: BSON::ObjectId('4cc5955ec2f79d4c84000001')>
> comment = Comment.new
=> #<Comment _id: BSON::ObjectId('4cc59563c2f79d4c84000002')>
> post.comments << comment
=> [#<Comment _id: BSON::ObjectId('4cc59563c2f79d4c84000002')>]
> post.save
=> true
> post.reload
=> #<Post _id: BSON::ObjectId('4cc5955ec2f79d4c84000001')>
> comment = post.comments.first
=> #<Comment _id: BSON::ObjectId('4cc59563c2f79d4c84000002')>
> comment.post
=> nil
> class Comment
?> key :post_id
?> end
=> #<MongoMapper::Plugins::Keys::Key:0xb5ab0328 #name="post_id", #type=nil, #default_value=nil, #options={}>
> comment
=> #<Comment post_id: nil, _id: BSON::ObjectId('4cc59563c2f79d4c84000002')>
> comment.post
=> nil
> comment.post = post
=> #<Post _id: BSON::ObjectId('4cc5955ec2f79d4c84000001')>
> comment.save
=> true
> comment.post
=> #<Post _id: BSON::ObjectId('4cc5955ec2f79d4c84000001')>

Related

rails 4 f.fields_for doesn't iterate over my embedded collection

I am trying to build a simple application where user can post something and attach questions to the post (post.questions).
I am getting problem displaying view in embeds_many scenario. Though I create multiple questions in PostsController # new method, html after rendering only shows one question.
I am using rails4/mongoid/mongodb.
Post.rb:
class Post
include Mongoid::Document
include Mongoid::Timestamps
field :title, type: String
field :body, type: String
embeds_many :post_replies
embeds_many :questions
belongs_to :poster, class_name: "User"
accepts_nested_attributes_for :questions
end
Quesion.rb.
class Question
include Mongoid::Document
include Mongoid::Timestamps
field :q
embedded_in :post
end
PostsController.rb
class PostsController < ApplicationController
def new
#post = Post.new
3.times{
post.questions.build
}
end
def post_params
params.require(:post).permit(:title, :body, questions_attributes: [:id, :q, :_destroy])
end
end #end controller
Routes.rb
devise_for :users
resources :users do
resources :posts
resources :addresses
end
views/posts/_form.html.erb
<%= form_for([current_user, #post], :html => { :class => "form-horizontal" }) do |f| %>
-
-
<%=f.fields_for #post.questions do|question| %>
<%= question.text_field :q, :placeholder =>'question', :class => "form-control" %>
<%end%>
-
-
<%end%>
I expect above fields for to display 3 questions but it only shows one question. I tried different ways for fields_for but none worked.
Any help in this is greatly appreciated.
Thanks in advance.
I changed
<%=f.fields_for #post.questions do|question| %>
to
<%=f.fields_for :questions, #post.questions do|question| %>
and it worked.
Api ref: here http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for

Rails Create has_many_through won't create records

I am creating an exercise tracking website that has a Workout table, a WorkoutExercise table, and an Exercise table (this is just some of the schema).
I have been trying to create a "WorkoutExercise" from the Edit / create Workout form. I'm using Formtastic and my form displays the list of exercises for the user, but when I submit the form, no WorkoutExercises get created, but the workout does get created or updated.
I've searched google and stackoverflow for this issue for a day and a half with not much help. From what I can tell, this should "just work", I think I've missed something simple. This is my first RoR website, so please bear with me:)
Another thing to note, if I manually create the relationship in PSQL, the exercises that I create a relation for show up checked, but when I hit submit, the WorkoutExercise records are deleted from the database.
Database Schema for these 3 tables:
create_table "exercises", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.text "notes"
t.decimal "base_weight"
end
create_table "workout_exercises", force: true do |t|
t.integer "workout_id"
t.integer "exercise_id"
t.integer "exercise_order"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "workouts", force: true do |t|
t.integer "user_id"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
end
Exercise Model:
class Exercise < ActiveRecord::Base
has_many :weight_logs, dependent: :destroy
belongs_to :user
has_many :workout_exercises
has_many :workouts, :through => :workout_exercises
...
WorkoutExercise Model (Many to many table)
class WorkoutExercise < ActiveRecord::Base
belongs_to :exercise
belongs_to :workout
accepts_nested_attributes_for :workout
#default_scope -> { order('exercise_order ASC') }
validates :exercise_id, presence: true
validates :workout_id, presence: true
validates :exercise_order, presence: true
end
Workout Model:
class Workout < ActiveRecord::Base
has_many :workout_exercises
has_many :exercises, :through => :workout_exercises, :class_name => 'Exercise'
belongs_to :user
accepts_nested_attributes_for :exercises
accepts_nested_attributes_for :workout_exercises
...
Workouts_conroller.rb
class WorkoutsController < ApplicationController
before_action :set_workout, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :correct_user, only: :destroy
# GET /workouts
# GET /workouts.json
def index
#workouts = Workout.all
end
# GET /workouts/1
# GET /workouts/1.json
def show
end
def add_exercise
end
# GET /workouts/new
def new
#workout = current_user.workouts.new
#exercises = current_user.exercises.all
end
# GET /workouts/1/edit
def edit
#exercises = #workout.exercises.build
end
# POST /workouts
# POST /workouts.json
def create
#workout = current_user.workouts.build(workout_params)
respond_to do |format|
if #workout.save
format.html { redirect_to #workout, notice: 'Workout was successfully created.' }
format.json { render :show, status: :created, location: #workout }
else
format.html { render :new }
format.json { render json: #workout.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /workouts/1
# PATCH/PUT /workouts/1.json
def update
respond_to do |format|
if #workout.update(workout_params)
format.html { redirect_to #workout, notice: 'Workout was successfully updated.' }
format.json { render :show, status: :ok, location: #workout }
else
format.html { render :edit }
format.json { render json: #workout.errors, status: :unprocessable_entity }
end
end
end
# DELETE /workouts/1
# DELETE /workouts/1.json
def destroy
#workout.destroy
respond_to do |format|
format.html { redirect_to workouts_url, notice: 'Workout was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_workout
#workout = Workout.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def workout_params
params.require(:workout).permit(:description, :name,
:exercise_ids => [:exercise_id])
end
def correct_user
#workout = current_user.workouts.find_by(id: params[:id])
redirect_to root_url if #workout.nil?
end
end
Rails Server log on Save:
Started PATCH "/workouts/6" for 127.0.0.1 at 2014-07-13 19:30:05 -0700
Processing by WorkoutsController#update as HTML
Parameters: {"utf8"=>"v", "authenticity_token"=>"WY2P1Z7/g/PoG5iB6VufDNOG7Hyk7tnJQzTT00CpNTM=", "workout"=>{"name"=>"sad", "description"=>"asd", "exercise_ids"=>["", "17", "23",
"9", "15", "14", "18"]}, "commit"=>"Update Workout", "id"=>"6"}
?[1m?[36mWorkout Load (0.0ms)?[0m ?[1mSELECT "workouts".* FROM "workouts" WHERE "workouts"."id" = $1 LIMIT 1?[0m [["id", 6]]
?[1m?[35mUser Load (1.0ms)?[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
?[1m?[36m (0.0ms)?[0m ?[1mBEGIN?[0m
?[1m?[35mExercise Load (0.0ms)?[0m SELECT "exercises".* FROM "exercises" INNER JOIN "workout_exercises" ON "exercises"."id" = "workout_exercises"."exercise_id" WHERE "workout_ex
ercises"."workout_id" = $1 ORDER BY name ASC [["workout_id", 6]]
?[1m?[36m (0.0ms)?[0m ?[1mCOMMIT?[0m
Redirected to http://localhost:3000/workouts/6
And here's my form too:
<%= semantic_form_for #workout do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<%= f.text_area :description %>
<%= f.input :exercises, :as => :check_boxes, :collection => Exercise.where(:user_id => #workout.user_id) %>
<% end%>
<%= f.actions %>
<% end %>
EDIT
I'm unable to answer my question, but I've found a workaround. I don't think this is the best solution, but it works! This was added in the def update of my workouts_controller.rb
if #workout.update(workout_params)
params[:workout].each do |key,value|
if key == 'exercise_ids'
#exercises = Exercise.find(value.reject!{|a| a==""})
#workout.exercises << #exercises
end
end

How to find a document by looking for a match in an embedded document array?

This seems so simple but apparently I'm missing something. I've got a WebSite model:
class WebSite
include Mongoid::Document
has_many :domains, inverse_of: :web_site
field :name, type: String
end
The WebSite model has an embedded array of Domains:
class Domain
include Mongoid::Document
belongs_to :web_site, inverse_of: :domains
field :name, type: String
end
All I'm trying to do is find a WebSite with a domain of "test.com". Nothing I've tried seems to work, either with Mongoid or with the MongoDB console. For example, if I have one WebSite, with one Domain, with the name "test.com":
2.0.0p0 :001 > WebSite.count
=> 1
2.0.0p0 :002 > WebSite.first.domains
=> [#<Domain _id: 5148d9b76a3b8b1fe6000003, web_site_id: "5148d9a96a3b8b1fe6000002", name: "test.com">]
...then shouldn't this work?
2.0.0p0 :003 > WebSite.elem_match(domains: { name: "test.com" }).count
=> 0
2.0.0p0 :004 > WebSite.elem_match('domains' => { 'name' => "test.com" }).count
=> 0
I get zero. I should get one.
I also got the impression from the O'Reilly book that this should work:
2.0.0p0 :005 > WebSite.where('domains.name' => "test.com").count
=> 0
...same with any_in:
.0.0p0 :006 > WebSite.any_in('domains' => { 'name' => "test.com" }).count
=> 0
I've also tried those same queries from the MongoDB console with the same results:
> db.web_sites.find({"domains" : {"$elemMatch" : {"name" : "test.com"}}}).size();
0
> db.web_sites.find({"domains.name" : "test.com"}).size();
0
> db.web_sites.find({"domains" : {$in : {"name" : "test.com"}}}).size();
0
I must be missing something?
Update:
Here is some more information about the schema, from the MongoDB console:
> db.web_sites.find().pretty()
{ "_id" : ObjectId("5148d9a96a3b8b1fe6000002"), "name" : "test" }
Thanks to uldall's help with debugging (that awesome pretty() technique) I realized that my problem was that I had two separate collections side by side, instead of a Domain collection embedded in my WebSite collection. This is how my models should have been set up, with "embedded_in" and "embeds_many":
class WebSite
include Mongoid::Document
embeds_many :domains, inverse_of: :web_site
field :name, type: String
end
class Domain
include Mongoid::Document
embedded_in :web_site, inverse_of: :domains
field :name, type: String
end
Now the document looks like this from the MongoDB console:
> db.web_sites.find().pretty()
{
"_id" : ObjectId("5148e63f6a3b8b8ffa000001"),
"domains" : [
{
"_id" : ObjectId("5148e6706a3b8b8ffa000002"),
"name" : "test.com"
}
],
"name" : "test"
}
With that schema, this works:
2.0.0p0 :008 > WebSite.where('domains.name' => 'test.com').count
=> 1
[CHEER]

OR operator in Mongo 'update' $criteria

I want to check for an existing record by matching either title or url fields. If either one matches, update that record. Otherwise, insert.
How do write the following properly (using Mongoid in Ruby):
articles.update(
{ **:title => story.title OR :url => story.url** },
{ :title => story.title, :url => story.url, :source => story.source, :last_updated => Time.now },
{ :upsert => true } )
Thanks!
You need do the request and update it like :
'''
articles.any_of({:title => xxx}, {:url => yyyy}).update( :foo => 'bar')
'''

Using a .where IN clause with .includes down and up many nested levels to look up a group of IDs in Rails 3.1

I would like to filter a list of parent emails addresses by a classroom of students.
Here is a simplification of my models:
Class District
has_many :schools
has_many :families
has_many :children, :through => :families
has_many :parents, :through => :families
has_many :enrollments, :through => :children
end
Class Enrollments
belongs_to :child
belongs_to :classroom
end
The email address is associated with the parent record and I would like to filter emails by an array of classroom IDs.
I can get this to work:
idees = [49, 50]
current_district = District.first
#emails = current_district.parents.includes(:family => { :children => { :enrollments => {:classroom => { :program => :location }}}}).where("family_id IN (?)", idees)
# Returns families with ID 49 and 50
But I cannot get anything like this to work
idees = [49, 50]
current_district = District.first
#emails = current_district.parents.includes(:family => { :children => { :enrollments => {:classroom => { :program => :location }}}}).where("family.children.enrollments.classroom_id IN (?)", idees)
# Returns: PGError: ERROR: cross-database references are not implemented: family.children.enrollment.classroom_id
What am I doing wrong or is there another, better way to write this query?
The line below is the problem.
where("family.children.enrollments.classroom_id IN (?)", idees)
Change it to :
where("enrollments.classroom_id IN (?)", idees)