Сохранение внешнего JSON в БД с помощью Rails

Я делаю вызов GET к внешнему источнику, используя драгоценный камень 'httparty'; вот мой Controller.rb:

def show
  response = HTTParty.get('URI')
  user = JSON.parse(response)
  user.each {|line| puts line['user']['id']}
  #the "['user']['id']" is because of the nested JSON object that is returned after the 
  parse.
end

Это возвращает правильный вывод в моей консоли rails, но теперь вопрос в том, как сохранить ['id'] в моей базе данных?

В настоящее время моя модель User имеет: id и: name; объект JSON из внешнего API отправляет: id и : name вместе с кучей другой информации, которая мне не нужна.

class User < ActiveRecord::Base
   attr_accessor :id, :name
end

Любая помощь приветствуется, спасибо!


person asing    schedule 17.01.2013    source источник
comment
Может быть, вам следует сохранить свой json id не в столбце id, а в чем-то вроде uri_id? В этом случае просто создайте новый столбец и используйте его в своем приложении. Думаю, это будет лучший вариант.   -  person tiktak    schedule 17.01.2013
comment
Верно подмечено. Я попробую это; есть ли способ перебрать массив json, чтобы сохранить его, только если: uri_id и: name?   -  person asing    schedule 17.01.2013


Ответы (2)


Во-первых, я бы посоветовал вам создать еще один столбец для идентификатора (скажем, external_id или что-то в этом роде), а не сохранять его в фактическом столбце id модели User (этот столбец очень важен в ActiveRecord, и вы действительно не хотите, чтобы установить его напрямую). Вы можете проверить уникальность этого столбца, чтобы убедиться, что вы не импортируете одни и те же пользовательские данные в несколько отдельных записей в базе данных.

Получив этот столбец, создайте метод класса в вашей модели User (я назвал свою модель save_data_from_api) для загрузки данных JSON в базу данных:

class User < ActiveRecord::Base

  # optional, but probably a good idea
  validates :external_id, :uniqueness => true

  def self.save_data_from_api
    response = HTTParty.get('URI')
    user_data = JSON.parse(response)
    users = user_data.map do |line|
      u = User.new
      u.external_id = line['user']['id']
      # set name value however you want to do that
      u.save
      u
    end
    users.select(&:persisted?)
  end

end

Что это делает:

  • Map the JSON data to a block (user_data.map) which takes each JSON user and
    1. initializes a new user (User.new)
    2. присваивает ему данные id JSON (line['user']['id'])
    3. сохраняет нового пользователя (u.save) и
    4. вернуть его (последний u в блоке).
  • Затем возьмите результат (назначенный users) и выберите только те, которые действительно существуют (были сохранены) в базе данных (users.select(&:persisted?)). Например, если у вас есть ограничение уникальности для external_id и вы пытаетесь снова загрузить данные базы данных, u.save вернет false, запись не будет (повторно) создана, и эти результаты будут отфильтрованы из результатов, возвращенных из метод.

Это может быть или не быть возвращаемым значением, которое вы хотите. Кроме того, вы, вероятно, захотите добавить в блок дополнительные назначения атрибутов (name и т. д. из данных JSON). Я оставляю это на ваше усмотрение, чтобы заполнить эти другие детали.

person Chris Salzberg    schedule 17.01.2013
comment
Эй, Сиояма, это похоже на решение, которое я ищу. В настоящее время, когда я помещаю этот код в свой проект и открываю консоль rails, я получаю ОГРОМНУЮ строку JSON, когда оцениваю User.save_data_from_api. Затем, когда я оцениваю User.all, я все равно получаю пустые значения external_id и name = nil. Идеи? - person asing; 17.01.2013
comment
Вы получаете нулевые значения external_id и name, потому что приведенный выше код не сохраняет эти атрибуты в модели, он сохраняет только поле id. Если вы хотите сохранить данные id в поле external_id, вам нужно будет добавить этот атрибут в свою модель и изменить строку u.id = line['user']['id'] на u.external_id = line['user']['id']. Кроме того, возвращаемое значение не очень важно, но если оно вас беспокоит, вы можете изменить код, чтобы, например, возвращать набор моделей. - person Chris Salzberg; 17.01.2013
comment
Я подробно изложил свой ответ, см. выше. - person Chris Salzberg; 17.01.2013
comment
Большое спасибо за помощь @shioyama ... Я новичок, и наличие подробных описаний (как вы сделали выше) действительно помогает. Однако еще один быстрый вопрос: как только я включу этот метод в свою модель, где лучше всего его вызывать? - person asing; 18.01.2013
comment
Это хороший вопрос. Это зависит от характера данных: это разовая вещь или нужно периодически подгружать пользовательские данные из API? Или вы хотите, чтобы база данных всегда синхронизировалась с данными API? - person Chris Salzberg; 18.01.2013
comment
Это статические данные, поэтому только один раз. Я все еще получаю нулевые значения при реализации приведенного выше кода. Я уже добавил атрибуты в файлы модели и миграции: gist.github.com/bba613569b946e390d5e - person asing; 18.01.2013
comment
Вы получаете нулевые значения для external_id? Я проверил код, и он работает. Похоже, у вас нет атрибута external_id в вашей модели (его нет в вашей миграции). Кроме того, почему у вас есть атрибут user_id в миграции User? Вам это не нужно, если ваши пользователи не связаны друг с другом. - person Chris Salzberg; 18.01.2013
comment
На самом деле я тестировал :name, а не :external_id. Я добавлю external_id, как только смогу успешно заполнить атрибут имени (поскольку он включен как в мою модель, так и в миграцию). Для тестирования я фактически поместил код в свой метод show в контроллере (не знал, где вызвать save_data_from_api) и отобразил код в представлении show.html.erb с помощью ‹%[email protected]% › (который в настоящее время показывает ноль). - person asing; 18.01.2013
comment
Извините, но я действительно не могу помочь вам больше, чем это. Последнее, что я бы посоветовал, это изменить u.save на u.save!, что вызовет исключение, если сохранение не удалось, с указанием причины, по которой оно не было сохранено (при условии, что проблема в этом). - person Chris Salzberg; 18.01.2013
comment
Я очень ценю всю помощь, извините, что бомбардирую вас вопросами. Вы предоставили мне более чем достаточно. - person asing; 18.01.2013
comment
Рад, что помог! Одна вещь, которую я бы посоветовал при тестировании ваших моделей: вместо того, чтобы помещать код в действие контроллера, а затем обращаться к этому действию через браузер, просто откройте консоль rails (rails c из командной строки) и попробуйте что-то там. Если что-то пошло не так, вы получите немедленную обратную связь, что упрощает отладку. - person Chris Salzberg; 19.01.2013

Здесь я предполагаю, что вы создали поле uri_id для хранения внешнего идентификатора и поле name для хранения внешнего имени в пользовательской таблице.

Код вашего контроллера —

def show
  response = HTTParty.get('URI')
  JSON.parse(response).each do |item|
    if (item.id != nil && item.name != nil)
       u = User.new(:uri_id => item.id, :name => item.name)
       u.save
    end
  end
end
person My God    schedule 17.01.2013
comment
Спасибо за ответ, Saurabh, но я не использую devise. Просто заполняю статические данные из внешнего API в мою БД. - person asing; 17.01.2013
comment
чем ваша задача становится проще, так как теперь вы берете только внешние параметры и сохраняете их в пользовательской таблице в БД. Смотрите мой обновленный ответ о том, как перебирать json и сохранять внешние параметры в db, только если: uri_id и: name. - person My God; 17.01.2013
comment
-1 Это не работает. Похоже, вы не читали объяснение OP: данные JSON вложены (с одной стороны), а также item.id, item.name и т. д. В любом случае вы не получите доступ к проанализированным данным. JSON.parse(response) — это массив хэш-объектов, поэтому вам нужно получить доступ к элементам с помощью item['id'], item['name'] и т. д. - person Chris Salzberg; 17.01.2013