2016-04-18 5 views
1

Es ist eine Rake-Aufgabe, die nach einem Künstler sucht, und wenn er existiert, speichert er ihn mit Künstleralben. Ich habe versucht, eine gem zu verwenden, aber aus irgendeinem Grund gibt das Juwel etwas zurück, das ich nicht wirklich brauche. Wenn ich einen Künstler suche, funktioniert es gut.Erstellen vieler Alben mit ActiveRecord

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

wird dies zurück:

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

das ist nicht das, was ich überhaupt brauchen. Here's was ich bekommen sollte.

Also entschied ich mich, es selbst zu kodieren und dann erkannte ich, dass es ein leeres Modell speichert, alles in meinem Album ist Null. 2 Fragen:

1) Wie kann ich es beheben?

2) Wie kann ich ALLE Alben speichern, nicht nur eines?

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 

Schema:

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 
+0

Können Sie den Schema- und Modellcode für 'Album' posten? –

+0

@AnthonyE Sicher, aktualisiert den Beitrag. Album hat gehört zu: Künstler Das war's. – r0uder

Antwort

1

Antwort zu Teil 1. Sie müssen wahrscheinlich einige Modellvalidierungen für Einzigartigkeit und Präsenz hinzuzufügen. In Ihrer artist.rb-Datei:

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

Das sollte verhindern, dass Ihr Modell in einem ungültigen Zustand gespeichert wird. Jedes Attribut itunes_id und name muss sowohl vorhanden (nicht nil) als auch eindeutig sein (Sie können keine 2 'ABBA' Künstlerdatensätze haben).

Mehr zu Active Validierung finden Sie unter: http://guides.rubyonrails.org/active_record_validations.html

Sobald Sie Ihre Validierungsregeln an Ort und Stelle haben dann den Code für bestehende Datensätze zu überprüfen und zu aktualisieren, sie vereinfachen:

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

Dann erhalten wir für Fehler zu überprüfen, die den Datensatz aus persistierenden auf die Datenbank verhindert:

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 

Sobald wir diesen Block verlassen haben (wir haben ihn nicht verlassen), wissen wir, dass unser Objekt artist ein gültiges und dauerhaftes Modellobjekt ist.

Antwort zu Teil 2. Sie müssen eine Eins-zu-viele (has_many) Zuordnung zwischen dem Künstler und Album-Modell haben. Dann müssen Sie nur das Ergebnisarray durchlaufen und für jeden Eintrag ein neues Album erstellen.

Wenn Sie Ihr Schema betrachten, müssen Sie dem Albummodell ein Ganzzahlattribut mit dem Namen artist_id hinzufügen.Sie können eine Migration mit dem Befehl erstellen:

rails g migration AddArtistToAlbums artist:references 

Die Magie Befehlszeilensyntax eine richtige Migrationsdatei erzeugen soll, die wie folgt aussehen sollte:

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

Führen Sie einen rake db:migrate zu aktualisieren, um die Datenbank Schema.

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

Sie können nun an einen Künstler zugeordnet zugreifen Alben durch Verein Attribut albums ist:

In Ihrem artist.rb Modell-Datei können Sie nun die folgendes hinzuzufügen.

In Ihrem album.rb Modell-Datei können Sie nun fügen die folgende:

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

Sie können nun Zugang Künstler zu einem Album zugeordnet durch Vereinigung Attribut artist ist.

Bevor Sie gerade in der Interpretation der Antwort Körper tauchen würde ich wahrscheinlich überprüfen, um zu sehen, ob ich die richtige Art von Anfrage zuerst bekam:

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

Bevor Sie mit der Antwort umgehen können Sie die JSON analysieren müssen . Am Anfang der Datei an require 'json' dann analysieren, um die album_response.body wie:

album_response_json = JSON.parse(album_response.body) 

Danach habe ich auch der Körper, um sicherzustellen, überprüfen würde wie erwartet bevölkert.

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

Sie können auch prüfen, ob die Antwort Hash den erwarteten results Array hat.

Als nächstes wurden Zugriff Sie einen Schlüsselwert aus dem Hash-Index album_response.body[0], die eine ganze Zahl (23) basierend auf dem Beispiel JSON wäre. Ich glaube, Sie wollten auf das erste Element des Arrays results zugreifen.

Sie müssen über die Ergebnisse iterieren und ein neues Modellobjekt für jedes Album erstellen. Ich bemerkte, dass in Ihrem Beispiel JSON-Antwort, dass es eine wrapperType von ‚Künstler‘ ist, die ich nehme an, Sie wollen herausgefiltert werden, so der Code wie folgt aussehen würde:

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 

Sie sollten nun die Alben wie erwartet gespeichert .

Hinweis. Ich habe es übersprungen, dem Albummodell Validierungen hinzuzufügen, aber es wäre eine gute Idee.

+0

Vielen Dank für die tolle Antwort. Ich habe alles, was Sie geschrieben haben, und das ist, was ich jetzt bekomme: "NoMethodError: undefined Methode' each 'für "Ergebnisse": String " Es scheint, es weiß nicht, wo Ergebnisse oder so etwas zu bekommen. – r0uder

+0

Entschuldigung, ich überspringe die Details beim Parsen des Antwortkörpers.Ich habe die Antwort aktualisiert, um zu zeigen, wie der Antworttext als JSON analysiert wird. Der Fehler, den Sie bekommen haben, war, dass der Antworttext ein wirklich großer String ist, und String-Objekte haben keine "each" -Methode! Hoffe, das funktioniert jetzt für dich. –

+0

Vielen Dank, du hast meinen Tag gemacht:]. Ich wünschte, ich könnte Sie aufrüsten, aber ich werde es tun, sobald ich genug Ansehen habe. – r0uder