Hallo,
ich stehe gerade vor einem Verständnisproblem betreffend named scopes und
associations.
Ich habe zwei Models Publication und Price die durch eine
has_many-Association
verbunden sind:
class Publication < ActiveRecord::Base
has_many :prices
end
class Price < ActiveRecord::Base
belongs_to :publication
named_scope :effective,
:conditions => ['valid_from <= ? AND valid_through >= ?',
Date.today, Date.today]
end
Jeder Price hat eine durch einen Anfangs- und ein Endzeitpunkt
vorgegebene
Gültikeitsdauer.
Price.effective gibt also die Preise aus, die zum Zeitpunkt der Abfrage
gültig sind.
So weit so gut.
Jetzt benötige ich allerdings auch eine Möglichkeit, daß eine Product-Methode
sämtliche Publications zu erhalten, die Preise haben, die gerade gültig sind.
Ich könnte jetzt für Publication noch einen scope anlegen, der über include dann
den entsprechenden scope erzeugt:
class Publication < ActiveRecord::Base
has_many :prices
named_scope :effective,
:include => :prices,
:conditions => ['prices.valid_from <= ? AND
prices.valid_through
>= ?', Date.today, Date.today]
end
Aber das erscheint mir nicht wirklich DRY. Gibt es denn nicht eine
Möglichkeit,
mir die gültigen Produkte unter Verwendung des unter Price bereits angelegten
scope zu erhalten?
Viele
Grüße
Michael Kastner
on 2009-06-25 11:18
on 2009-06-25 12:08
Michael Kastner wrote: > Aber das erscheint mir nicht wirklich DRY. Gibt es denn nicht eine > Möglichkeit, > mir die gültigen Produkte unter Verwendung des unter Price bereits angelegten > scope zu erhalten? Genau das geht! Mit dem definierten named_scope im Price-Model kannst du folgendes machen: some_publication.prices.effective Damit bekommt Du alle Preise, die dem scope entsprechen und zu einer Publikation gehören. named_scopes 4tw!
on 2009-06-25 12:12
Mike Zaschka wrote: > Michael Kastner wrote: > >> Aber das erscheint mir nicht wirklich DRY. Gibt es denn nicht eine >> Möglichkeit, >> mir die gültigen Produkte unter Verwendung des unter Price bereits angelegten >> scope zu erhalten? > > Genau das geht! > > Mit dem definierten named_scope im Price-Model kannst du folgendes > machen: > > some_publication.prices.effective > > Damit bekommt Du alle Preise, die dem scope entsprechen und zu einer > Publikation gehören. > > named_scopes 4tw! Wohl zu schnell geschossen... Du wolltest ja die Publikationen haben. Da musst Du wohl tatsächlich über den Scope im Publication-Model gehen!
on 2009-06-25 12:13
Ich will aber nicht die Preise, sondern die Produkte: alle Produkte, die Preise haben, die derzeit gültig sind Mike Zaschka schrieb:
on 2009-06-25 12:19
Kein Problem! Trotzdem vielen Dank für die Antwort. Viele Grüße Michael Kastner Mike Zaschka schrieb:
on 2009-06-25 12:37
Hallo Michael. Immer dann, wenn man merkt, es geht in der aktuellen Modellierung nicht DRY, sollte man über sein Design nachdenken. Ich denke, Dir fehlt einfach eine (weitere) Abstraktion. Schon mal über ein zusätzliches (datenbankloses) Modell nachgedacht? Viele GrüßeNicolai
on 2009-06-25 12:52
Hi, ok, das Designproblem sehe ich ein, aber wo sollte das datenbanklose Modell weiterhelfen? Viele Grüße Michael Kastner Codeblogger schrieb:
on 2009-06-25 19:03
On Thursday 25 June 2009, Michael Kastner wrote: > has_many :prices > Date.today, Date.today] > So weit so gut. > > Jetzt benötige ich allerdings auch eine Möglichkeit, daß eine > Product-Methode sämtliche Publications zu erhalten, die Preise haben, > die gerade gültig sind. Den letzten Satz kann ich nicht fehlerfrei parsen, vielleicht verstehe ich dich deshalb nicht richtig. > >= ?', Date.today, Date.today] > > end Macht class Publication ... ... def current Prices.effective(:include => :publications).map(&:publication) end end was du haben willst? Nachteilig daran ist, dass du Publication#current nicht noch weiter durch Scoping einschränken kannst. Schau zur Inspiration vielleicht mal in diese Präsentation http://www.schuerig.de/michael/pres/kreative-assoz... Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
on 2009-06-25 19:32
Hallo Michael, Michael Schuerig schrieb: >> Jetzt benötige ich allerdings auch eine Möglichkeit, daß eine >> Product-Methode sämtliche Publications zu erhalten, die Preise haben, >> die gerade gültig sind. > > Den letzten Satz kann ich nicht fehlerfrei parsen, vielleicht verstehe > ich dich deshalb nicht richtig. Mein Verbalgenerator war auf random gestellt ;-) Was ich sagen wollte, war: Jetzt suche ich eine Möglichkeit, über eine Publication-Methode sämtliche Publications zu erhalten, die Preise haben, die gerade gültich sind. > class Publication ... > ... > def current > Prices.effective(:include => :publications).map(&:publication) > end > end > > was du haben willst? Super, ja genau das tut es. Muß allerdings dann so heißen class Publication ... ... def self.current Price.effective(:include => :publications).map(&:publication) end end > > Nachteilig daran ist, dass du Publication#current nicht noch weiter > durch Scoping einschränken kannst. Das macht im Moment nichts. Vielleicht brauche ich garkein weiteres Scoping. > Schau zur Inspiration vielleicht mal > in diese Präsentation > > http://www.schuerig.de/michael/pres/kreative-assoz... Die Präsentation kenne ich. Die ist klasse. Ich schau eh ab und zu auf Deinem Blog vorbei ;-) Vielen Dank für Deinen Tip. Hat mir gerade den Abend gerettet. Viele Grüße Michael Kastner
on 2009-06-25 19:37
Hallo, die Lösung mag pragmatisch sein, aber unter OO-Gesichtspunkten ist sie definitiv suboptimal. Jetzt ist auf einmal Price von Publication ohne guten Grund abhängig. Viel Spaß beim Testen, sag ich mal... Nicolai
on 2009-06-26 00:25
On Thursday 25 June 2009, Codeblogger wrote: > Hallo, > > die Lösung mag pragmatisch sein, aber unter OO-Gesichtspunkten ist > sie definitiv suboptimal. Fällt dir eine bessere Lösung ein? > Jetzt ist auf einmal Price von Publication ohne guten Grund abhängig. Das stimmt nicht, Price ist nicht von Publication abhängig, nur Publication von Price, wofür es aber einen Grund gibt. Es gibt zwar durchaus class Price < ActiveRecord::Base belongs_to :publication end Das führt aber zu keiner Abhängigkeit. Die Model-Klasse Price ist problemlos nutzbar, auch wenn keine Model-Klasse Publication existiert. Falls dich die Assoziation ernsthaft stören sollte, könntest du sie auch getrennt von der Basisfunktionalität von Price an geeigneter Stelle definieren. Etwa mit Price.class_eval { belongs_to :publication } in einem Initializer. > Viel Spaß beim Testen, sag ich mal... Wo siehst du da eine Schwierigkeit? Vielleicht täusche ich mich oder sehe einfach das, was ich von mir selbst kenne: ich habe den Eindruck, dass du mit Intuitionen auf Ruby- Code reagierst, die in einer anderen Umgebung (Java?) entstanden sind, und auf Ruby nicht zwangsläufig passen. Was meinst du? Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
on 2009-06-26 09:15
Hi! >> Jetzt ist auf einmal Price von Publication ohne guten Grund abhängig. > > Das stimmt nicht, Price ist nicht von Publication abhängig, nur > Publication von Price [...] > Stimmt, genau das habe ich auch gemeint. Harmlos ist die Abhängigkeit nicht. Die Suche nach einer Publikation mit einem gültigen Preis könnte in einer neuen Klasse abgebildet werden. Deren Verantwortung wäre es dann, die Dependency zu verwalten.
on 2009-06-26 09:43
On Friday 26 June 2009, Codeblogger wrote: > Hi! > > >> Jetzt ist auf einmal Price von Publication ohne guten Grund > >> abhängig. > > > > Das stimmt nicht, Price ist nicht von Publication abhängig, nur > > Publication von Price [...] > > Stimmt, genau das habe ich auch gemeint. Harmlos ist die Abhängigkeit > nicht. Warum nicht? Dass eine Publikation einen Preis hat, ist fachlich vorgegeben. > Die Suche nach einer Publikation mit einem gültigen Preis > könnte in einer neuen Klasse abgebildet werden. Deren Verantwortung > wäre es dann, die Dependency zu verwalten. Das würde ich gerne sehen. Wenn es eine Anforderung gäbe, gemeinsame Publikationen einer Menge von Autoren zu finden, würdest du dafür auch eine eigene Hilfsklasse anlegen? Oder würdest du alle derartigen Funktionen zu einer Klasse (Repository?) zusammenfassen? Würdest du die Suchmethoden als Instanz- oder Klassenmethoden implementieren? Ich neige dazu, Repositories als nicht Rails-gemäß anzusehen. Der Rails- Code selbst, wie auch eine Vielzahl an Erweiterungen, haben die Erwartung etabliert, dass Methoden, die sich auf alle Objekte einer Klasse beziehen, als Klassenmethoden implementiert sind. #find und named scopes sind klare Beispiele dafür. Eine physikalische Aufteilung in verschiedene Belange ist einfach mit concerned_with möglich http://m.onkey.org/2008/9/15/active-record-tips-and-tricks Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
on 2009-06-26 11:30
Michael Kastner wrote: > class Price < ActiveRecord::Base > named_scope :effective, > :conditions => ['valid_from <= ? AND valid_through >= ?', > Date.today, Date.today] > end Ich wollte noch anmerken, dass obiger Code sehr heimtückisch ist. In der Development-Umgebung läuft nämlich alles prima! Wenn aber in Production `config.cache_classes = true` ist, wird das Macro named_scope nur noch beim Laden der Anwendung ausgeführt. D.h. ich übergebe hier dem Model nur noch die Startzeit des Servers, spätestens nach 24 Stunden liefert die Abfrage aber veraltete Daten. Prinzipiell könnte man dieses Problem umgehen, wenn man statt Date.today ein Proc Object übergibt, im Fall der :conditions wird das aber nicht funktionieren. Nur meine 0,02 EUR. Beste Grüße Christoph
on 2009-06-26 11:39
Hi Christoph, guter Hinweis für 0,02 EUR. Werd' ich gleich mal korrigieren ... Vielen Dank Michael Kastner Christoph Petschnig schrieb:
on 2009-06-26 11:48
On Friday 26 June 2009, Christoph Petschnig wrote: > Ich wollte noch anmerken, dass obiger Code sehr heimtückisch ist. In > der Development-Umgebung läuft nämlich alles prima! > > Wenn aber in Production `config.cache_classes = true` ist, wird das > Macro named_scope nur noch beim Laden der Anwendung ausgeführt. D.h. > ich übergebe hier dem Model nur noch die Startzeit des Servers, > spätestens nach 24 Stunden liefert die Abfrage aber veraltete Daten. > > Prinzipiell könnte man dieses Problem umgehen, wenn man statt > Date.today ein Proc Object übergibt, im Fall der :conditions wird das > aber nicht funktionieren. Zwei Möglichkeiten: named_scope :effective, :conditions => 'valid_from <= CURRENT_DATE AND valid_through >= CURRENT_DATE' Kennt MySQL CURRENT_DATE? Im SQL:1999-Standard steht es und PostgreSQL kann es. named_scope :effective, lambda { |*args| when = args.first || Date.today :conditions => ['valid_from <= :when AND valid_through >= :when', :when => when] } Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
on 2009-06-26 13:04
Hi,
es genügt, wenn man den gesamten conditions-Hash ohne args in den
lambda-Block
setzt, damit jeweils das aktuelle Datum im SQL verwendet wird:
named_scope :effective,
lambda{
{
:conditions => ['valid_from <= ? AND valid_through >=
?',
Date.today,
Date.today]
}
}
Hab's mit gecachten Klassen probiert und hat funktioniert.
Viele Grüße und vielen Dank an die beteiligten Helfer
Michael Kastner
Michael Schuerig schrieb:
on 2009-06-26 13:27
On Friday 26 June 2009, Michael Kastner wrote: > es genügt, wenn man den gesamten conditions-Hash ohne args in den > lambda-Block setzt, damit jeweils das aktuelle Datum im SQL verwendet > wird: Ich weiß, ich wollte nur spielen. Mit der parametrisierten Variante kann man nach historischen Daten suchen. Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.