Nested forms and automatic creation of parent, children - ruby-on-rails

I was wondering if it was possible to create new parent, children in a has many relationship, using rails nested forms.
Rails documentation clearly says that this works in a one to one relationship. Not sure if its the same in has many relationship.
For example:
If
params = {
:employee => {
:name => "Tester",
:account_attributes => {:login => 'tester'}
}
}
works as one to one relationship. So Employee.new(params) works fine. New employee, account are created.
Supposing I had
params = {
:employee => {
:name => "Tester",
:account_attributes => {
"0" => {:login => 'tester'},
"1" => {:login => 'tester2'}
}
}
}
Employee.new(params) doesnt work. It fails on child validations saying parent cant be blank.
Any help is appreciated. Thanks
Karen

The child_attributes= writer that comes with accepts_nested_attributes_for expects an array when it comes to one to many relationships.
This will create two accounts for the new employee
params = {
:employee => {
:name => "Tester",
:account_attributes => [
{:login => 'tester'},
{:login => 'tester2'}
]
}
}

Related

Mongoid - two query conditions to both be met in an embedded doc?

I have two models
class User
include Mongoid::Document
include Mongoid::Timestamps
field :username
embeds_many :user_tags
end
class UserTag
include Mongoid::Document
field :name
field :like_count, :type => Integer, :default => 0
embedded_in :user
end
I want to query all the users that have the user_tag named "nyc" and where the user_tag "nyc" has a like_count > 10. I've tried the following:
users = User.where('user_tags.name' => "nyc").and('user_tags.like_count' => {'$gte' => 10 })
Logically this does what it's supposed to do, but not what I need it to do. It returns users that have the user_tag "nyc" and have any user_tag with a like_count >= 10. I need users that have the user_tag "nyc" and where the user_tag "nyc"'s like_count is >= 10.
How do I do that? I'm running mongoid 4.0.2.
Actually your query is not correct for the purpose you are trying to achieve. It translates to the following MongoDB query:
db.users.find({'user_tags.name': 'nyc' }, {'user_tags.like_count': {$gte: 10}})
It means that MongoDB will find all documents with both criteria. Mongoid is returning you the same data, as MongoDB.
What you need instead is the following MongoDB query:
db.users.find({ user_tags: {
$elemMatch: {
name: 'nyc',
like_count: { $gte: 10 }
}
}})
With Mongoid you can write:
User.where(user_tags: {
'$elemMatch' => {
name: 'nyc',
like_count: { '$gte' => 10 }
}
}).count
Maybe you should write something like this:
users = User.where('user_tags.name' => "nyc", 'user_tags.like_count' => {'$gte' => 10 })
Mongoid will try to find Documents which satisfies both conditions.
You can try this
users = User.where('user_tags.name' => "nyc").where('user_tags.like_count' => {'$gte' => 10 }).all
or
users = User.where('user_tags.name' => "nyc", 'user_tags.like_count' => {'$gte' => 10 }).all

Making several methods inside includes

im trying to add some methods inside some includes with as_json in rails, but the second includes with method doesnt shows on the json. this is the code
format.json { render :json => { :projects => #projects.as_json(:include => [
:retail,
:property => {:methods => :name},
:user => {:methods => [:name, :address] } ] ) } }
and this is the json generated:
{"projects":[{"comment":null,"contractor":1,"created_at":"2013-10-17T16:57:36Z","id":5,"property_id":3,"retail_id":1,"updated_at":"2013-10-17T16:57:36Z","user_id":2,"retail":{"address":null,"created_at":"2013-10-16T22:29:38Z","id":1,"name":"asdasdsa","phone":"6677969224","property_id":null,"social_reason_id":null,"updated_at":"2013-10-17T17:18:09Z","web":"www.boxie.io"},"property":{"city":"Culiacan","colony":"Fracc Los Sauces","coordinates":"(24.7802016, -107.34894730000002)","country_id":1,"created_at":"2013-10-17T00:56:32Z","external_number":null,"id":3,"internal_number":2848,"population":null,"state_id":4,"street":"Alame\u00f1a","updated_at":"2013-10-17T00:56:32Z","zipcode":8028,"name":"Calle Alame\u00f1a, Fracc Los Sauces, 8028, Culiacan, Campeche Mexico"}}]}
as you can see the user information doesn't appear on the json.
Thank you
You didn't wrap them correctly, Your second and third child association should be hashes.
#projects.as_json(:include => [
:retail,
{ :property => {:methods => :name } },
{ :user => {:methods => [:name, :address] } }
]

Create json document from ActiveRecord and avoid N+1

I'm trying to create a json array (string actually) based on my db structure. I have the following relationship:
Country > State > City
The way I'm doing it now is very innefficient (N+1):
data = "[" + Country.all.map{ |country|
{
name: country.name,
states: country.states_data
}.to_json
}.join(",") + "]"
Then on the Country model:
def states_data
ret_states = []
states.all.each do |state|
ret_states.push name: state.name, cities: state.cities_data
end
ret_states
end
Then on the State model:
def cities_data
ret_cities = []
cities.all.each do |city|
ret_cities.push name: city.name, population: city.population
end
ret_cities
end
How can I do this more efficiently?
Eager load the states and cities. Just be careful because this could take up a lot of memory for large datasets. See documentation here http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations. Whenever possible I like using joins in addition to includes to fetch all data at once.
#to_json will also serialize Arrays for you, so you don't need to manually add bits of JSON.
Your code from above could be altered like so:
data = Country.joins(:states => :cities).includes(:states => :cities).all.map{ |country|
{
name: country.name,
states: country.states_data
}
}.to_json
But you could also remove the need for the _data methods.
data = Country.joins(:states => :cities).includes(:states => :cities).to_json(
:only => :name,
:include => {
:states => {
:only => :name,
:include => {
:cities => {
:only => [:name, :population]
}
}
}
}
)
That is pretty ugly, so you may want to look into overriding #as_json for each of your models. There is a lot of information about that available on the web.
u can provide the model to be included when converting to json.
country.to_json(:include => {:states => {:include => :cities}})
check http://apidock.com/rails/ActiveRecord/Serialization/to_json for

Find Or Create Rails Nested Attribute By Name

I have a Model called Example that accepts_nested_attributes_for NestedExample.
When I create a new Example Model I can also create NestedExamples:
params = { :name => 'Example 1', :nested_example_attributes => { :name => 'Nested Example 1' } }
Example.find_or_create_by_name params
This is all working fine. However, I rather than creating a new NestedExample every time, I would like Rails to perform a find_or_create_by_name on the NestedExample model, so that in the above situation, if there is already a NestedModel with a name of Nested Example 1, it will be used, rather than a new instance of NestedExample with the same name.
My current result:
params_1 = { :name => 'Example 1', :nested_example_attributes => { :name => 'Nested Example 1' } }
params_2 = { :name => 'Example 2', :nested_example_attributes => { :name => 'Nested Example 1' } }
example_1 = Example.find_or_create_by_name params_1
example_2 = Example.find_or_create_by_name params_2
puts example_1.nested_example.id == example_2.nested_example.id # Should be true

Why is the order of execution of attribute assignments failing?

I am passing these parameters to a controller:
{
"utf8" => "✓",
"authenticity_token" => "ersjaJ4/ieZelVifP/YpBHTJtiQ53HgO5KYjEdW0BlQ=",
"transaction" => {
"use_balance" => "1",
"traces_attributes" => {
"trace_ids" => ["6"],
"6" => {
"amount" => "12.0",
"charge_id" => "6"
}
},
"positive_balance" => "12",
"property_id" => "2",
"community_id" => "1"
},
"commit" => "Save Payment",
"community_id" => "1",
"property_id" => "2"
}
The controller#create then:
#payment = Transaction.new(params[:transaction])
Then the Transaction model:
belongs_to :property
belongs_to :community
attr_accessible :positive_balance
def traces_attributes=(params)
#INSIDE HERE THE VALUES OF
#params[:trace_ids] => ['6'] OK
#BUT
#self.possitive_balance => "" **NOT OK**
#self.property_id => nil **NOT OK**
end
My hypothesis is that traces_attribute= is executed before positive_balance= and property_id
Can I change this?
Why is this failing?
The order of the assignments should be the same as the order of the params in the form, but I don't think this is guaranteed.
A safer solution would be to only store the data in the traces_attributes= method, and access the other attributes later, for example in a before_save callback.
it looks based on the transaction hash that the property_id is outside of that hash so if you are building based on transactions it won't have a property_id
"transaction"=>{"use_balance"=>"1",
"traces_attributes"=>{"trace_ids"=>["6"],
"6"=>{"amount"=>"12.0",
"charge_id"=>"6"
}
},
"positive_balance"=>"12",
"property_id"=>"2",
"community_id"=>"1"
},
"commit"=>"Save Payment",
"community_id"=>"1",
"property_id"=>"2"}
do you see what i mean, the number of curly braces is messed up and prop. id isn't ending up in transactions ( i just copy and pasted your code pasted above )

Resources