使用 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_id
和 name
都必须存在(不是零)并且是唯一的(您不能有 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 模型添加验证,但这是个好主意。
这是一个搜索艺术家的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_id
和 name
都必须存在(不是零)并且是唯一的(您不能有 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 模型添加验证,但这是个好主意。