Skip to content
mrbrdo edited this page Jan 4, 2015 · 3 revisions

In advanced mode, Datasource will optimize your queries even more by SELECT-ing only the database fields that are necessary (instead of table.*).

You can turn advanced mode on by turning off simple mode in the initializer:

Datasource.setup do |config|
  config.adapters = [:activerecord, :active_model_serializers]
  config.simple_mode = false
end

An example of how the queries look with advanced mode:

class PostSerializer < ActiveModel::Serializer
  attributes :id, :title
end

class UserSerializer < ActiveModel::Serializer
  attributes :id, :email
  has_many :posts
end
SELECT id, email FROM users
SELECT id, title, user_id FROM posts WHERE id IN (?)

There is a caveat: You will need to tell Datasource which database columns your virtual attributes depend on, as explained below.

Model Methods and Virtual Attributes

If you try to use a model method or virtual attribute in your serializer, you will get an error from Datasource. Since the method computes the value based on some other attributes, you need to tell Datasource what these dependencies are.

To do this, call computed in a datasource_module block like in the below example. It can depend on database columns, other computed attributes or loaders (advanced feature).

class User < ActiveRecord::Base
  datasource_module do
    # The method first_name_initial depends on first_name column
    computed :first_name_initial, :first_name
    # The method both_initials (in the serializer)
    # depends on first_name and last_name columns
    computed :both_initials, :first_name, :last_name
  end

  # method can be in model
  def first_name_initial
    first_name[0].upcase
  end
end

class UserSerializer < ActiveModel::Serializer
  attributes :first_name_initial, :both_initials

  # method can also be in serializer
  def both_initials
    object.last_name[0].upcase + object.last_name[0].upcase
  end
end
SELECT first_name, last_name FROM users

Using Datasource without a serializer

You can also use Datasource without a specific serializer:

Post.with_datasource.datasource_select(:id, :title, :comments).to_a

This is actually more or less what for_serializer does under the hood. It determines the arguments for datasource_select from the serializer's metadata.

Selecting additional attributes

You can also use for_serializer and then select additional attributes. A real world example where you might need this is in a loader:

class Comment < ActiveRecord::Base
  belongs_to :post
end

class Post < ActiveRecord::Base
  has_many :comments

  datasource_module do
    loaded :newest_comment, group_by: :post_id, one: true do |post_ids|
      Comment.for_serializer.where(post_id: post_ids)
        .group("post_id")
        .having("id = MAX(id)")
        .datasource_select(:post_id)
    end
  end
end

class CommentSerializer < ActiveModel::Serializer
  attributes :id, :comment
end

class PostSerializer < ActiveModel::Serializer
  attributes :id, :title, :newest_comment

  def newest_comment
    CommentSerializer.new(object.newest_comment).as_json
  end
end

In this case, the CommentSerializer does not need post_id, however we need it for the loader's group_by. So we used both for_serializer and also datasource_select(:post_id) to additionally select post_id.

Clone this wiki locally