Env :
ubuntu
sinatra
mongoid
And my question is :
I have blog,and post model like:
class Blog
include Mongoid::Document
fields ~
has_many :posts
end
class Post
include Mongoid::Document
fields ~
belongs_to :blog
end
and then I try this in Rakefile
task :my_test do
ENV['RACK_ENV'] = 'development'
require "api.rb" #it will require all models、..etc
blog = Blog.where(urlname: "testblog").first
** p blog ** #this will output only blog attributes without posts
** p JSON.parse(blog.to_json) ** #this will output blog blog attrs and all it's post
end
I don't know what cause this difference results between them
thanks
Related
There are tons of these, especially for Rails 4.0 and Rails 3.x. I am new to nested routing and I find it very difficult, especially with forms.
So I have the following:
Routes
resources :users do
resources :api_keys, path: '/developmentcenter'
end
The relationship here is: user has many api keys where api key belongs to user.
Controller
class ApiKeysController < ApplicationController
before_action :authenticate_user!
def new
#user = User.find(params[:user_id])
#api_key = ApiKey.new(:user => #user)
end
def index
#user = User.find(params[:user_id])
#api_key = #user.api_keys
end
def create
#user = User.find(params[:user_id])
#api_key = ApiKey.new(create_new_api_key)
create_api_key(#api_key, #user)
end
def destroy
#user = User.find(params[:user_id])
destroy_api_key(#user)
end
private
def create_new_api_key
params.permit(:api_key, user_attributes: [:id])
end
end
The above is pretty basic. create_api_key is a method that does something on save, or does something else on failure to save. While destroy_api_key Just find the api key based on the user id and deletes it, does something on success and something on failure.
So now lets create a form - which has a single button for creating the API key.
<h2>Create a new Key</h2>
<%= form_for ([#user, #api_keys]) do |f| %>
<p class="button"><%= f.submit "Generate API Key" %></p>
<% end %>
All we need is a single submit button that upon click, creates a new api key for the user whom is logged in.
But wait we have a an error:
First argument in form cannot contain nil or be empty
This error takes place at:
<%= form_for ([#user, #api_keys]) do |f| %>
So - I have looked at every single one of the stack questions (well most) that deal with this error, I have changed my controller based on a few, to what you see above. I have even look at the form helpers docs in the rails manual.
I cannot figure this out.
It is telling you that #user is empty or nil in the context it is using. Either this is a user who has not been created in the DB yet, or your User.find call is not working to return an actual user. The form needs to know who #user is before it can create a nested resource (#api_key) for it. Your `create_api_key' is completely wrong. You need to whitelist your params first, then find the user in the DB (or create them), then you can use the #user instance variable to create a form for that user to create a key. I think if you do it right, you shouldn't need to call #api_keys in the beginning of the form if you defined the relationships in your models (has_many or has_one, belongs_to etc.). Can you post the web server console output when you visit that page? First off you are calling
#user = User.find(params[:user_id])
every time in your controller. You should DRY that up with a before_action.
private
def set_user
#user = User.find(api_key_params[:user_id])
end
Then at the top of the controller you would have:
class ApiKeysController < ApplicationController
before_action :authenticate_user!
before_action: :set_user
Also you should make your .permit a method that returns a variable called api_key_params:
def api_key_params
params.require(:user).permit(:api_key)
end
That way you have the things you want returned in a whitelist. To access these params you just call the method. You can specify a param you want returned from the method like my set_user example. But it also gives you the ability to do things like:
def create
#api_key = Api_key.new(api_key_params)
end
That way the Api_key.new gets the user and creates the key. You don't show how or where you generate the key itself. If it gets created by some method, you should put that in the model for Api_key. There are some other things about your code that are confusing without seeing the rest of your files.
My User model has_and_belongs_to_many :conversations.
The Conversation model embeds_many :messages.
The Message model needs to have a sender and a recipient.
I was not able to find referenced_in at the Mongoid documentation.
How do I assign the users in the message? I tried to follow something similar to this implementation, but kept getting BSON::InvalidDocument: Cannot serialize an object of class Mongoid::Relations::Referenced::In into BSON.
November 2013 Update: reference_in no longer works with Mongoid 3.0? Changed to belongs_to and it seems to work the same.
As it turns out, my structure of the Message referencing the User was appropriate, and the serialization error was related to associating the Users with the Conversation. Here is my structure and the creation steps. I appreciate any feedback on better practices, thanks.
class User
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
has_and_belongs_to_many :conversations
end
class Conversation
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
has_and_belongs_to_many :users
embeds_many :messages
end
class Message
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
embedded_in :conversation
embeds_one :sender, class_name: 'User'
embeds_one :recipient, class_name: 'User'
field :content
field :read_at, type: DateTime
field :sender_deleted, type: Boolean, default: false
field :recipient_deleted, type: Boolean, default: false
belongs_to :sender, class_name: "User", inverse_of: :sender, foreign_key: 'sender_id'
belongs_to :recipient, class_name: "User", inverse_of: :recipient, foreign_key: 'recipient_id'
end
Where before I was trying to #conversation.build(user_ids: [#user_one,#user_two]), the appropriate way is to #conversation.users.concat([#user_one,#user_two]). Then you can simply #conversation.messages.build(sender: #user_one, recipient: #user_two).
I'm not very confident about this schema design (suggestions welcome), but I have two collections:
Business
Customer
and each of them contains embedded documents:
Businesses contain Events
Customers contain Registrations
The class definitions are below, but first, the question. When I create a Business and add an Event, everything looks fine. When I create a Customer and add a Registration, everything looks fine. At this point, I can do:
ruby-1.9.2-p180 :380 > customer1.registrations.first.customer
=> #<Customer _id: BSON::ObjectId('4eb22a5bbae7a01331000019'), business_id: BSON::ObjectId('4eb215a9bae7a01331000001'), registrations: nil>
Perfect! But then... I add the Registration to the Event using event1.registrations << registration1, and now event1 has a different customer reference depending on whether I access it through the Event or through the Customer:
ruby-1.9.2-p180 :444 > customer1.registrations.first.customer
=> #<Customer _id: BSON::ObjectId('4eb22a5bbae7a01331000019'), business_id: BSON::ObjectId('4eb215a9bae7a01331000001'), posts: nil>
ruby-1.9.2-p180 :445 > business1.events.first.registrations.first.customer
=> #<Event _id: BSON::ObjectId('4eb21ab2bae7a0133100000f'), business_id: BSON::ObjectId('4eb215a9bae7a01331000001'), posts: nil>
ruby-1.9.2-p180 :446 > business1.events.first.registrations.first == customer1.registrations.first
=> true
Not perfect.... my best guess is that duplicates of registration1 have been embedded in both customer1 and event1? What I wanted was links between the Event and its many Registrations (which are owned and embedded in Customers). Is that possible with this schema?
This is what my models look like. They all have additional (and irrelevant) keys that are not displayed:
class Business
include MongoMapper::Document
many :events
many :customers
end
class Event
include MongoMapper::EmbeddedDocument
embedded_in :business
many :registrations
end
class Customer
include MongoMapper::Document
belongs_to :business
key :business_id, ObjectId
many :registrations
end
class Registration
include MongoMapper::EmbeddedDocument
embedded_in :customer
belongs_to :event
key :event_id, ObjectId
end
Yep. Registration is a MongoMapper::EmbeddedDocument so it's always embedded. That means because both Customer and Event have many :registrations, different registration objects will be embedded in each.
In MongoMapper, the embedded_in :customer just alias a customer method to return the document's parent. It's just a fancier way of calling _parent_document. That's why your event's registration's customer is an event object. (See source here).
The rule of thumb is to only embed when the child will always be used in context of its parent.
I am new to mongo/mongoid and I am trying to setup a self referencing relationships on my sites table.
# sites model
has_many :child_sites, :class_name => 'Site'
belongs_to :parent, :class_name => 'Site'
#controller
#event = current_site.child_sites.build(params[:site])
current_site is a function that returns the current site object.
I get this error -
undefined method `entries' for #
You can try changing your relation definitions to the following:
has_many :child_sites, :class_name => 'Site', :cyclic => true
belongs_to :parent_site, :class_name => 'Site', :cyclic => true
I don't know exactly what it does, but I remember it being discussed in the Mongoid google group. If that doesn't work, you should try to set inverse_of on both the relation macros. Most of the time setting inverse_of correctly does the job.
has_many :child_sites, :class_name => 'Site', :inverse_of => :parent_site
belongs_to :parent_site, :class_name => 'Site', :inverse_of => :child_sites
About the extra queries, yes there would be extra queries whenever you want to fetch child_sites of a site or the parent site of a site.
You should consider embedding child sites in parent site, but keep in mind that way you would loose the ability to query child sites in a stand_alone manner. You would always have to access any child site as "parent_site > child_sites".
Also keep in mind the 16MB limit on size of document, which is hard to reach, but might be possible if there are a lot of child sites for a parent and if you are storing template info, like html, css etc. in the document itself.
Cyclic was originally implemented for embedded documents (see user group entry). To make this working on mongoid 2.3 or higher you have to remove the cyclic option:
has_many :child_sites, :class_name => 'Site'
belongs_to :parent_site, :class_name => 'Site'
Can't you use recursively_embeds_many or recursively_embeds_one? http://mongoid.org/en/mongoid/docs/relations.html#common
I need users to be able to become fans of other users. How should I design/set this up?
I need to be able to view details of user fans.
For example. I have user: Foo. Foo has 3 fans. I'd like to be able to find the names of Foo's fans. As such:
foo = User.first
foo.name (returns 'Foo')
foo.fans.first.user.name (should return 'bar', since 'bar' is a fan of 'foo')
This is how I have it set up at the moment:
User model:
embeds_many :fans
references_many :fans
Fan model:
embedded_in :user, :inverse_of => :fans
referenced_in :user
In console, I do:
User.first.fans.create!(:user => User.first)
and I get:
Mongoid::Errors::InvalidCollection: Access to the collection for Fan is not allowed since it is an embedded document, please access a collection from the root document. I think the problem is, because the fan model is embedded in the user model which has a self-reference to the user model as well....
Your thoughts will be much appreciated.
How about a self-referential association:
class User
include Mongoid::Document
references_many :fans,
:class_name => 'User',
:stored_as => :array,
:inverse_of => :fan_of
references_many :fan_of,
:class_name => 'User',
:stored_as => :array,
:inverse_of => :fans
end
# let's say we have users: al, ed, sports_star, movie_star
sports_star.fans << al
movie_star.fans << al
sports_star.fans << ed
movie_star.fans << ed
movie_star.fans # => al, ed
al.fan_of # => sports_star, movie_star
The problem is that you are trying to do relational association using only embedded documents. When you have a Fan embedded inside a User, you can only access the Fan through its parent User. You can't do something like Fan.find(some_id) because there is no collection of Fan records.
Eventually, MongoDB will support virtual collections that will allow you to do this. For now, you have to use relational-type associations. If you want to use embedded documents in this case, you have to create some ugly and inefficient custom methods to search through parent records.
With MongoDB and Mongoid, I have found that you can switch between embedded and relational associations easily. SQL-type relationships and embedded relationships both have their place and can be used together to great effect.