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
Related
I'm using AJAX to get some results, but the problem is I'm querying two separate models and I want to return both and their relationship to one another as one JSON object.
Here's an example of two models I'm trying to link together -
Car
belongs_to :user
:id
:make
:year
:user_id
User
has_many :cars
:id
:first_name
:last_name
:birthday
I'm trying to get it to look something like this -
{
1: {
id: 1,
first_name: 'Joe'
last_name: 'Smith'
cars: {
23: {
id: 23,
make: 'BMW',
year: 2009,
user_id: 1
},
24: {
id: 24,
make: 'Volvo',
year: 2012,
user_id: 1
}
}
},
2: {
id: 2,
first_name: 'Bob'
last_name: 'Johnson'
cars: {
35: {
id: 35,
make: 'Ford',
year: 2013,
user_id: 2
}
}
}
}
Create a new (private) method in your controller:
def format_json(users)
result = {}
users.each do |user|
result[user.id] = user.formatted_data
end
return result
end
Change the action to return:
users = Users.includes(:cars).where("<your_where_clause>").limit(<n>)
render :json => { :result => format_json(users) }.to_json
app/models/user.rb
class User < ActiveRecord::Base
def formatted_data
{
:id => self.id,
:first_name => self.first_name,
:last_name => self.last_name,
:cars => self.get_car_info
}
end
def get_car_info
car_info = {}
self.cars.each do |car|
car_info[car.id] = car.info
end
return car_info
end
end
app/models/car.rb
class Car < ActiveRecord::Base
def info
{
:id => self.id,
:make => self.make,
:year => self.year,
:user_id => self.user_id
}
end
end
I ended up using the .as_json I found detailed here to convert ActiveRecord to a plain Ruby hash and passed that hash to the view.
user = User.find(params[:user_id].to_i)
#json = {}
#json[user.id] = user.as_json
#json[user.id]['cars'] = {}
user.cars.each do |car|
#json[user.id]['cars'][car.id] = car.as_json
end
render json: { status: true, users: #json }
How I can create a hash like this in a cycle ?
User.items.each do |m|
......
Result:
test = [{:name => 'Unit 1', :price => "10.00"},
{:name => 'Unit 2', :price => "12.00"},
{:name => 'Unit 3', :price => "14.00"}]]
You can use map to return hashes that you build.
Assuming your Item resource responds to name and price, it would look like
test = User.items.map do |m|
{
name: m.name,
price: m.price
}
end
You also can do like this:
Item.connection.select_all("select name, price from items where user_id = xxxxx;")
you will get an array containing hash, like this:
[{"name"=>"xxx", "price"=> xxx},{}......]
I have a few constants which are arrays that I don't want to create databse records for but I don't know where to store the constants without getting errors.
For example
CONTAINER_SIZES = [["20 foot"],["40 foot"]]
Where can I store this so all models and controller have access to this?
I will write my way to you.
class User < ActiveRecord::Base
STATES = {
:active => {:id => 100, :name => "active", :label => "Active User"},
:passive => {:id => 110, :name => "passive", :label => "Passive User"},
:deleted => {:id => 120, :name => "deleted", :label => "Deleted User"}
}
# and methods for calling states of user
def self.find_state(value)
if value.class == Fixnum
Post::STATES.collect { |key, state|
return state if state.inspect.index(value.to_s)
}
elsif value.class == Symbol
Post::STATES[value]
end
end
end
so i can call it like
User.find_state(:active)[:id]
or
User.find_state(#user.state_id)[:label]
Also if i want to load all states to a select box and if i don't want some states in it (like deleted state)
def self.states(arg = nil)
states = Post::STATES
states.delete(:deleted)
states.collect { |key, state|
if arg.nil?
state
else
state[arg]
end
}
end
And i can use it now like
select_tag 'state_id', User.states.collect { |s| [s[:label], s[:id]] }
I put them directly in the model class.
class User < ActiveRecord::Base
USER_STATUS_ACTIVE = "ACT"
USER_TYPES = ["MANAGER","DEVELOPER"]
end
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'}
]
}
}
What do you think is the most optimal way to retrieve all attributes for all the associations an AR model has?
i.e: let's say we have the model Target.
class Target < ActiveRecord::Base
has_many :countries
has_many :cities
has_many :towns
has_many :colleges
has_many :tags
accepts_nested_attributes_for :countries, :cities, ...
end
I'd like to retrieve all the association's attributes by calling a method on a Target instance:
target.associations_attributes
>> { :countries => { "1" => { :name => "United States", :code => "US", :id => 1 },
"2" => { :name => "Canada", :code => "CA", :id => 2 } },
:cities => { "1" => { :name => "New York", :region_id => 1, :id => 1 } },
:regions => { ... },
:colleges => { ... }, ....
}
Currently I make this work by iterating on each association, and then on each model of the association, But it's kind of expensive, How do you think I can optimize this?
Just a note: I realized you can't call target.countries_attributes on has_many associations with nested_attributes, one_to_one associations allow to call target.country_attributes
I'm not clear on what you mean with iterating on all associations. Are you already using reflections?
Still curious if there's a neater way, but this is what I could come up with, which more or less results in the hash you're showing in your example:
class Target < ActiveRecord::Base
has_many :tags
def associations_attributes
# Get a list of symbols of the association names in this class
association_names = self.class.reflect_on_all_associations.collect { |r| r.name }
# Fetch myself again, but include all associations
me = self.class.find self.id, :include => association_names
# Collect an array of pairs, which we can use to build the hash we want
pairs = association_names.collect do |association_name|
# Get the association object(s)
object_or_array = me.send(association_name)
# Build the single pair for this association
if object_or_array.is_a? Array
# If this is a has_many or the like, use the same array-of-pairs trick
# to build a hash of "id => attributes"
association_pairs = object_or_array.collect { |o| [o.id, o.attributes] }
[association_name, Hash[*association_pairs.flatten(1)]]
else
# has_one, belongs_to, etc.
[association_name, object_or_array.attributes]
end
end
# Build the final hash
Hash[*pairs.flatten(1)]
end
end
And here's an irb session through script/console to show how it works. First, some environment:
>> t = Target.create! :name => 'foobar'
=> #<Target id: 1, name: "foobar">
>> t.tags.create! :name => 'blueish'
=> #<Tag id: 1, name: "blueish", target_id: 1>
>> t.tags.create! :name => 'friendly'
=> #<Tag id: 2, name: "friendly", target_id: 1>
>> t.tags
=> [#<Tag id: 1, name: "blueish", target_id: 1>, #<Tag id: 2, name: "friendly", target_id: 1>]
And here's the output from the new method:
>> t.associations_attributes
=> {:tags=>{1=>{"id"=>1, "name"=>"blueish", "target_id"=>1}, 2=>{"id"=>2, "name"=>"friendly", "target_id"=>1}}}
try this with exception handling:
class Target < ActiveRecord::Base
def associations_attributes
tmp = {}
self.class.reflections.symbolize_keys.keys.each do |key|
begin
data = self.send(key) || {}
if data.is_a?(ActiveRecord::Base)
tmp[key] = data.attributes.symbolize_keys!
else
mapped_data = data.map { |item| item.attributes.symbolize_keys! }
tmp[key] = mapped_data.each_with_index.to_h.invert
end
rescue Exception => e
tmp[key] = e.message
end
end
tmp
end
end
This is updated version of Stéphan Kochen's code for Rails 4.2
def associations_attributes
association_names = self.class.reflect_on_all_associations.collect { |r| r.name }
me = self.class.includes(association_names).find self.id
pairs = association_names.collect do |association_name|
object_or_array = me.send(association_name)
if object_or_array.is_a? ActiveRecord::Associations::CollectionProxy
association_pairs = object_or_array.collect { |o| [o.id, o.attributes] }
[association_name, Hash[*association_pairs.flatten(1)]]
else
[association_name, object_or_array.attributes]
end
end
Hash[*pairs.flatten(1)]
end