add user 'name' to posts JSON with Ruby on Rails API - ruby-on-rails

I have a Posts model which belongs to the User model in my Rails API repo.
I have added a column in the Posts table which is 'username'.
I would like to add the current_user.name to everypost in the username column.
Can I use delegate to add the User who creates the post's Name to the username column on my Post table.
I have already added the user_id as the reference which works.
class Post < ActiveRecord::Base
belongs_to :user
def as_json(options={})
super(only: [:description, :created_at, :user_id, :username])
end
end
This is only for use as a API and everytime I access the Post show route I would like the JSON to return the Users name that the posts belongs to in the JSON under :username

The idea behind relational databases like you are using it with ActiveRecord is that you don't have to take care of things like "copying" over the user names.
If your database schema says that every Post belongs to a User and you can call the User of a Post by calling:
post.user.username
The post you are trying to get the user from has to be.
Some good examples that might help you understand this further can be found here: http://guides.rubyonrails.org/association_basics.html
To get an understanding of how you could build out and structure your Rails API you could start with this guide: https://www.codementor.io/ruby-on-rails/tutorial/creating-simple-api-with-rails

You can use ActiveModelSerializers instead of override as_json.
Look at this article : https://robots.thoughtbot.com/better-serialization-less-as-json
gem 'active_model_serializers', '~> 0.10.0'
bundle install
rails g serializer post
rails g serializer user
Adds the attributes / relations that you want to expose.
class PostSerializer < ActiveModel::Serializer
has_one :user
end
class UserSerializer < ActiveModel::Serializer
attributes :username, :user_id
end

I simply changed the format of the Post JSON to include the User association:
def as_json(options={})
super(only: [:id, :description, :created_at, :user_id],
include: [user: { only: [:name] }]
)
end

Related

rails_admin how to include a child attribute in create form

I'm trying to make a form to create new record for a model user which has one billing_information. Billing_information has an attribute account_name that I want to include in the form. I tried using the delegate method but it's not working. It produces :-
error: unknown attribute 'billing_information_account_name' for User.
class User < ActiveRecord::Base
accepts_nested_attributes_for :billing_information
has_one :billing_information, inverse_of: :user
delegate :account_name, to: :billing_information, allow_nil: true
rails_admin do
create do
field :name
field :email
field :billing_information_account_name do
def value
bindings[:object].account_name
end
end
end
end
end
Does anyone has a better solution? Thank you.
Sadly, you won't get help from rails admin in this case, but it can be done.
You have to add a new virtual field and handle in a setter the input. Take a look at this example.
class User < ApplicationRecord
has_one :billing_information, inverse_of: :user
# A getter used to populate the field value on rails admin
def billing_information_account_name
billing_information.account_name
end
# A setter that will be called with whatever the user wrote in your field
def billing_information_account_name=(name)
billing_information.update(account_name: name)
end
rails_admin do
configure :billing_information_account_name, :text do
virtual?
end
edit do
field :billing_information_account_name
end
end
end
You can always create the full billing_information using the nested attributes strategy, meaning add the billing_information field and you'll get a nice form to fill all the information.

Rails 4: ActiveModelSerializer how to include only those records which are approved?

In my Rails 4.2 API, I'm using active model serializers for constructing json response. Suppose Post is a model and it has many comments and I only want to include comments which are approved/published. I'm using a scope called approved which gives approved comments.
JSON response for post includes all comments, how do I include records which are approved and not everything. How do I construct Post serializer for that.
class PostSerializer < ActiveModel::Serializer
attributes :name, :body
has_many :comments
end
class PostSerializer < ActiveModel::Serializer
attributes :name, :body
has_many :comments
def comments
object.comments.where( status: 'approved' )
end
end
See Active Model Serializers - overriding association methods
Overriding associations in your serializer will work. In serializer just override with this method
def comments
#Your comments filtering
end
If that doesn't work then that has got to be some issue with your version of serializer. Look at this issue for more details and workarounds. https://github.com/rails-api/active_model_serializers/issues/267
Check this out too. How do I select which attributes I want for active model serializers relationships
class PostSerializer < ActiveModel::Serializer
attributes :name, :body
has_many :approved_comments, -> { where status: 'approved' }, class_name: 'Comment'
end
PostSerializer.includes(:approved_comments)
Scoping with the approved_comments. Fetching only the comments with the status of approved.
Got the concept from this
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods

How to avoid N+1?

I’m building JSON API for a mobile social networking application, users be able to have some posts and other users able to like and comments on those posts.
class Post < ActiveRecord::Base
belongs :user
end
class Like < ActiveRecord::Base
belongs :user
belongs :post, counter_cache: true
end
class PostSerializer < ActiveModel::Serializer
attributes :id, :description, :likes_count, :is_liked
def likes_count
object.likes.size # should pull from counter_cache
end
# did current_user already like this post
def is_liked
object.likes(user_id: scope.id).exists? # N+1
end
end
class PostsController < ApplicationController
def index
#posts = Post.recent
render json: #posts, each_serializer: PostSerializer
end
end
# Output of JSON API to mobile client
{
"id": 1,
"body": "...",
"likes_count": 10,
"is_liked": true
}
User logins into mobile clients and make a request to get recent posts when they open the app, but to be able to generate JSON post response with a flag is_liked, we need to issue query to hit DB to able to know if the user is already liked the post, so on mobile screen we show that status on each post on screen.
I was facing the same problem here, so let's see if I can help you...
When you add belongs_to :post, counter_cache: true in the Like model, you must add a column in the posts table called likes_count type integer.
Also, note that counter caches are incremented or decremented when you create or delete a like, so if you have some data in your database it won't work. You have to reset the counters.
I hope it helps...

Filter on parent object attribute in ActiveAdmin

I'd like to be able to filter an object based on an attribute of it's parent:
class Call < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :calls
end
I'd like to be able to do this:
ActiveAdmin.register Call do
filter :user
end
and have it filter on user.name, rather than present a select of all users. Can this be done?
Denis's solution almost worked for me. I just needed to add the filter type. For example:
ActiveAdmin.register Call do
filter :user_name, :as => :string
end
Try this:
ActiveAdmin.register Call do
filter :user_name
end
Since ActiveAdmin uses meta_search for filters their doc is very helpful: https://github.com/ernie/meta_search
In the next release of ActiveAdmin (I work with 1.0.0.pre) you can use Ransack methods. So, let say you have an Article, which belongs_to User.
You will have the following admin/article.rb file
ActiveAdmin.register Article do
controller do
def scoped_collection
Article.includes(:user)
end
end
index do
column :id
column :created_at
column :title
column("Author", sortable: 'users.first_name') { |item| link_to item.user.full_name, user_path(item.user) }
actions
end
filter :user_first_name_cont, :as => :string
filter :user_last_name_cont, :as => :string
end
Here, user_first_name_cont is ransack method which filters on associated user first_name and 'cont' means contains.
You can use nested resources from InheritedResource which is used by ActiveAdmin, so your list is automatically filtered by the parent.
ActiveAdmin.register User do
# this is the parent resource
end
ActiveAdmin.register Call do
belongs_to :user # nested below resource user
end
You can then use rake routes to see the new nested routes, generated by ActiveAdmin :) Hope it helps
I'd say it hugely depends on the types of associations you have between your models - it took me hours to figure this out.
Example
class User < ActiveRecord::Base
belongs_to :user
end
class Email < ActiveRecord::Base
has_one :email
end
To filter users based on their emails, you do this (don't forget the as: :string part as it gives you access to Ransack search methods such as contains and the likes)
ActiveAdmin.register User do
filter :user_email, :as => :string
end

Rails 3 and clean URLs

What is the most efficient way to map URLs to database IDs.
Example:
/newspaper/article/how-interesting-is-internet
In routing the newspaper_controller gets article and how-interesting-is-internet.
Where and how should I store the mapping for clean URLs and IDs?
FriendlyId is a good plugin for this (https://github.com/norman/friendly_id)
It allows you to specify a database column that will be used to create the id (name or description or whatever) and it takes care of making everything just work.
you should check out to_param method
class Article < AR::Base
def to_param
self.cool_url # cool_url is column in your articles table with your clean url
end
end
So my suggestion is to store your clean_url right in your Article model with your ID and other stuff
I like the friendlyId approach too
The way I handle this sort of thing using the clasic blog example
Blog Controller {Fetches all posts by date}
Posts Controller {}
Routes
resource :blog do
resources :posts
end
App/Models/Post.rb
class Post < ActiveRecord::Base
belongs_to :site
has_friendly_id :title, :use_slug => true
end
Then you end up with some nice paths
blog_path #Blog Index
blog_post(p) #Post Show
Use to_param method to create custom URLs such as http://myblog.com/posts/2012-04-22/123-my-first-post.html
class Post < ActiveRecord::Base
def to_param
"/posts/#{published_at}/{id}-#{title.parameterize}.html"
end
end

Resources