Ich arbeite an einem sehr einfachen Warenkorbsystem.
Ich habe eine Tabelle items
, die eine Spalte price
vom Typ integer
hat.
Ich habe Probleme, den Preis in meinen Ansichten für Preise anzuzeigen, die sowohl Euro als auch Cent enthalten. Fehle ich etwas offensichtliches, was den Umgang mit Währung im Rails-Framework angeht?
Sie möchten wahrscheinlich einen DECIMAL
-Typ in Ihrer Datenbank verwenden. Führen Sie in Ihrer Migration Folgendes aus:
# precision is the total number of digits
# scale is the number of digits to the right of the decimal point
add_column :items, :price, :decimal, :precision => 8, :scale => 2
In Rails wird der :decimal
-Typ als BigDecimal
zurückgegeben, was sich hervorragend für die Preisberechnung eignet.
Wenn Sie darauf bestehen, Ganzzahlen zu verwenden, müssen Sie die Variable BigDecimal
s überall manuell konvertieren, was wahrscheinlich nur schmerzhaft wird.
Wie von mcl angegeben, verwenden Sie zum Ausdrucken des Preises:
number_to_currency(price, :unit => "€")
#=> €1,234.01
Hier ist ein einfacher, einfacher Ansatz, der composed_of
(Teil von ActiveRecord, unter Verwendung des ValueObject-Musters) und des Money-Gems nutzt
Du brauchst
Product
integer
-Spalte in Ihrem Modell (und Ihrer Datenbank), zum Beispiel :price
Schreiben Sie dies in Ihre product.rb
-Datei:
class Product > ActiveRecord::Base
composed_of :price,
:class_name => 'Money',
:mapping => %w(price cents),
:converter => Proc.new { |value| Money.new(value) }
# ...
Was du bekommst:
product.price = "$12.00"
wird automatisch in die Money-Klasse konvertiertproduct.price.to_s
zeigt eine dezimal formatierte Zahl an ("1234.00")product.price.format
zeigt eine ordnungsgemäß formatierte Zeichenfolge für die Währung anproduct.price.cents.to_s
Für die Handhabung von Währungen ist es üblich, den Dezimaltyp ..__ zu verwenden.
add_column :products, :price, :decimal, :precision => 8, :scale => 2
Damit können Sie mit Preisen von -999,999,99 bis 999,999,99 umgehen
Möglicherweise möchten Sie auch eine Überprüfung in Ihre Artikel einfügen, z
def validate
errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01
end
deine Werte zu überprüfen.
Verwenden Sie money-Rails gem . Es kann gut mit Geld und Währungen in Ihrem Modell umgehen und hat auch eine Reihe von Helfern, um Ihre Preise zu formatieren.
Virtuelle Attribute verwenden (Link zu überarbeitetem (bezahltem) Railscast) Sie können Ihre price_in_cents in einer ganzzahligen Spalte speichern und ein virtuelles Attribut price_in_dollars in Ihrem Produktmodell als Getter und Setter hinzufügen.
# Add a price_in_cents integer column
$ Rails g migration add_price_in_cents_to_products price_in_cents:integer
# Use virtual attributes in your Product model
# app/models/product.rb
def price_in_dollars
price_in_cents.to_d/100 if price_in_cents
end
def price_in_dollars=(dollars)
self.price_in_cents = dollars.to_d*100 if dollars.present?
end
Quelle: RailsCasts # 016: Virtuelle Attribute : Virtuelle Attribute stellen eine saubere Methode zum Hinzufügen von Formularfeldern dar, die nicht direkt der Datenbank zugeordnet sind. Hier zeige ich, wie Validierungen, Zuordnungen und Mehr.
Wenn Sie Postgres verwenden (und seit wir jetzt 2017 sind), möchten Sie vielleicht den :money
-Spaltentyp ausprobieren.
add_column :products, :price, :money, default: 0
Wenn jemand Sequel verwendet, würde die Migration ungefähr so aussehen:
add_column :products, :price, "decimal(8,2)"
irgendwie ignoriert Sequel: Präzision und: Skalierung
(Fortsetzung Version: Fortsetzung (3.39.0, 3.38.0))
Auf jeden Fall ganze Zahlen .
Und obwohl BigDecimal technisch vorhanden ist, gibt Ihnen 1.5
trotzdem einen reinen Float in Ruby.
Ich verwende es auf diesem Weg:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")
Natürlich hängt das Währungssymbol, die Genauigkeit, das Format usw. von jeder Währung ab.
Meine zugrunde liegenden APIs verwendeten alle Cent, um Geld darzustellen, und ich wollte das nicht ändern. Ich arbeitete auch nicht mit großen Geldbeträgen. Also habe ich nur eine Hilfsmethode verwendet:
sprintf("%03d", amount).insert(-3, ".")
Dadurch wird die Ganzzahl in eine Zeichenfolge mit mindestens drei Ziffern umgewandelt (wobei ggf. führende Nullen hinzugefügt werden). Anschließend wird vor den letzten beiden Ziffern ein Dezimalpunkt eingefügt, wobei niemals eine Float
verwendet wird. Von dort aus können Sie die für Ihren Anwendungsfall geeigneten Währungssymbole hinzufügen.
Es ist auf jeden Fall schnell und schmutzig, aber manchmal ist das gut!
Sie können einige Optionen an number_to_currency
(einen Standard-Helfer für View-4-Ansichten) übergeben:
number_to_currency(12.0, :precision => 2)
# => "$12.00"
Wie geschrieben von Dylan Markow
Einfacher Code für Ruby & Rails
<%= number_to_currency(1234567890.50) %>
OUT PUT => $1,234,567,890.50
Nur ein kleines Update und eine Zusammenfassung aller Antworten für aufstrebende Junioren/Anfänger in der RoR-Entwicklung, die sicherlich zu Erläuterungen kommen werden.
Verwenden Sie :decimal
, um Geld in der Datenbank zu speichern, wie @molf vorgeschlagen hat (und was mein Unternehmen als goldenen Standard verwendet, wenn Sie mit Geld arbeiten).
# precision is the total number of digits
# scale is the number of digits to the right of the decimal point
add_column :items, :price, :decimal, precision: 8, scale: 2
Einige Punkte:
:decimal
wird als BigDecimal
verwendet, wodurch viele Probleme gelöst werden.
precision
und scale
sollten je nach Darstellung angepasst werden
Wenn Sie mit dem Empfangen und Senden von Zahlungen arbeiten, erhalten Sie mit precision: 8
und scale: 2
999,999.99
den höchsten Betrag, was in 90% der Fälle in Ordnung ist.
Wenn Sie den Wert einer Immobilie oder eines seltenen Autos darstellen müssen, sollten Sie ein höheres precision
verwenden.
Wenn Sie mit Koordinaten (Längengrad und Breitengrad) arbeiten, benötigen Sie sicherlich ein höheres scale
.
Um die Migration mit dem obigen Inhalt zu generieren, führen Sie das Terminal aus:
bin/Rails g migration AddPriceToItems price:decimal{8-2}
oder
bin/Rails g migration AddPriceToItems 'price:decimal{5,2}'
wie in diesem Blog Post erklärt.
KISS die zusätzlichen Bibliotheken verabschieden sich und verwenden integrierte Helfer. Verwenden Sie number_to_currency
als @molf und @facundofarias vorgeschlagen.
Um mit dem number_to_currency
-Helfer in der Rails-Konsole zu spielen, müssen Sie einen Anruf an die ActiveSupport
-Klasse von NumberHelper
senden, um auf den Helfer zuzugreifen.
Zum Beispiel:
ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
gibt die folgende Ausgabe
2500000,61€
Überprüfen Sie das andere options
von number_to_currency helper.
Sie können es in einem Anwendungshelfer ablegen und innerhalb von Ansichten für jeden beliebigen Betrag verwenden.
module ApplicationHelper
def format_currency(amount)
number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
Sie können es auch als Instanzmethode in das Modell Item
einfügen und dort anrufen, wo Sie den Preis formatieren müssen (in Ansichten oder Helfern).
class Item < ActiveRecord::Base
def format_price
number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
Und ein Beispiel, wie ich den number_to_currency
in einem Steuerelement verwende (beachten Sie die negative_format
-Option, die für die Rückerstattung verwendet wird).
def refund_information
amount_formatted =
ActionController::Base.helpers.number_to_currency(@refund.amount, negative_format: '(%u%n)')
{
# ...
amount_formatted: amount_formatted,
# ...
}
end