【问题标题】:Creating many albums using ActiveRecord使用 ActiveRecord 创建许多专辑
【发布时间】:2016-04-18 13:47:20
【问题描述】:

这是一个搜索艺术家的 rake 任务,如果存在,它将与艺术家专辑一起存储。我尝试使用gem,但由于某种原因,gem 返回了我并不真正需要的东西。如果我搜索一个艺术家,它工作正常。

result = ITunesSearchAPI.lookup(:id => 372976 , :entity => 'album')

将返回:

{"wrapperType"=>"artist", "artistType"=>"Artist", "artistName"=>"ABBA", "artistLinkUrl"=>"https://itunes.apple.com/us/artist/abba/id372976?uo=4", "artistId"=>372976, "amgArtistId"=>3492, "primaryGenreName"=>"Pop", "primaryGenreId"=>14} 

这根本不是我需要的。 Here's 我应该得到什么。

所以我决定自己编写代码,然后我意识到它保存了一个空模型,我相册中的所有内容都是零。 2个问题:

1) 我该如何解决?

2) 如何保存所有专辑,而不仅仅是一张?

require 'net/http'

  task :artist,[""] => :environment do |t, args|
    result = ITunesSearchAPI.search(:term => args.to_s, :entity => 'musicArtist')
    if result.empty? then puts "Nothing was found. Try another artist."
      puts result
    elsif result
      uniqueness = Artist.find_by(itunes_id: result[0]["artistId"])
      if uniqueness.nil?
        Artist.create(name: result[0]["artistName"], itunes_id: result[0]["artistId"])
        puts result
      else
        puts "The artist already exists in database"
      end
    end
    if uniqueness.nil?
    album = URI('https://itunes.apple.com/lookup')
    album_params = { :id => result[0]['artistId'], :entity => 'album'}
    album.query = URI.encode_www_form(album_params)
    album_response = Net::HTTP.get_response(album)
    puts album_response.body
    Album.create!(name: album_response.body[0]["collectionName"], artwork_url_100: album_response.body[0]["artworkUrl100"])
  end
end

架构:

ActiveRecord::Schema.define(version: 20160418120725) do

  create_table "albums", force: true do |t|
    t.string   "name"
    t.string   "artwork_url_100"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "artists", force: true do |t|
    t.string   "name"
    t.integer  "itunes_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "artists", ["itunes_id"], name: "index_artists_on_itunes_id", unique: true

end

【问题讨论】:

  • 你能发布Album的架构和模型代码吗?
  • @AnthonyE 当然,更新了帖子。专辑有 belongs_to :artist 就是这样。

标签: ruby-on-rails ruby rake


【解决方案1】:

第 1 部分的答案。您可能需要添加一些模型验证以确保唯一性和存在性。在您的艺术家.rb 文件中:

class Artist < ActiveRecord::Base
  ...
  validates :itunes_id, presence: true, uniqueness: true
  validates :name, presence: true, uniqueness: true
  ...
end

这应该可以防止您的模型以无效状态保存。每个属性 itunes_idname 都必须存在(不是 nil)并且是唯一的(不能有 2 个“ABBA”艺术家记录)。

有关 ActiveRecord 验证的更多信息,请访问: http://guides.rubyonrails.org/active_record_validations.html

一旦您制定了验证规则,那么检查现有记录并更新它们的代码就可以简化为:

artist = Artist.where(itunes_id: result[0]["artistId"]).first_or_initialize
artist.name = result[0]["artistName"]
artist.save

然后我们要检查是否存在阻止记录持久保存到数据库的错误:

if artist.errors.any?
  puts "There were errors preventing the artist being saved:"
  artist.errors.full_messages.each do |message|
    puts " - #{message}"
  end
  puts "Result data: #{result}"
  exit
end

一旦我们通过了这个块(我们还没有退出),我们就知道我们的artist 对象是一个有效且持久的模型对象。

第 2 部分的答案。 您需要在 Artist 和 Album 模型之间建立一对多 (has_many) 关联。然后,您只需遍历结果数组,为每个条目创建一个新专辑。

查看您的架构,您需要向专辑模型添加一个整数属性,名为artist_id。您可以使用以下命令创建迁移:

rails g migration AddArtistToAlbums artist:references

magic 命令行语法应该生成一个正确的迁移文件,应该如下所示:

class AddArtistToAlbums < ActiveRecord::Migration
  def change
    add_reference :albums, :artist, index: true, foreign_key: true
  end
end

运行rake db:migrate 以更新数据库架构。

在您的 artist.rb 模型文件中,您现在可以添加以下内容:

class Artist < ActiveRecord::Base
  ...
  has_many :albums
  ...
end

您现在可以通过其关联属性albums 访问与艺术家关联的专辑。

在您的 album.rb 模型文件中,您现在可以添加以下内容:

class Album < ActiveRecord::Base
  ...
  belongs_to :artist
  ...
end

您现在可以通过其关联属性artist 访问与专辑关联的艺术家。

在您直接解释响应正文之前,我可能会先检查一下我是否收到了正确的请求:

if !album_response.is_a?(Net::HTTPOK)
  puts "There was an error fetching albums."
  exit
end

在处理响应之前,您需要解析 JSON。在文件顶部require 'json' 然后解析album_response.body 像:

album_response_json = JSON.parse(album_response.body)

之后,我还会检查以确保正文按预期填充。

if !album_response_json.is_a?(Hash)
  puts "Response JSON is not a Hash as expected."
  exit
end

您还可以检查响应哈希是否具有预期的results 数组。

if !album_response_json["results"].is_a?(Array)
  puts "Response JSON does not contain the expected 'results' array."
  exit
end

接下来,您将通过索引album_response.body[0] 从哈希中访问键值,这将是基于您的示例 JSON 的整数 (23)。我认为您的意思是访问 results 数组的第一个元素。

您需要做的是迭代结果,为每个专辑创建一个新模型对象。我注意到在您的示例 JSON 响应中,有一个“艺术家”的 wrapperType,我认为您想过滤掉它,因此代码看起来像这样:

album_response_json["results"].each do |album_hash|
  next if album_hash["wrapperType"] == "artist"
  artist.albums.create!(name: album_hash["collectionName"], artwork_url_100: album_hash["artworkUrl100"])
end

您现在应该已按预期存储了相册。

注意。我跳过了向专辑模型添加验证的步骤,但这是个好主意。

【讨论】:

  • 感谢您提供如此好的答案。我做了你写的一切,这就是我现在得到的:“NoMethodError: undefined method `each' for "results":String" 它似乎不知道从哪里得到结果或类似的东西。
  • 对不起,我跳过了解析响应正文的细节。我已更新答案以显示如何将响应正文解析为 JSON。你得到的错误是响应体是一个非常大的字符串,而字符串对象没有each 方法!希望这对你有用。
  • 非常感谢,你让我开心:]。我希望我能投票给你,但一旦我获得足够的声望,我就会这样做。
  • @r0uder,我刚刚注意到您现在已经发布了您的架构,因此我更新了有关如何修改架构以添加我之前假设您的一对多关系的信息有。
  • 不能感谢你足够的人。因为你,现在一切都清楚了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2011-11-12
相关资源
最近更新 更多