I am using mongoid with my rails and am having a slight issue with running multiple .each
I have 3 models: Users, `Places', and 'Posts'
Users and Places both has_many Posts and Posts belongs_to both Users and Places
class User
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paperclip
has_many :posts
}
class Place
include Mongoid::Document
include Mongoid::Timestamps
has_many :posts
}
class Post
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paperclip
belongs_to :place
belongs_to :user
}
i have a method on my user model to get all the users within my friends, including myself like so:
def self.as_objects_with_self(id)
friends = Array.new
self.as_ids_with_self(id).each do | friend |
friends << User.find(friend)
end
friends
end
This returns back an array of all my friends including myself.
What I am trying to do, is get a list of my friends and myself, and under each user have a list of their posts, and within each post have a reference to the place it is tied to... most of this is working
resources = Friendship.as_objects_with_self(current_user.id)
resources.each do |resource|
if resource.posts.count > 0
resource[:posts] = resource.posts.all
resource[:posts].each do |post|
post[:place] = Place.find(post[:place])
end
end
end
The only part to this that isn't working, is the .each on my :posts. I can see in the logs that it's searching the place table, but nothing is being added to the collection.
It adds this output:
[
{
"_id": "556e26844a75730453170000",
"authentication_token": "Q6R4FNs5i3n1-zfQfbp8",
"created_at": "2015-06-02T21:56:20.684Z",
"dob": "1982-08-01",
"email": "test1#gmail.com",
"gender": "Male",
"last_action": null,
"name": "tester1",
"picture_content_type": null,
"picture_file_name": null,
"picture_file_size": null,
"picture_fingerprint": null,
"picture_updated_at": null,
"picture_url": "/pictures/original/missing.png",
"posts": [
{
"_id": "556e32b54a75730453270000",
"created_at": "2015-06-02T22:48:21.962Z",
"media_content_type": null,
"media_file_name": null,
"media_file_size": null,
"media_fingerprint": null,
"media_updated_at": null,
"picture_content_type": "image/jpeg",
"picture_file_name": "8580243774_479d5fe7bf_z.jpg",
"picture_file_size": 191938,
"picture_fingerprint": "61bdc1d21158c76d601b028bf826b437",
"picture_updated_at": "2015-06-02T22:48:21.768+00:00",
"place_id": "556cd5dc4a75730453010000",
"type": "picture",
"updated_at": "2015-06-02T22:48:21.962Z",
"user_id": "556e26844a75730453170000"
},
{
"_id": "556e351f4a75730453280000",
"created_at": "2015-06-02T22:58:39.761Z",
"media_content_type": null,
"media_file_name": null,
"media_file_size": null,
"media_fingerprint": null,
"media_updated_at": null,
"picture_content_type": "image/jpeg",
"picture_file_name": "8580243774_479d5fe7bf_z.jpg",
"picture_file_size": 191938,
"picture_fingerprint": "61bdc1d21158c76d601b028bf826b437",
"picture_updated_at": "2015-06-02T22:58:39.571+00:00",
"place_id": "556cd5dc4a75730453010000",
"type": "picture",
"updated_at": "2015-06-02T22:58:39.761Z",
"user_id": "556e26844a75730453170000"
}
],
"telephone": "5555555555",
"thumb_picture_url": "/pictures/thumbnail/missing.png",
"updated_at": "2015-06-02T21:56:20.705Z",
"username": "tester1"
},
{
"_id": "556cd5934a75730453000000",
"authentication_token": "RErefcuH5eDsSaNw6gCB",
"created_at": "2015-06-01T21:58:43.198Z",
"dob": "1982-08-01",
"email": "test#gmail.com",
"gender": "Male",
"last_action": null,
"name": "tester",
"picture_content_type": null,
"picture_file_name": null,
"picture_file_size": null,
"picture_fingerprint": null,
"picture_updated_at": null,
"picture_url": "/pictures/original/missing.png",
"telephone": "5555555555",
"thumb_picture_url": "/pictures/thumbnail/missing.png",
"updated_at": "2015-06-01T21:58:43.200Z",
"username": "tester"
}
]
Your issue is not with each. It's either with Place.find or with resource[:posts]
Try to replace
post[:place] = Place.find(post[:place])
with
post[:place] = Place.find(post[:place_id])
--- edit ----
replace
resource[:posts].each do |post|
post[:place] = Place.find(post[:place])
end
with
resource[:posts].map! do |post|
post[:place_id] = Place.find(post[:place_id])
post
end
or
resource[:posts].each.with_index do |post, i|
resource[:posts][i][:place] = Place.find(post[:place_id])
end
or
resource.posts.each do |post|
post[:place] = Place.find(post.place)
end
Related
I have a collection of Posts, where an Organization has many posts. A post belongs to an Account. The models look like the following:
# Post class
class Post < ApplicationRecord
belongs_to :account, foreign_key: 'account_id'
belongs_to :postable, polymorphic: true
validates :content, length: { in: 1..500 }
end
# Organization
class Organization < ApplicationRecord
has_many :posts, as: :postable
end
I can get the posts of an organization (ie in the console, I can do Organization.first.posts, which returns a shape like:
[
{
"id": 101,
"accountId": 50,
"postableType": "Organization",
"postableId": 3,
"content": "Consequatur in illum vel voluptates.",
"createdAt": "2022-11-23T04:57:45.271Z",
"updatedAt": "2022-11-23T04:57:45.271Z"
},
{
"id": 102,
"accountId": 46,
"postableType": "Organization",
"postableId": 3,
"content": "Fugit totam minus et consequatur.",
"createdAt": "2022-11-23T04:57:45.274Z",
"updatedAt": "2022-11-23T04:57:45.274Z"
},
]
With that query, what would be the best way of including the Account object with every Post returned? I tried doing something like: Organization.first.posts.includes(:account) and Organization.first.posts.joins(:account) but that just returned the same thing. Would it be by simply mapping over and doing a separate query to find the account, and merging that Account object with the Post?
For example, I'd ideally like to return something like:
[
{
"id": 101,
"account": {
"id": 46,
"firstName": "Bob"
},
"postableType": "Organization",
"postableId": 3,
"content": "Consequatur in illum vel voluptates.",
"createdAt": "2022-11-23T04:57:45.271Z",
"updatedAt": "2022-11-23T04:57:45.271Z"
},
{
"id": 102,
"account": {
"id": 46,
"firstName": "Bob"
},
"postableType": "Organization",
"postableId": 3,
"content": "Fugit totam minus et consequatur.",
"createdAt": "2022-11-23T04:57:45.274Z",
"updatedAt": "2022-11-23T04:57:45.274Z"
},
]
I was able to do this by doing:
Organization.first.posts.as_json(include: :account)
I'm not sure if there is a better ActiveRecord-y way of doing this, but it worked for my use-case above.
I have a model Category that use awesome_nested_set gem, so it has children of the model itself. I have created CategorySerializer for the model
class CategorySerializer < ActiveModel::Serializer
attributes :id, :parent_id, :lft, :rgt, :text, :permalink, :children
def children
object.children
end
end
But children is not serialized. I have also tried add has_many :children, serializer: self, the result is this
{
"id": 25918,
"parent_id": null,
"lft": 3,
"rgt": 8,
"text": "ARAG",
"permalink": "25918-arag",
"children": [
{
"id": 25919,
"parent_id": 25918,
"lft": 4,
"rgt": 7,
"text": "Coperchi",
"permalink": "25919-coperchi",
"children": [
{
"id": 25920,
"parent_id": 25919,
"lft": 5,
"rgt": 6,
"text": "Ribaltabili",
"description": "",
"page_title": "",
"meta_key": "",
"meta_description": "",
"key_1": null,
"key_2": null,
"key_3": null,
"extra": null,
"created_at": "2019-03-01T21:08:15.000+01:00",
"updated_at": "2019-04-02T12:27:05.000+02:00"
}
]
}
]
}
Second level of children is successfully serialized but it children is not. Is there a way or alternative to serialize all object children?
If you want deep nesting by default, then you can set following config property in the initializer file
# config/initializers/active_model_serializer.rb
ActiveModelSerializers.config.default_includes = '**
For more details, you can check this.
You can also add another serializer for children as follow
class CategorySerializer < ActiveModel::Serializer
attributes :id, :parent_id, :lft, :rgt, :text, :permalink, :children
def children
ActiveModel::SerializableResource.new(object.children, each_serializer: ChildrenSerializer)
end
end
For more information, you can refer to this link
How about this solution with invoking serializer on children? Be careful to not get into infinite loop with deep nesting
def children
object.children.map { |obj| SomeSerializer.new(obj) }
end
I am trying to use where query with relationships and updated_at compare.
When I send this query with updated_at params, I want to fetch only the latest reasons filtered by updated_at date, but it filters with project's updated_at, not by reasons updated_at.
How can I fix this to get reasons data with updated_at filter?
#Getting reasons with updated_at filter
https://server.com/api/v1/entries?updated_at=2016-05-02T01:12:57.204Z
#Model
User
has_many :projects
has_many :reasons, through: :projects
Project
belongs_to :user
has_many :reasons
Reasons
belongs_to :project
#Reasons Controllers
# GET /reasons
def index
reasons = current_user.reasons
updated_at = params[:updated_at]
# Filter with updated_at for reloading from mobile app
if updated_at.present?
# This is my first try, but it shows error because of relations
# reasons = reasons.where("updated_at > ?", DateTime.parse(updated_at))
# This shows reasons data, but it only compares updated_at in Project data, not reasons data...
reasons = reasons.joins(:project).where("projects.updated_at > ?", DateTime.parse(updated_at))
# Get all non deleted objects when logging in from mobile app
else
reasons = reasons.where(deleted: false)
end
render json: reasons
end
You fetching projects.updated_at, but if I understood you right you need to fetch reasons.updated_at
reasons = reasons.joins(:project).where("reasons.updated_at > ?", DateTime.parse(updated_at))
produses that output for me
GET http://localhost:3000/reasons/index?updated_at=2016-05-04T06:43:19.280Z
[
{
"id": 2,
"name": "R2",
"project_id": 3,
"created_at": "2016-05-04T06:43:19.280Z",
"updated_at": "2016-05-04T06:43:19.280Z",
"deleted": false
},
{
"id": 3,
"name": "R3",
"project_id": 3,
"created_at": "2016-05-04T06:43:25.895Z",
"updated_at": "2016-05-04T06:43:25.895Z",
"deleted": false
}
]
And without any filter
GET http://localhost:3000/reasons/index
[
{
"id": 1,
"name": "R1",
"project_id": 3,
"created_at": "2016-05-04T06:43:11.044Z",
"updated_at": "2016-05-04T06:43:11.044Z",
"deleted": false
},
{
"id": 2,
"name": "R2",
"project_id": 3,
"created_at": "2016-05-04T06:43:19.280Z",
"updated_at": "2016-05-04T06:43:19.280Z",
"deleted": false
},
{
"id": 3,
"name": "R3",
"project_id": 3,
"created_at": "2016-05-04T06:43:25.895Z",
"updated_at": "2016-05-04T06:43:25.895Z",
"deleted": false
}
]
It was just changing to reasons.updated_at, thanks!
reasons = reasons.joins(:project).where("reasons.updated_at > ?", DateTime.parse(updated_at))
I'm fairly new to Rails and have been familiarizing myself with the generators, but this is the first time I'm trying to create a custom route.
I have a star schema with the three tables Clients, Products, and Features that each have an n/n relationship to each other, so my "star" junction table has the three foreign keys, client_id, product_id, and feature_id. I created an API that'll return a list of clients, which each have a nested list of products, which each have a nested list of features. Here's the method that builds and returns the result:
def return_all
allData = Client
.includes(:products)
.includes(:features)
.to_json(:include => {:products => {:include => :features}})
render json: allData
end
The issue with this is that there are multiple features for every product, so the resulting JSON returns this (I removed all but one client and some of the data for brevity sake):
{
"id": 13,
"name": "client_1",
"created_at": "2015-10-09T18:44:56.000Z",
"updated_at": "2015-10-09T18:44:56.000Z",
"products": [
{
"id": 1,
"name": "product_1",
"created_at": "2015-10-12T03:10:34.000Z",
"updated_at": "2015-10-12T03:10:34.000Z",
"features": [
{
"id": 9,
"name": "email",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 10,
"name": "sms",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 11,
"name": "ivr",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 12,
"name": "print",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
}
]
},
{
"id": 1,
"name": "product_1",
"created_at": "2015-10-12T03:10:34.000Z",
"updated_at": "2015-10-12T03:10:34.000Z",
"features": [
{
"id": 9,
"name": "email",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 10,
"name": "sms",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 11,
"name": "ivr",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
},
{
"id": 12,
"name": "print",
"created_at": "2015-10-12T03:10:55.000Z",
"updated_at": "2015-10-12T03:10:55.000Z",
"value": null,
"group": "channel"
}
]
}
]
}
How do I go about removing the duplicate listings of products? Please let me know if you need any more information about my question.
Thanks
Edit: by request, here are my models:
class ClientsFeaturesProduct < ActiveRecord::Base
belongs_to :client
belongs_to :feature
belongs_to :product
end
class Client < ActiveRecord::Base
has_many :clients_features_products
has_many :features, through: :clients_features_products
has_many :products, through: :clients_features_products
end
class Product < ActiveRecord::Base
has_many :clients_features_products
has_many :clients, through: :clients_features_products
has_many :features, through: :clients_features_products
end
class Feature < ActiveRecord::Base
has_many :clients_features_products
has_many :clients, through: :clients_features_products
has_many :products, through: :clients_features_products
end
try using
has_many :products, -> { distinct }, through: :clients_features_products
on the Client model
see http://guides.rubyonrails.org/association_basics.html#distinct
A couple of other things
code style: use all_data instead of allData. See the style guide for more info: https://github.com/styleguide/ruby
you might want to use preload instead of includes; includes will either act like preload or like joins and you don't know which, so I tend to stay away from it; good article here: http://blog.arkency.com/2013/12/rails4-preloading/
I would try this, but it's just a guess.
def return_all
allData = Client
.joins(:products) # might need to use raw sql
.joins(:features)
.to_json(:include => {:products => {:include => :features}})
render json: allData
end
Alternatively, you could use Active Model Serializers to explicitly define a json view.
I've built a small API that, when posted a JSON object, creates the representative model records. The data looks like this:
{
"customer": {
"email": "michael#myemail.com",
"first_name": "Michael T. Smith",
"last_name": "",
"shipping_address_1": "",
"telephone": "5551211212",
"source": "Purchase"
},
"order": {
"system_order_id": "1070",
"shipping_address_1": "",
"billing_address_1": "123 Your Street",
"shipping": "0",
"tax": "0",
"total": "299",
"invoice_date": 1321157461,
"status": "PROCESSING",
"additional_specs": "This is my info!",
"line_items": [
{
"quantity": "1",
"price": "239",
"product": "Thing A",
"comments": "comments"
"specification": {
"width": "12",
"length": "12",
},
},
{
"quantity": "1",
"price": "239",
"product": "Thing A",
"comments": "comments"
"specification": {
"width": "12",
"length": "12",
},
},
]
}
}
The question is how to create the nested objects. My models are setup as such:
class Order < ActiveRecord::Base
has_many :line_items
belongs_to :customer
accepts_nested_attributes_for :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
has_many :specifications
end
class Specification < ActiveRecord::Base
belongs_to :LineItem
end
I'm trying to create the records using this code:
#order = #customer.orders.build(#data[:order])
#order.save
Is there a better way to do this? Currently I'm getting this error: ActiveRecord::AssociationTypeMismatch in ApiController#purchase_request LineItem(#70310607516240) expected, got Hash(#70310854628220)
Thanks!
accepts_nested_attributes_for defines a new setter method for the association: the original name with _attributes appended to it.
In your case, there is a line_items_attributes= method on your Order model, which is what you need to use to take advantage of the nested attributes feature. Something as simple as swapping the key before building the model would probably work, e.g.:
#data[:order][:line_items_attributes] = #data[:order].delete(:line_items)
#order = #customer.orders.build(#data[:order])
#order.save
You can use accepts_nested_attributes_for like this:
(in my example, Products has_many ProductionItineraries and ProductionItineraries belongs_to Products)
model/product.rb
has_many :production_itineraries, dependent: :delete_all
accepts_nested_attributes_for :production_itineraries, allow_destroy: true
model/production_itinerary.rb
belongs_to :product
To instantiate:
products = Product.new
products.production_itineraries.build(other_production_itineraries_fields)
Doing this, after save products, the production_itinerary object will be save automatically, with the respective product_id field