使用 ActiveRecord 创建多个相册

Creating many albums using ActiveRecord

这是一个搜索艺术家的rake任务,如果存在,它将与艺术家专辑一起存储。我试过使用 gem,但出于某种原因,gem return 是我并不真正需要的东西。不过,如果我搜索艺术家,效果很好。

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

将return这个:

{"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

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

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

这应该可以防止您的模型以无效状态保存。每个属性 itunes_idname 都必须存在(不是零)并且是唯一的(您不能有 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 部分的答案。您需要在艺术家和专辑模型之间建立一对多 (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 of 'artist',我认为您想要过滤掉它,因此代码看起来像这样:

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

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

注意。我跳过了向 Album 模型添加验证,但这是个好主意。