Ruby on Rails: ActiveRecord: Beziehungen


In unserer Glossarverwaltung gibt es mehrere Kunden. Jeder hat sein eigenes Glossar mit vielen Glossareinträgen. Wie modellieren wir das? In einem Projekt gibt es mehrere Aufgaben. Jede Abteilung hat einen Abteilungsleiter. Ein Blogeintrag hat eine Überschrift und gehört zu vielen Kategorien. Offensichtlich sind Beziehungen zwischen Objekten etwas, was häufig vorkommt. Und natürlich könnten wir jede inividuell modellieren. Aber weil Beziehungen zwischen Objekten ein Standardproblem sind, bietet uns Rails Musterlösungen an, die wir ggf. anpassen können.

Die belongs_to Beziehung

Bearbeiten

Fangen wir mit einer belongs-to Beziehung an. Betrachten wir als Beispiel die Beziehung zwischen einem Abteilungsleiter (manager) und einer Abteilung (department). Wir können behaupten, dass der manager zum department gehört. Das Modell dazu sieht dann so aus:

script/generate model department name:string description:text
script/generate model manager name:string department_id:integer

class Manager < ActiveRecord::Base 
  belongs_to :department 
end

Die Zuordnung des manager zum department über die department_id ist eindeutig und über diese id kann man leicht das department zu einem manager herausfinden.

Die has_one Beziehung

Bearbeiten

Mit einer has_one Beziehung können wir die Gegenrichtung modellieren. Das Datenbankmodel bleibt und wir schreiben:

class Department < ActiveRecord::Base 
  has_one :manager
end

Da wir im department die manager_id nicht kennen, müssen wir alle manager durchsuchen und ihre department_id checken, wenn wir den manager zu einem department herausfinden wollen.

Zusammen mit der belongs_to Beziehung haben wir jetzt eine 1:1 Beziehung modelliert, mit dem Fremdschlüssel department_id beim manager. Mit den belongs_to und has_one Beziehungen stellt uns Rails eine Reihe von Helper-methoden zur Verfügung:

  test "we can create a department and manger without association" do
    dep_edv = Department.create(:name => 'Department for EDV') 
    mgr_edv = Manager.create(:name => 'Egon Data')
    
    assert_equal 'Department for EDV', dep_edv.name
    assert_equal 'Egon Data', mgr_edv.name
    assert_nil mgr_edv.department
    assert_nil dep_edv.manager
  end
  
  test "we can associate department to manger (with manager-method)" do
    dep_edv = Department.create(:name => 'Department for EDV') 
    mgr_edv = Manager.create(:name => 'Egon Data')
    
    # in one direction we can navigate through the relationship 
    # this is because the (yet unsaved) manager already knows the department_id
    mgr_edv.department = dep_edv
    assert_equal dep_edv, mgr_edv.department
    
    # for navigating in the other direction we have to save the ralationship data first
    # i.e we need to save the manager
    mgr_edv.save
    # now we can navigate the relationship in the opposite direction too
    # (this involves a seach for the manger with the appropriate id and thats why it has to be saved first)
    assert_equal mgr_edv, dep_edv.manager
  end   
  
  test "we can use build to create a department associaed to a manger" do
    mgr_edv = Manager.create(:name => 'Egon Data')
    dep_edv = mgr_edv.build_department(:name => 'Department for EDV')
    
    assert_equal dep_edv, mgr_edv.department
    mgr_edv.save
    assert_equal mgr_edv, dep_edv.manager
  end
  
  # TODO build/create in die andere Richtung!
  # TODO Unterschied zwischen build_other und create_other

Die has_many Beziehung

Bearbeiten

Jetzt nehmen wir an, dass die Abteilungen nicht nur einen Abteilungsleiter sondern auch Angestellte (employees) haben. Das modellieren wir mit einer has_many Beziehung. Das macht die Zeile "has_many :employees". Die has_one Beziehung für den manager lassen wir wie sie ist. Die Abteilung soll ja weiterhin einen Abteilungsleiter haben.

script/generate model employee name:string department_id:integer
class Department < ActiveRecord::Base 
  has_one :manager
  has_many :employees
end

Auch die belongs_to Beziehung in Gegenrichtung kennen wir schon.

class Employee < ActiveRecord::Base
  belongs_to :department
end

Zusammen haben wir damit zusätzlich zur 1:1 Beziehung eine 1:n Beziehung modeliert und wieder stellt uns Rails eine Reihe von praktischen Methoden zur Verfügung. ..