web-dev-qa-db-fra.com

Rails: Forcer la chaîne vide à NULL dans la base de données

Existe-t-il un moyen simple (c’est-à-dire une configuration) de forcer ActiveRecord à enregistrer les chaînes vides en tant que NULL dans la base de données (si la colonne le permet)? 

La raison en est que si vous avez une colonne de chaîne NULLable dans la base de données sans valeur par défaut, les nouveaux enregistrements ne définissant pas cette valeur contiendront NULL, tandis que les nouveaux enregistrements définissant cette valeur sur la chaîne vide ne seront pas NULL, ce qui conduit aux incohérences dans la base de données que je voudrais éviter.

En ce moment, je fais ce genre de choses dans mes modèles:

before_save :set_nil

def set_nil
  [:foo, :bar].each do |att|
    self[att] = nil if self[att].blank?
  end
end

ce qui fonctionne mais n'est pas très efficace ou sec. Je pourrais en tenir compte dans une méthode et la mélanger dans ActiveRecord, mais avant de suivre cette voie, j'aimerais savoir s'il existe déjà un moyen de le faire.

42
Thilo

Essayez si cette gemme fonctionne:

https://github.com/rubiety/nilify_blanks

Fournit une structure pour enregistrer les valeurs vides entrantes en tant que nil dans la base de données dans les cas où vous préférez utiliser DB NULL plutôt qu'une simple chaîne vide ...

Dans Rails, lorsque l'utilisateur enregistre un modèle à partir d'un formulaire et que les valeurs ne sont pas fournies par l'utilisateur, une chaîne vide est enregistrée dans la base de données au lieu d'un NULL comme beaucoup le souhaiteraient (le mélange d'espaces et de NULL peut prêter à confusion). Ce plugin vous permet de spécifier une liste d'attributs (ou d'exceptions de tous les attributs) qui seront convertis en nil s'ils sont vides avant l'enregistrement d'un modèle.

Seuls les attributs répondant à blank? avec une valeur de true sera converti en nil. Par conséquent, cela ne fonctionne pas avec les champs entiers avec la valeur 0, par exemple ...

19
dexter

Oui, la seule option pour le moment est d'utiliser un rappel.

before_save :normalize_blank_values

def normalize_blank_values
  attributes.each do |column, value|
    self[column].present? || self[column] = nil
  end
end

Vous pouvez convertir le code en un mix pour l'inclure facilement dans plusieurs modèles.

module NormalizeBlankValues
  extend ActiveSupport::Concern

  included do
    before_save :normalize_blank_values
  end

  def normalize_blank_values
    attributes.each do |column, value|
      self[column].present? || self[column] = nil
    end
  end

end

class User
  include NormalizeBlankValues
end

Ou vous pouvez le définir dans ActiveRecord :: Base pour l’avoir dans tous vos modèles.

Enfin, vous pouvez également l'inclure dans ActiveRecord :: Base mais l'activer si nécessaire.

module NormalizeBlankValues
  extend ActiveSupport::Concern

  def normalize_blank_values
    attributes.each do |column, value|
      self[column].present? || self[column] = nil
    end
  end

  module ClassMethods
    def normalize_blank_values
      before_save :normalize_blank_values
    end
  end

end

ActiveRecord::Base.send(:include, NormalizeBlankValues)

class User
end

class Post
  normalize_blank_values

  # ...
end
48
Simone Carletti

Une autre option consiste à fournir des paramètres personnalisés, au lieu de gérer cela dans un crochet. Par exemple.:

def foo=(val)
  super(val == "" ? nil : val)
end
6
troelskn

Ma suggestion:

# app/models/contact_message.rb
class ContactMessage < ActiveRecord::Base
  include CommonValidations
  include Shared::Normalizer
end


# app/models/concerns/shared/normalizer.rb
module Shared::Normalizer
  extend ActiveSupport::Concern

  included do
    before_save :nilify_blanks
  end

  def nilify_blanks
    attributes.each do |column, value|
      # ugly but work
      # self[column] = nil if !self[column].present? && self[column] != false

      # best way
      #
      self[column] = nil if self[column].kind_of? String and self[column].empty?
    end
  end

end
4
klay

Désolé pour le nécropostage, mais je n'ai pas trouvé la chose exacte ici dans les réponses, si vous avez besoin d'une solution pour spécifier les champs qui doivent être nilifiés:

module EnforceNil
  extend ActiveSupport::Concern

  module ClassMethods
    def enforce_nil(*args)
      self.class_eval do
        define_method(:enforce_nil) do
          args.each do |argument|
            field=self.send(argument)
            self.send("#{argument}=", nil)  if field.blank?
          end
        end           
        before_save :enforce_nil
      end
    end
  end
end

ActiveRecord::Base.send(:include, EnforceNil)

Par ici:

class User
  enforce_nil :phone #,:is_hobbit, etc  
end

L'application de certains champs est pratique lorsque vous avez field1 et field2. Field1 a un index unique en SQL, mais peut être vide, vous avez donc besoin d'une application (NULL est considéré comme unique, "" - pas par SQL), mais pour Field2, vous ne vous en souciez pas vraiment et vous avez déjà des dizaines de callbacks ou méthodes qui fonctionnent. lorsque field2 est "", mais creusez votre application sous la couche d'erreurs si field2 est nil. Situation à laquelle je suis confronté.

Peut être utile pour quelqu'un.

2
Joe Half Face

Strip Attributes Gem

Il existe une gemme pratique qui le fait automatiquement lors de la sauvegarde d'un enregistrement, que ce soit sous une forme utilisateur, dans la console ou dans une tâche rake, etc.

Elle s'appelle strip_attributes et est extrêmement facile à utiliser, avec des valeurs par défaut saines dès la sortie de la boîte.

Il fait par défaut deux choses principales qui devraient toujours être effectuées: presque

  1. Enlève les espaces blancs de début et de fin: 

    " My Value " #=> "My Value"
    
  2. Transformez les chaînes vides en NULL:

    ""  #=> NULL
    " " #=> NULL
    

Installer

Vous pouvez l'ajouter à votre fichier gem avec: 

gem strip_attributes

Usage

Ajoutez-le à l'un des modèles (ou à tous les modèles) dont vous souhaitez supprimer les espaces de début et de fin et transformer les chaînes vides en NULL

class DrunkPokerPlayer < ActiveRecord::Base
  strip_attributes
end

Utilisation avancée

Il existe d'autres options que vous pouvez transmettre par modèle pour gérer les exceptions, par exemple si vous souhaitez conserver les espaces blancs de début/fin ou non, etc.

Vous pouvez voir toutes les options du dépôt GitHub ici: 

https://github.com/rmm5t/strip_attributes#examples

0
Joshua Pinter

J'utilise le attribut normaliseur gem pour normaliser les attributs avant de les insérer dans la base de données.

0
daslicious