Чистый способ вернуть объект ActiveRecord с ассоциациями - PullRequest
1 голос
/ 22 февраля 2020

Я хочу вернуть все объекты модели Thing только с ассоциациями без asscoiation_id, есть ли лучший способ сделать это без include и except?

# Thing.rb

belongs_to :object_a
belongs_to :object_b

# create_thing.rb

def change
  create_table :things, id: false do |t|
    t.string :id, limit: 36, primary_key: true
    t.string :object_a_id, foreign_key: true
    t.string :object_b_id, foreign_key: true

    t.timestamps
  end
end
# things_controller.rb

render json: Thing.all, include: [:object_a, :object_b]

output => {
  id: ....
  object_a_id: 'object_a_id',
  object_b_id: 'object_b_id',
  object_a: {
    id: object_a_id
    ...
  },
  object_b: {
    id: object_b_id
    ...
  }

Я знаю, что могу сделать это, чтобы получить то, что хочу, но мне было интересно, есть ли DRY способ сделать это без включения и исключения.

render json: Thing.all, include: [:object_a, :object_b], except: [:object_a_id, :object_b_id]

output => {
  id: ....
  object_a: {
    id: object_a_id
    ...
  },
  object_b: {
    id: object_b_id
    ...
  }

1 Ответ

1 голос
/ 22 февраля 2020

Решение

Подход DRY находится внутри ваших моделей, вы можете определить метод attributes и вернуть ему форму объекта, которую вы хотите использовать функцией рендеринга.

# thing.rb

def attributes
  # Return the shape of the object
  # You can use symbols if you like instead of string keys
  {
    'id' => id,                      # id of the current thing
    'other_fields' => other_fields,  # add other fields you want in the serialized result   
    'object_a' => object_a,          # explicitly add the associations
    'object_b' => object_b
  }
end

Ассоциации object_a и object_b должны быть сериализованы как обычно. Вы можете повторить тот же подход для них, добавив метод attributes в их соответствующие классы, если вы хотите ограничить / настроить их сериализованный результат.

То есть, когда render json: вызывается для одного или коллекции из этой (ой) модели (ов) вещей, форма возвращаемых объектов json будет такой, как определено в методе выше.

Примечание:

Одно предостережение - ваши ключевые имена в ха sh, возвращаемое в attributes, должно соответствовать названию метода (или имени ассоциации). Я не слишком уверен, почему. Но обходной путь, который я использовал, когда мне нужно было добавить ключ с именем, отличным от соответствующего столбца, - это создать метод в модели имени ключа, который я хочу использовать.

Например, скажем, ваш Модель Thing имеет столбец name, но в результате json вы хотите, чтобы имя ключа, соответствующее этому столбцу, было названо name_of_thing. Вы должны сделать следующее:

def name_of_thing
  name
end

def attributes
  {
    'name_of_thing' => name_of_thing,
    # other fields follow
    # ...
  }
end

Условные логики c

Условия, зависящие от полей / ассоциаций в модели

Метод attributes может поддерживать условные на основе полей в модели.

# thing.rb

def attributes
  result = {}

  result['id'] = id
  # add other fields

  # For example, if association to object_a exists
  if object_a
    result.merge!({
      'object_a' => object_a
    })
  end

  # return result
  result
end

Условия, зависящие от ввода извне модели

Если вы хотите, чтобы ваш метод отображал разные поля в разных местах, вы можете переопределить метод as_json, который может работать лучше в этих случаях, потому что этот метод принимает параметры в параметре.

# thing.rb

def as_json(options = {})
  result = {}

  result['id'] = id
  # add other fields to result

  if(options[:show_only_ids])
    result.merge!({
      'object_a_id' => object_a_id,
      'object_b_id' => object_b_id
    })
  else
    result.merge!({
      'object_a' => object_a,
      'object_b' => object_b
    })
  end

  # return result
  result
end

Затем вам нужно изменить контроллер (или везде, где вы вызываете сериализацию модели Thing) для передачи в соответствующем параметре при необходимости.

# thing_controller.rb

render json: Thing.all.as_json({ show_only_ids: true })

При рендеринге не всегда нужно явно указывать as_json. Функция рендера будет вызывать его в любом случае по умолчанию. Вам нужно явно сделать этот вызов, когда вы хотите передать параметры.

...