2016-12-05 4 views
0

Ich habe zwei Klassen: Restream und Channel. Channel könnte viele Restreams haben und restream kann zu vielen Channels gehören. Allerdings muss ich Restream auch active_channel - ID eines Kanals speichern, der es jetzt verwendet.HABTM und has_one Modellfehler

class Restream < ActiveRecord::Base 
    has_one :active_channel, class_name: 'Channel' 

    has_and_belongs_to_many :channels 
    ... 
end 

class Channel < ActiveRecord::Base 
    has_and_belongs_to_many :restreams 
    ... 

end 

Migration Ich schrieb für active_channel hinzufügen:

add_column :restreams, :active_channel, :integer, index: true 
add_foreign_key :restreams, :channels, column: :active_channel 

immer noch jedes Mal, wenn ich rails c laufen Restream.last und rufen ich dieses:

irb(main):002:0> Restream.last 
    Restream Load (1.2ms) SELECT "restreams".* FROM "restreams" ORDER BY "restreams"."id" DESC LIMIT 1 
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column channels.restream_id does not exist 
LINE 1: SELECT "channels".* FROM "channels" WHERE "channels"."restr... 
               ^
: SELECT "channels".* FROM "channels" WHERE "channels"."restream_id" = $1 ORDER BY "channels"."name" ASC LIMIT 1 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/postgresql_adapter.rb:637:in `prepare' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/postgresql_adapter.rb:637:in `prepare_statement' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/postgresql_adapter.rb:596:in `exec_cache' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/postgresql_adapter.rb:585:in `execute_and_clear' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:160:in `exec_query' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/abstract/database_statements.rb:355:in `select' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/abstract/query_cache.rb:70:in `select_all' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/querying.rb:39:in `find_by_sql' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/relation.rb:639:in `exec_queries' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/association_relation.rb:32:in `exec_queries' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/relation.rb:515:in `load' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/relation.rb:243:in `to_a' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/associations/singular_association.rb:42:in `get_records' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/associations/singular_association.rb:57:in `find_target' 
    from /bundler_cache/gems/activerecord-4.2.5.1/lib/active_record/associations/association.rb:138:in `load_target' 
... 4 levels... 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/ext/active_record.rb:45:in `each' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/ext/active_record.rb:45:in `inject' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/ext/active_record.rb:45:in `awesome_active_record_instance' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/formatter.rb:26:in `format' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/inspector.rb:137:in `unnested' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/inspector.rb:104:in `awesome' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/core_ext/kernel.rb:10:in `ai' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/core_ext/kernel.rb:20:in `ap' 
    from /bundler_cache/gems/awesome_print-1.6.1/lib/awesome_print/inspector.rb:31:in `output_value' 
    from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/console.rb:110:in `start' 
    from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/console.rb:9:in `start' 
    from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/commands_tasks.rb:68:in `console' 
    from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands/commands_tasks.rb:39:in `run_command!' 
    from /bundler_cache/gems/railties-4.2.5.1/lib/rails/commands.rb:17:in `<top (required)>' 
    from bin/rails:4:in `require' 
    from bin/rails:4:in `<main>' 

Ich fand heraus, dass es auf Anweisung schlägt fehl SELECT "channels".* FROM "channels" WHERE "channels"."restream_id" = $1 ORDER BY "channels"."name" ASC LIMIT 1

Was bin ich doi falsch? Danke

Antwort

0

Das Problem hier ist, dass der Spaltenname restream_id nicht restreams sein sollte.

Und dass Sie hinzugefügt, um eine Spalte und ausländischen Bezug auf Restream für Channel, aber hat man würde bedeuten, dass Channel zu Restream gehört, die den anderen Weg um aus dem Code.

Ich denke, Sie könnten dies leicht beheben, indem Sie sagen, dass ein Restream zu einem Channel gehört.

class Restream < ActiveRecord::Base 
    belongs_to :active_channel, class_name: 'Channel' 
    has_and_belongs_to_many :channels 
    ... 
end 

class Channel < ActiveRecord::Base 
    has_and_belongs_to_many :restreams 
    ... 
end 

Diese Lösung setzt voraus, dass die „aktiven“ Kanal auf der ReStream speichern möchten, einen Kanal bedeutet viele/eine ReStream (n) haben.

Wenn Sie andersherum möchten, müssen Sie den gesamten Code umschalten, Rollback Ihrer DB Ihre Migration ändern, dann sollte alles funktionieren.

+0

Ja, das Ändern von 'has_one' zu' gehört_to' löste es. Ich denke auch, es klingt besser mit 'gehört_to' =) Auch ich zurück zu Schienen Führer und erkannte, was das Problem war, aber jetzt versuchen zu erkennen, warum' has_one' erstellt 'ID' so. Ich würde erwarten, dass das Erstellen von IDs wie 'gehört' tatsächlich funktioniert. – Ngoral

+0

@Ngoral, 'gehört',' hat_eine', 'hat_viele' und' hat_und_zweigt_zu_vielzahl' steuert nur die Abfragen, die zum Abrufen von Datensätzen verwendet werden. Die tatsächliche Erstellung der Spalte in der Datenbank hängt jedoch von der ausgeführten Migration ab. Und 'has_one' ist etwas Besonderes, man kann es' has_many' machen, indem man einfach den Code ändert, da es das 'gehört' ist, das wirklich zählt und die fremde Beziehung hält.Der einzige Unterschied zwischen 'has_many' und' has_one' besteht darin, dass 'has_one' eine' SELECT ... LIMIT 1' Abfrage durchführt, die auf einen einzelnen Datensatz beschränkt ist. Beide verwenden jedoch das 'gehört' des anderen Modells für die Abfrage. – fbelanger

0

Sie vermasselt den Spaltennamen. In ActiveRecord-Konventionen sollten Fremdschlüsselspalten mit _id enden.

Wenn Sie die fehlerhafte Migration noch nicht durchgeführt haben, können Sie sie einfach mit rake db:rollback zurücksetzen und die Migration korrigieren.

add_column :restreams, :active_channel_id, :integer, index: true 
add_foreign_key :restreams, :channels, column: :active_channel_id 

Andernfalls müssen Sie eine neue Migration erstellen, die die Spalte umbenennt.

remove_foreign_key(:restreams, :channels, column: :active_channel) 
remove_index(:restreams, :active_channel_id) 
rename_column(:restreams, :active_channel, :active_channel_id) 
add_foreign_key(:restreams, :channels, column: :active_channel_id) 
add_index(:restreams, :active_channel_id) 

Und Sie sollten eine belongs_to und keine has_one Beziehung wie diese verwenden legt den Fremdschlüsselspalt auf der anderen Seite der Beziehung.

class Restream < ActiveRecord::Base 
    # stores the relation as `restreams.active_channel_id` 
    belongs_to :active_channel, class_name: 'Channel', 
           optional: true 

    has_and_belongs_to_many :channels 
    ... 
end 

class Channel < ActiveRecord::Base 
    # or has_one 
    has_many :active_restreams, class_name: 'Restream', 
           foreign_key: 'active_channel_id' 
    has_and_belongs_to_many :restreams 
    ... 

end 
+0

Sie haben Recht mit der Spaltenbenennung. Aber 'has_one: active_channel' wird immer noch versuchen, den Kanal zu finden, indem er' 'channels" abfragt. "Restream_id" ', was die Migration nicht tut. Das Hinzufügen einer Spalte zu restrem bedeutet, dass sie 'gehört'. – fbelanger

+0

Die Antwort mit einer Erklärung editiert, dass der Fragesteller 'goes_to' verwenden soll. – max

+0

@max Eigentlich funktioniert es jetzt, ohne eine Migration zu ändern, aber nur durch Ersetzen von' has_one' mit 'gehört_to' – Ngoral