[RUBY] [Rails / ActiveRecord] Ich möchte den Wert überprüfen, bevor der Typ konvertiert wird (_before_type_cast).

Einführung

Wenn Sie in ActiveRecord eine Zeichenfolge in eine Spalte vom Typ Datum oder Ganzzahl einfügen, wird diese automatisch in diesen Spaltentyp konvertiert.

Wenn Sie beispielsweise die folgende Benutzertabelle haben: (Created_at und aktualisierte_at werden der Einfachheit halber weggelassen.)

   create_table "users", force: :cascade do |t|
    t.string "name"
    t.integer "age"
  end

Versuchen wir, verschiedene Werte über die Rails-Konsole einzugeben.

 irb (main): 001: 0> User.new (Name: "Taro", Alter: 21) Versuchen Sie, eine Nummer in #age einzugeben
 => # <Benutzername: "Taro", Alter: 21>

 irb (main): 002: 0> User.new (Name: "Taro", Alter: "21") Versuchen Sie, eine Zeichenfolge in #age einzufügen
 => # <Benutzername: "Taro", Alter: 21>

 irb (main): 003: 0> User.new (Name: "Taro", Alter: "21") Versuchen Sie, eine Zeichenfolge in #age einzufügen (1 ist eine Zahl in voller Breite).
 => # <Benutzername: "Taro", Alter: 2>

Der erste ist ein numerischer Wert. Da es sich um einen numerischen Wert handelt, kann er unverändert eingegeben werden.

Der zweite ist eine Zeichenfolge. Die Zeichenfolge "21" wurde in die Nummer 21 konvertiert. Wunderbar.

Wie wäre es mit dem dritten, der 1 Teil von "21" ist eine Zahl in voller Breite. Es ist Alter geworden: 2. Es ist völlig anders als 21.

Übrigens, selbst wenn Sie "2 1" (mit einem Leerzeichen dazwischen) oder "2 a" (mit einem anderen Zeichen als einer Zahl) verwenden, ist es Alter: 2.

Was ist los

Angenommen, Sie versuchen, eine nicht numerische Eingabe abzuspielen und die folgende Validierung anzuwenden.

  validates :age, format: { with: /\A[0-9]+\z/}

In diesem Fall wird, wenn der Benutzer "2 1", "2 a" usw. eingibt, die Validierung auf das 2. Lebensjahr angewendet und als korrekte Daten gespeichert.

Lösung "\ _before_type_cast"

Die Lösung besteht darin, _before_type_cast zu age hinzuzufügen, um den vom Benutzer eingegebenen Wert anstelle des Werts nach der Konvertierung des Typs zu überprüfen. [* Referenz "before_type_cast with Rails / Validates"](https://dora.bk.tsukuba.ac.jp/~takeuchi/?%E3%82%BD%E3%83%95%E3%83%88%E3 % 82% A6% E3% 82% A7% E3% 82% A2% 2Frails% 2Fvalidates% E3% 81% A7before_type_cast)

  validates :age_before_type_cast, format: { with: /\A[0-9]+\z/ }, presence: true

Anwendungen (Konvertierung von Zeichenketten usw.)

Zusätzlich zur Validierung ist _before_type_cast auch nützlich, wenn Sie alphanumerische Zeichen in einer Zeichenfolge von voller Breite in halbe Breite konvertieren möchten oder wenn Sie nach dem Entfernen von Leerzeichen speichern möchten.

Im folgenden Code wird beispielsweise ein Edelstein namens Moji verwendet, um von voller Breite in halbe Breite zu konvertieren.

Dieses Mal verwende ich _before_type_cast, weil ich die Eingabe, die eine Mischung aus voller Breite und halber Breite ist, wie "21" (1 ist volle Breite) des Benutzers, vor der Validierung auf halbe Breite ausrichten möchte.

Nachdem die Zeichenfolge so empfangen wurde, wie sie vom Benutzer mit age_before_type_cast eingegeben wurde, und konvertiert wurde, wird die konvertierte Zeichenfolge in age gesetzt.

  validates :age_before_type_cast, format: { with: /\A[0-9]+\z/ }, presence: true

  before_validation do
    string = self.send(:age_before_type_cast)
    string = Moji.normalize_zen_han(string) 
    send(:write_attribute, :age, string)
  end

Die Art von _before_type_cast

Unterschied zwischen 〇〇 und 〇〇_before_type_cast

Erstens ist der Unterschied zwischen normalen Attributen und Attributen mit _before_type_cast. Der Wert kann nicht mit [: 〇〇_before_type_cast] erhalten werden. Speziell

 irb (main): 001: 0> user = User.new (Name: "Taro", Alter: "21")

irb(main):002:0> user.age
=> 21
irb(main):003:0> user.age_before_typecast
=> "21"

irb(main):004:0> user[:age] 
=> 21
irb(main):005:0> user[:age_before_typecast] 
=> nil 

Sie können den Wert mit user.age_before_type_cast erhalten. Wenn ich versuche, den Wert mit user [: age_before_type_cast] abzurufen, wird nil zurückgegeben.

〇〇_before_type_cast kann nicht aktualisiert werden

irb(main):001:0> user.age = 21
=> 21
irb(main):002:0> user.age_before_type_cast = 21
 => #NoMethodError tritt auf.

Da _before_type_cast zum Überprüfen der Eingabe vor der Typkonvertierung in das Alter dient, ist es natürlich nicht möglich, sich selbst zu aktualisieren.

Was passiert nach dem Speichern?

Wenn Sie speichern, sind 〇〇 und 〇〇_before_type_cast identisch. Speziell,

 irb (main): 001: 0> user = User.new (Name: "Taro", Alter: "21")
irb(main):002:0> user.save
irb(main):003:0> user.age_before_typecast
=> 21

Die Nummer 21 wird anstelle der Zeichenfolge "21" zurückgegeben.

Seien Sie beim Implementieren oder Testen aufgrund der oben genannten Eigenschaften vorsichtig!

Referenzartikel

[before_type_cast with Rails / Validates](https://dora.bk.tsukuba.ac.jp/~takeuchi/?%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6 % E3% 82% A7% E3% 82% A2% 2Frails% 2Fvalidates% E3% 81% A7before_type_cast)

Recommended Posts

[Rails / ActiveRecord] Ich möchte den Wert überprüfen, bevor der Typ konvertiert wird (_before_type_cast).
Ich möchte den Wert in Ruby erhalten
[Rails] Ich möchte alles zurücksetzen, weil die Daten in der lokalen Umgebung falsch sind! Was ist vorher zu tun?
Ich möchte ein Formular erstellen, um die Kategorie [Schienen] auszuwählen
Ich habe versucht zu verstehen, wie die Rails-Methode "redirect_to" definiert ist
Ich habe versucht zu verstehen, wie die Rails-Methode "link_to" definiert ist
Ich möchte den Wert von Attribute in Selenium of Ruby ändern
[Ruby] Ich möchte nur den Wert des Hash und nur den Schlüssel extrahieren
Ich möchte ein Komitee mit Rails vorstellen, ohne zu schmutzig zu werden
Ich möchte nur die Zeit aus Zeittypdaten abrufen ...! [Strftime] * Zusätzliche Hinweise
[Rails] Ich habe versucht, die Version von Rails von 5.0 auf 5.2 zu erhöhen
Ich habe versucht, die Sitzung in Rails zu organisieren
Ich möchte den Inhalt der Absicht var_dump
Code zum Verbinden von Rails 3 mit PostgreSQL 10
Ich möchte mit Firestore von Rails spielen
Ich möchte nach dem Dezimalpunkt abschneiden
[Rails] Ich möchte CSS mit Webpacker laden
[Rails] Ich möchte das Linkziel von link_to auf einer separaten Registerkarte anzeigen
Ich möchte die API mit Rails auf mehreren lokal eingerichteten Docker-Composes treffen
[Java] Ich möchte die Differenz zum Datum berechnen
Ich möchte eine TraceId in das Protokoll einbetten
Ich möchte den Bereich anhand des monatlichen Abschlusses beurteilen
Ich möchte ein kleines Symbol in Rails verwenden
Ich möchte die Antwort der Janken-App wissen
[Rails] Ich weiß nicht, wie ich das Modell verwenden soll ...
Ich möchte den Namen des Posters des Kommentars anzeigen
Ich möchte den Dunkelmodus mit der SWT-App verwenden
Ich möchte Benutzer mit Devise + OmniAuth bei Rails authentifizieren
Ich möchte eine Funktion in der Rails Console definieren
Ich möchte die Hauptmethode mit Reflektion aufrufen
[Grober Kommentar] Ich möchte die Zupfmethode heiraten
Ich möchte die Bildlaufposition von UITableView zurückgeben!
Ich möchte die Protokollausgabe unter Android vereinfachen
Ich möchte eine generische Anmerkung für einen Typ erstellen
Ich möchte der Kommentarfunktion eine Löschfunktion hinzufügen
[Schienen] Überprüfen Sie die Startzeit (Datum / Uhrzeit-Typ) und die Endzeit
Ich möchte herausfinden, welche Java-Version die JAR-Datei hat, die ich habe
Nachdem ich einen Artikel mit Rails Simple Calendar veröffentlicht habe, möchte ich ihn im Kalender wiedergeben.
So lösen Sie das Problem, wenn der Wert nicht gesendet wird, wenn das Formular in Schienen deaktiviert und gesendet wird
Ich möchte mit Java8 StreamAPI redu () einen anderen Typ als das Eingabeelement zurückgeben.
Ich möchte herausfinden, ob die angegebene Zeichenfolge vom Zielzeichencode unterstützt wird
[Anfänger] Ich möchte die Migrationsdatei ändern.
(´-`) .. oO (Ich möchte die Standardausgabe" Hallo "leicht finden.
Ich möchte eine mit Rails 6 erstellte App an GitHub senden
Ich möchte Tomcat auf den Server bringen und die Anwendung starten
Ich möchte ein bestimmtes Modell von ActiveRecord ReadOnly erstellen
Ich möchte die Protokollausgabeeinstellung von UtilLoggingJdbcLogger ändern
Ich möchte eine Methode aufrufen und die Nummer zählen
Ich möchte die Java 8 DateTime-API (jetzt) langsam verwenden.
Ich möchte eine andere Desinfektionsmethode als Ansicht verwenden.
Ich möchte das JDK auf meinem Mac-PC installieren
Ich möchte dem select-Attribut einen Klassennamen geben