edge
Mais em rubyonrails.org: Mais Ruby on Rails

Validações do Active Record

Este guia ensina como validar o estado dos objetos antes de serem inseridos no banco de dados usando a funcionalidade de validações do Active Record.

Após ler este guia, você saberá:

1 Visão geral das validações

Aqui está um exemplo de uma validação muito simples:

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false

Como você pode ver, nossa validação nos informa que nossa Person não é válida sem um atributo name. A segunda Person não será persistida no banco de dados.

Antes de nos aprofundarmos em mais detalhes, vamos falar sobre como as validações se encaixam no contexto geral da sua aplicação.

1.1 Por que usar validações?

As validações são usadas para garantir que apenas dados válidos sejam salvos no seu banco de dados. Por exemplo, pode ser importante para a sua aplicação garantir que cada usuário forneça um endereço de e-mail e um endereço de correspondência válidos. As validações em nível de modelo são a melhor maneira de garantir que apenas dados válidos sejam salvos no seu banco de dados. Elas são independentes do banco de dados, não podem ser ignoradas pelos usuários finais e são convenientes para testar e manter. O Rails fornece auxiliares embutidos para necessidades comuns e permite que você crie seus próprios métodos de validação também.

Existem várias outras maneiras de validar dados antes de salvá-los no banco de dados, incluindo restrições nativas do banco de dados, validações do lado do cliente e validações em nível de controlador. Aqui está um resumo dos prós e contras:

  • Restrições e/ou procedimentos armazenados do banco de dados tornam os mecanismos de validação dependentes do banco de dados e podem tornar os testes e a manutenção mais difíceis. No entanto, se o seu banco de dados for usado por outras aplicações, pode ser uma boa ideia usar algumas restrições em nível de banco de dados. Além disso, as validações em nível de banco de dados podem lidar com segurança com algumas coisas (como unicidade em tabelas muito usadas) que podem ser difíceis de implementar de outra forma.
  • As validações do lado do cliente podem ser úteis, mas geralmente são pouco confiáveis se usadas sozinhas. Se forem implementadas usando JavaScript, elas podem ser ignoradas se o JavaScript estiver desativado no navegador do usuário. No entanto, se combinadas com outras técnicas, a validação do lado do cliente pode ser uma maneira conveniente de fornecer aos usuários um feedback imediato ao usar o seu site.
  • As validações em nível de controlador podem ser tentadoras de usar, mas geralmente se tornam difíceis de gerenciar e testar. Sempre que possível, é uma boa ideia manter seus controladores simples, pois isso tornará sua aplicação mais agradável de trabalhar a longo prazo.

Escolha essas opções em casos específicos. É a opinião da equipe do Rails que as validações em nível de modelo são as mais apropriadas na maioria das circunstâncias.

1.2 Quando a validação ocorre?

Existem dois tipos de objetos Active Record: aqueles que correspondem a uma linha dentro do seu banco de dados e aqueles que não correspondem. Quando você cria um novo objeto, por exemplo, usando o método new, esse objeto ainda não pertence ao banco de dados. Uma vez que você chama o método save nesse objeto, ele será salvo na tabela do banco de dados apropriada. O Active Record usa o método de instância new_record? para determinar se um objeto já está no banco de dados ou não. Considere a seguinte classe Active Record:

class Person < ApplicationRecord
end

Podemos ver como isso funciona olhando para a saída do bin/rails console:

irb> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>

irb> p.new_record?
=> true

irb> p.save
=> true

irb> p.new_record?
=> false

Criar e salvar um novo registro enviará uma operação SQL INSERT para o banco de dados. Atualizar um registro existente enviará uma operação SQL UPDATE. As validações são normalmente executadas antes desses comandos serem enviados para o banco de dados. Se alguma validação falhar, o objeto será marcado como inválido e o Active Record não executará a operação INSERT ou UPDATE. Isso evita armazenar um objeto inválido no banco de dados. Você pode escolher executar validações específicas quando um objeto é criado, salvo ou atualizado.

CUIDADO: Existem muitas maneiras de alterar o estado de um objeto no banco de dados. Alguns métodos acionarão as validações, mas outros não. Isso significa que é possível salvar um objeto no banco de dados em um estado inválido se você não tiver cuidado. Os seguintes métodos acionam as validações e salvarão o objeto no banco de dados apenas se o objeto for válido:

  • create
  • create!
  • save
  • save!
  • update
  • update!

As versões com "!" (por exemplo, save!) lançam uma exceção se o registro for inválido. As versões sem "!" não: save e update retornam false, e create retorna o objeto.

1.3 Ignorando Validações

Os seguintes métodos ignoram as validações e salvarão o objeto no banco de dados independentemente de sua validade. Eles devem ser usados com cautela.

  • decrement!
  • decrement_counter
  • increment!
  • increment_counter
  • insert
  • insert!
  • insert_all
  • insert_all!
  • toggle!
  • touch
  • touch_all
  • update_all
  • update_attribute
  • update_column
  • update_columns
  • update_counters
  • upsert
  • upsert_all

Observe que save também tem a capacidade de ignorar as validações se passado validate: false como argumento. Essa técnica deve ser usada com cautela.

  • save(validate: false)

1.4 valid? e invalid?

Antes de salvar um objeto Active Record, o Rails executa suas validações. Se essas validações produzirem erros, o Rails não salvará o objeto.

Você também pode executar essas validações por conta própria. valid? aciona suas validações e retorna true se nenhum erro for encontrado no objeto e false caso contrário. Como você viu acima:

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false

Depois que o Active Record executou as validações, quaisquer falhas podem ser acessadas através do método de instância errors. Esse método retorna uma coleção de erros. Por definição, um objeto é válido se essa coleção estiver vazia após a execução das validações.

Observe que um objeto instanciado com new não reportará erros, mesmo que seja tecnicamente inválido, porque as validações são executadas automaticamente apenas quando o objeto é salvo, como nos métodos create ou save.

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> p = Person.new
=> #<Person id: nil, name: nil>
irb> p.errors.size
=> 0

irb> p.valid?
=> false
irb> p.errors.objects.first.full_message
=> "Name can’t be blank"

irb> p = Person.create
=> #<Person id: nil, name: nil>
irb> p.errors.objects.first.full_message
=> "Name can’t be blank"

irb> p.save
=> false

irb> p.save!
ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

irb> Person.create!
ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

invalid? é o inverso de valid?. Ele aciona suas validações, retornando true se algum erro for encontrado no objeto e false caso contrário.

1.5 errors[]

Para verificar se um atributo específico de um objeto é válido ou não, você pode usar errors[:atributo]. Ele retorna uma matriz com todas as mensagens de erro para :atributo. Se não houver erros no atributo especificado, uma matriz vazia será retornada.

Este método só é útil após as validações terem sido executadas, porque ele apenas inspeciona a coleção de erros e não aciona as validações por si só. É diferente do método ActiveRecord::Base#invalid? explicado acima porque não verifica a validade do objeto como um todo. Ele verifica apenas se há erros encontrados em um atributo individual do objeto.

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.new.errors[:name].any?
=> false
irb> Person.create.errors[:name].any?
=> true

Abordaremos os erros de validação com mais detalhes na seção Trabalhando com Erros de Validação.

2 Assistentes de Validação

O Active Record oferece muitos assistentes de validação pré-definidos que você pode usar diretamente em suas definições de classe. Esses assistentes fornecem regras de validação comuns. Sempre que uma validação falha, um erro é adicionado à coleção errors do objeto e isso é associado ao atributo sendo validado.

Cada assistente aceita um número arbitrário de nomes de atributos, então com uma única linha de código você pode adicionar o mesmo tipo de validação a vários atributos.

Todos eles aceitam as opções :on e :message, que definem quando a validação deve ser executada e qual mensagem deve ser adicionada à coleção errors se ela falhar, respectivamente. A opção :on aceita um dos valores :create ou :update. Há uma mensagem de erro padrão para cada um dos assistentes de validação. Essas mensagens são usadas quando a opção :message não é especificada. Vamos dar uma olhada em cada um dos assistentes disponíveis.

Para ver uma lista dos assistentes padrão disponíveis, dê uma olhada em ActiveModel::Validations::HelperMethods.

2.1 acceptance

Este método valida se uma caixa de seleção na interface do usuário foi marcada quando um formulário foi enviado. Isso é normalmente usado quando o usuário precisa concordar com os termos de serviço do seu aplicativo, confirmar que algum texto foi lido ou qualquer conceito similar.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: true
end

Essa verificação é realizada apenas se terms_of_service não for nil. A mensagem de erro padrão para esse helper é "deve ser aceito". Você também pode passar uma mensagem personalizada através da opção message.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { message: 'deve ser cumprido' }
end

Também é possível receber uma opção :accept, que determina os valores permitidos que serão considerados como aceitáveis. O valor padrão é ['1', true] e pode ser facilmente alterado.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { accept: 'yes' }
  validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }
end

Essa validação é muito específica para aplicações web e essa 'aceitação' não precisa ser registrada em nenhum lugar do seu banco de dados. Se você não tiver um campo para isso, o helper criará um atributo virtual. Se o campo existir no seu banco de dados, a opção accept deve ser definida como ou incluir true, caso contrário, a validação não será executada.

2.2 confirmation

Você deve usar esse helper quando tiver dois campos de texto que devem receber exatamente o mesmo conteúdo. Por exemplo, você pode querer confirmar um endereço de e-mail ou uma senha. Essa validação cria um atributo virtual cujo nome é o nome do campo que deve ser confirmado, com "_confirmation" adicionado.

class Person < ApplicationRecord
  validates :email, confirmation: true
end

Em seu modelo de visualização, você pode usar algo como

<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>

NOTA: Essa verificação é realizada apenas se email_confirmation não for nil. Para exigir a confirmação, certifique-se de adicionar uma verificação de presença para o atributo de confirmação (veremos a opção presence mais adiante neste guia):

class Person < ApplicationRecord
  validates :email, confirmation: true
  validates :email_confirmation, presence: true
end

Também há uma opção :case_sensitive que você pode usar para definir se a restrição de confirmação será sensível a maiúsculas e minúsculas ou não. Essa opção tem o valor padrão true.

class Person < ApplicationRecord
  validates :email, confirmation: { case_sensitive: false }
end

A mensagem de erro padrão para esse helper é "não coincide com a confirmação". Você também pode passar uma mensagem personalizada através da opção message.

Geralmente, ao usar esse validador, você desejará combiná-lo com a opção :if para validar apenas o campo "_confirmation" quando o campo inicial for alterado e não toda vez que você salvar o registro. Mais sobre validações condicionais posteriormente.

class Person < ApplicationRecord
  validates :email, confirmation: true
  validates :email_confirmation, presence: true, if: :email_changed?
end

2.3 comparison

Essa verificação validará uma comparação entre dois valores comparáveis.

class Promotion < ApplicationRecord
  validates :end_date, comparison: { greater_than: :start_date }
end

A mensagem de erro padrão para esse helper é "falha na comparação". Você também pode passar uma mensagem personalizada através da opção message.

Essas opções são todas suportadas:

  • :greater_than - Especifica que o valor deve ser maior que o valor fornecido. A mensagem de erro padrão para essa opção é "deve ser maior que %{count}".
  • :greater_than_or_equal_to - Especifica que o valor deve ser maior ou igual ao valor fornecido. A mensagem de erro padrão para essa opção é "deve ser maior ou igual a %{count}".
  • :equal_to - Especifica que o valor deve ser igual ao valor fornecido. A mensagem de erro padrão para essa opção é "deve ser igual a %{count}".
  • :less_than - Especifica que o valor deve ser menor que o valor fornecido. A mensagem de erro padrão para essa opção é "deve ser menor que %{count}".
  • :less_than_or_equal_to - Especifica que o valor deve ser menor ou igual ao valor fornecido. A mensagem de erro padrão para essa opção é "deve ser menor ou igual a %{count}".
  • :other_than - Especifica que o valor deve ser diferente do valor fornecido. A mensagem de erro padrão para essa opção é "deve ser diferente de %{count}".

NOTA: O validador requer que uma opção de comparação seja fornecida. Cada opção aceita um valor, proc ou símbolo. Qualquer classe que inclua Comparable pode ser comparada.

2.4 formato

Este auxiliar valida os valores dos atributos testando se eles correspondem a uma expressão regular fornecida, que é especificada usando a opção :with.

class Product < ApplicationRecord
  validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
    message: "permite apenas letras" }
end

Inversamente, usando a opção :without, você pode exigir que o atributo especificado não corresponda à expressão regular.

Em ambos os casos, a opção :with ou :without fornecida deve ser uma expressão regular ou um proc ou lambda que retorne uma.

A mensagem de erro padrão é "é inválido".

ATENÇÃO. use \A e \z para corresponder ao início e ao fim da string, ^ e $ correspondem ao início/fim de uma linha. Devido ao uso frequente incorreto de ^ e $, você precisa passar a opção multiline: true caso use algum desses dois âncoras na expressão regular fornecida. Na maioria dos casos, você deve usar \A e \z.

2.5 inclusão

Este auxiliar valida se os valores dos atributos estão incluídos em um conjunto dado. Na verdade, esse conjunto pode ser qualquer objeto enumerável.

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} não é um tamanho válido" }
end

O auxiliar inclusão tem uma opção :in que recebe o conjunto de valores que serão aceitos. A opção :in tem um alias chamado :within que você pode usar para o mesmo propósito, se desejar. O exemplo anterior usa a opção :message para mostrar como você pode incluir o valor do atributo. Para todas as opções, consulte a documentação da mensagem.

A mensagem de erro padrão para este auxiliar é "não está incluído na lista".

2.6 exclusão

O oposto de inclusão é... exclusão!

Este auxiliar valida se os valores dos atributos não estão incluídos em um conjunto dado. Na verdade, esse conjunto pode ser qualquer objeto enumerável.

class Account < ApplicationRecord
  validates :subdomain, exclusion: { in: %w(www us ca jp),
    message: "%{value} está reservado." }
end

O auxiliar exclusão tem uma opção :in que recebe o conjunto de valores que não serão aceitos para os atributos validados. A opção :in tem um alias chamado :within que você pode usar para o mesmo propósito, se desejar. Este exemplo usa a opção :message para mostrar como você pode incluir o valor do atributo. Para todas as opções do argumento de mensagem, consulte a documentação da mensagem.

A mensagem de erro padrão é "está reservado".

Alternativamente a um objeto enumerável tradicional (como um Array), você pode fornecer um proc, lambda ou símbolo que retorna um objeto enumerável. Se o objeto enumerável for um intervalo numérico, de tempo ou de data e hora, o teste é realizado com Range#cover?, caso contrário, com include?. Ao usar um proc ou lambda, a instância em validação é passada como argumento.

2.7 comprimento

Este auxiliar valida o comprimento dos valores dos atributos. Ele fornece uma variedade de opções, para que você possa especificar restrições de comprimento de diferentes maneiras:

class Person < ApplicationRecord
  validates :name, length: { minimum: 2 }
  validates :bio, length: { maximum: 500 }
  validates :password, length: { in: 6..20 }
  validates :registration_number, length: { is: 6 }
end

As possíveis opções de restrição de comprimento são:

  • :minimum - O atributo não pode ter menos que o comprimento especificado.
  • :maximum - O atributo não pode ter mais que o comprimento especificado.
  • :in (ou :within) - O comprimento do atributo deve estar incluído em um determinado intervalo. O valor para esta opção deve ser um intervalo.
  • :is - O comprimento do atributo deve ser igual ao valor fornecido.

As mensagens de erro padrão dependem do tipo de validação de comprimento sendo realizada. Você pode personalizar essas mensagens usando as opções :wrong_length, :too_long e :too_short e %{count} como um espaço reservado para o número correspondente à restrição de comprimento sendo usada. Ainda é possível usar a opção :message para especificar uma mensagem de erro.

class Person < ApplicationRecord
  validates :bio, length: { maximum: 1000,
    too_long: "%{count} caracteres é o máximo permitido" }
end

Observe que as mensagens de erro padrão são no plural (por exemplo, "é muito curto (o mínimo é de %{count} caracteres)"). Por esse motivo, quando :minimum é 1, você deve fornecer uma mensagem personalizada ou usar presence: true em vez disso. Quando :in ou :within têm um limite inferior de 1, você deve fornecer uma mensagem personalizada ou chamar presence antes de length. NOTA: Apenas uma opção de restrição pode ser usada de cada vez, exceto as opções :minimum e :maximum, que podem ser combinadas juntas.

2.8 numericality

Este auxiliar valida se seus atributos possuem apenas valores numéricos. Por padrão, ele irá corresponder a um sinal opcional seguido de um número inteiro ou de ponto flutuante.

Para especificar que apenas números inteiros são permitidos, defina :only_integer como true. Em seguida, ele usará a seguinte expressão regular para validar o valor do atributo.

/\A[+-]?\d+\z/

Caso contrário, ele tentará converter o valor em um número usando Float. Floats são convertidos em BigDecimal usando o valor de precisão da coluna ou um máximo de 15 dígitos.

class Player < ApplicationRecord
  validates :points, numericality: true
  validates :games_played, numericality: { only_integer: true }
end

A mensagem de erro padrão para :only_integer é "deve ser um número inteiro".

Além de :only_integer, este auxiliar também aceita a opção :only_numeric, que especifica que o valor deve ser uma instância de Numeric e tenta analisar o valor se for uma String.

NOTA: Por padrão, numericality não permite valores nil. Você pode usar a opção allow_nil: true para permitir. Observe que para colunas Integer e Float, strings vazias são convertidas em nil.

A mensagem de erro padrão quando nenhuma opção é especificada é "não é um número".

Também existem muitas opções que podem ser usadas para adicionar restrições aos valores aceitáveis:

  • :greater_than - Especifica que o valor deve ser maior que o valor fornecido. A mensagem de erro padrão para esta opção é "deve ser maior que %{count}".
  • :greater_than_or_equal_to - Especifica que o valor deve ser maior ou igual ao valor fornecido. A mensagem de erro padrão para esta opção é "deve ser maior ou igual a %{count}".
  • :equal_to - Especifica que o valor deve ser igual ao valor fornecido. A mensagem de erro padrão para esta opção é "deve ser igual a %{count}".
  • :less_than - Especifica que o valor deve ser menor que o valor fornecido. A mensagem de erro padrão para esta opção é "deve ser menor que %{count}".
  • :less_than_or_equal_to - Especifica que o valor deve ser menor ou igual ao valor fornecido. A mensagem de erro padrão para esta opção é "deve ser menor ou igual a %{count}".
  • :other_than - Especifica que o valor deve ser diferente do valor fornecido. A mensagem de erro padrão para esta opção é "deve ser diferente de %{count}".
  • :in - Especifica que o valor deve estar dentro do intervalo fornecido. A mensagem de erro padrão para esta opção é "deve estar em %{count}".
  • :odd - Especifica que o valor deve ser um número ímpar. A mensagem de erro padrão para esta opção é "deve ser ímpar".
  • :even - Especifica que o valor deve ser um número par. A mensagem de erro padrão para esta opção é "deve ser par".

2.9 presence

Este auxiliar valida se os atributos especificados não estão vazios. Ele usa o método Object#blank? para verificar se o valor é nil ou uma string vazia, ou seja, uma string que está vazia ou consiste apenas de espaços em branco.

class Person < ApplicationRecord
  validates :name, :login, :email, presence: true
end

Se você deseja garantir que uma associação esteja presente, você precisará testar se o próprio objeto associado está presente e não a chave estrangeira usada para mapear a associação. Dessa forma, não é verificado apenas se a chave estrangeira não está vazia, mas também se o objeto referenciado existe.

class Supplier < ApplicationRecord
  has_one :account
  validates :account, presence: true
end

Para validar registros associados cuja presença é obrigatória, você deve especificar a opção :inverse_of para a associação:

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

NOTA: Se você deseja garantir que a associação esteja presente e seja válida, você também precisa usar validates_associated. Mais informações abaixo

Se você valida a presença de um objeto associado por meio de um relacionamento has_one ou has_many, ele verificará se o objeto não está em branco (blank?) e não está marcado para destruição (marked_for_destruction?).

Como false.blank? é verdadeiro, se você deseja validar a presença de um campo booleano, você deve usar uma das seguintes validações:

# O valor _deve_ ser true ou false
validates :nome_do_campo_booleano, inclusion: [true, false]
# O valor _não deve_ ser nil, ou seja, true ou false
validates :nome_do_campo_booleano, exclusion: [nil]

Ao usar uma dessas validações, você garantirá que o valor NÃO será nil, o que resultaria em um valor NULL na maioria dos casos.

A mensagem de erro padrão é "não pode ficar em branco".

2.10 absence

Este auxiliar valida se os atributos especificados estão ausentes. Ele usa o método Object#present? para verificar se o valor não é nil ou uma string em branco, ou seja, uma string que está vazia ou consiste apenas de espaços em branco.

class Person < ApplicationRecord
  validates :name, :login, :email, absence: true
end

Se você quiser ter certeza de que uma associação está ausente, você precisará testar se o próprio objeto associado está ausente e não a chave estrangeira usada para mapear a associação.

class LineItem < ApplicationRecord
  belongs_to :order
  validates :order, absence: true
end

Para validar registros associados cuja ausência é necessária, você deve especificar a opção :inverse_of para a associação:

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

NOTA: Se você deseja garantir que a associação esteja presente e válida, você também precisa usar validates_associated. Mais informações sobre isso abaixo.

Se você validar a ausência de um objeto associado por meio de um relacionamento has_one ou has_many, ele verificará se o objeto não está present? nem marked_for_destruction?.

Como false.present? é falso, se você deseja validar a ausência de um campo booleano, deve usar validates :nome_do_campo, exclusion: { in: [true, false] }.

A mensagem de erro padrão é "deve ficar em branco".

2.11 uniqueness

Este auxiliar valida se o valor do atributo é único antes de o objeto ser salvo.

class Account < ApplicationRecord
  validates :email, uniqueness: true
end

A validação é feita executando uma consulta SQL na tabela do modelo, procurando por um registro existente com o mesmo valor nesse atributo.

Existe uma opção :scope que você pode usar para especificar um ou mais atributos que são usados para limitar a verificação de unicidade:

class Holiday < ApplicationRecord
  validates :name, uniqueness: { scope: :year,
    message: "deve ocorrer uma vez por ano" }
end

CUIDADO. Essa validação não cria uma restrição de unicidade no banco de dados, então pode acontecer que duas conexões de banco de dados diferentes criem dois registros com o mesmo valor para uma coluna que você pretende que seja única. Para evitar isso, você deve criar um índice único nessa coluna em seu banco de dados.

Para adicionar uma restrição de unicidade no banco de dados, use a instrução add_index em uma migração e inclua a opção unique: true.

Se você deseja criar uma restrição de banco de dados para evitar possíveis violações de uma validação de unicidade usando a opção :scope, você deve criar um índice único em ambas as colunas em seu banco de dados. Consulte o manual do MySQL para obter mais detalhes sobre índices de várias colunas ou o manual do PostgreSQL para exemplos de restrições únicas que se referem a um grupo de colunas.

Também existe uma opção :case_sensitive que você pode usar para definir se a restrição de unicidade será sensível a maiúsculas e minúsculas, insensível a maiúsculas e minúsculas ou respeitar a colação padrão do banco de dados. Essa opção tem como padrão respeitar a colação padrão do banco de dados.

class Person < ApplicationRecord
  validates :name, uniqueness: { case_sensitive: false }
end

CUIDADO. Observe que alguns bancos de dados são configurados para realizar pesquisas insensíveis a maiúsculas e minúsculas de qualquer maneira.

Existe uma opção :conditions que você pode usar para especificar condições adicionais como um fragmento SQL WHERE para limitar a busca da restrição de unicidade (por exemplo, conditions: -> { where(status: 'active') }).

A mensagem de erro padrão é "já está em uso".

Consulte validates_uniqueness_of para obter mais informações.

2.12 validates_associated

Você deve usar este auxiliar quando seu modelo tem associações que sempre precisam ser validadas. Sempre que você tentar salvar seu objeto, valid? será chamado em cada um dos objetos associados.

class Library < ApplicationRecord
  has_many :books
  validates_associated :books
end

Essa validação funcionará com todos os tipos de associação.

CUIDADO: Não use validates_associated em ambos os lados de suas associações. Eles chamariam um ao outro em um loop infinito.

A mensagem de erro padrão para validates_associated é "é inválido". Observe que cada objeto associado conterá sua própria coleção de errors; os erros não são propagados para o modelo chamador.

NOTA: validates_associated só pode ser usado com objetos ActiveRecord, tudo até agora também pode ser usado em qualquer objeto que inclua ActiveModel::Validations.

2.13 validates_each

Este auxiliar valida atributos contra um bloco. Ele não possui uma função de validação predefinida. Você deve criar uma usando um bloco, e cada atributo passado para validates_each será testado contra ele.

No exemplo a seguir, rejeitaremos nomes e sobrenomes que começam com letra minúscula.

class Person < ApplicationRecord
  validates_each :name, :surname do |record, attr, value|
    record.errors.add(attr, 'deve começar com letra maiúscula') if /\A[[:lower:]]/.match?(value)
  end
end

O bloco recebe o registro, o nome do atributo e o valor do atributo.

Você pode fazer qualquer coisa para verificar dados válidos dentro do bloco. Se a validação falhar, você deve adicionar um erro ao modelo, tornando-o inválido.

2.14 validates_with

Este auxiliar passa o registro para uma classe separada para validação.

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if record.first_name == "Evil"
      record.errors.add :base, "Esta pessoa é má"
    end
  end
end

class Person < ApplicationRecord
  validates_with GoodnessValidator
end

Não há mensagem de erro padrão para validates_with. Você deve adicionar manualmente erros à coleção de erros do registro na classe do validador.

NOTA: Erros adicionados a record.errors[:base] se referem ao estado do registro como um todo.

Para implementar o método de validação, você deve aceitar um parâmetro record na definição do método, que é o registro a ser validado.

Se você deseja adicionar um erro em um atributo específico, passe-o como o primeiro argumento, como record.errors.add(:first_name, "escolha outro nome"). Abordaremos [erros de validação][] com mais detalhes posteriormente.

def validate(record)
  if record.some_field != "aceitável"
    record.errors.add :some_field, "este campo é inaceitável"
  end
end

O auxiliar validates_with aceita uma classe ou uma lista de classes para usar na validação.

class Person < ApplicationRecord
  validates_with MyValidator, MyOtherValidator, on: :create
end

Como todas as outras validações, validates_with aceita as opções :if, :unless e :on. Se você passar outras opções, elas serão enviadas para a classe do validador como options:

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if options[:fields].any? { |field| record.send(field) == "Evil" }
      record.errors.add :base, "Esta pessoa é má"
    end
  end
end

class Person < ApplicationRecord
  validates_with GoodnessValidator, fields: [:first_name, :last_name]
end

Observe que o validador será inicializado apenas uma vez para todo o ciclo de vida da aplicação, e não em cada execução de validação, portanto, tenha cuidado ao usar variáveis de instância dentro dele.

Se o seu validador for complexo o suficiente para que você queira variáveis de instância, você pode usar facilmente um objeto Ruby simples em vez disso:

class Person < ApplicationRecord
  validate do |person|
    GoodnessValidator.new(person).validate
  end
end

class GoodnessValidator
  def initialize(person)
    @person = person
  end

  def validate
    if some_complex_condition_involving_ivars_and_private_methods?
      @person.errors.add :base, "Esta pessoa é má"
    end
  end

  # ...
end

Abordaremos validações personalizadas mais adiante.

erros de validação

3 Opções Comuns de Validação

Existem várias opções comuns suportadas pelos validadores que acabamos de ver, vamos ver algumas delas agora!

NOTA: Nem todas essas opções são suportadas por todos os validadores, consulte a documentação da API para ActiveModel::Validations.

Ao usar qualquer um dos métodos de validação que mencionamos, também há uma lista de opções comuns compartilhadas junto com os validadores. Vamos cobri-las agora!

  • :allow_nil: Ignora a validação se o atributo for nil.
  • :allow_blank: Ignora a validação se o atributo estiver em branco.
  • :message: Especifica uma mensagem de erro personalizada.
  • :on: Especifica os contextos nos quais essa validação está ativa.
  • :strict: Gera uma exceção quando a validação falha.
  • :if e :unless: Especifica quando a validação deve ou não ocorrer.

3.1 :allow_nil

A opção :allow_nil ignora a validação quando o valor sendo validado é nil.

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} não é um tamanho válido" }, allow_nil: true
end
irb> Coffee.create(size: nil).valid?
=> true
irb> Coffee.create(size: "mega").valid?
=> false

Para opções completas para o argumento de mensagem, consulte a documentação de mensagem.

3.2 :allow_blank

A opção :allow_blank é semelhante à opção :allow_nil. Essa opção permite que a validação seja aprovada se o valor do atributo estiver em branco, como nil ou uma string vazia, por exemplo.

class Topic < ApplicationRecord
  validates :title, length: { is: 5 }, allow_blank: true
end
irb> Topic.create(title: "").valid?
=> true
irb> Topic.create(title: nil).valid?
=> true

3.3 :message

Como você já viu, a opção :message permite especificar a mensagem que será adicionada à coleção de errors quando a validação falhar. Quando essa opção não é usada, o Active Record usará a mensagem de erro padrão correspondente para cada helper de validação.

A opção :message aceita tanto uma String quanto um Proc como valor.

Um valor String :message pode opcionalmente conter qualquer/ todos %{value}, %{attribute} e %{model}, que serão substituídos dinamicamente quando a validação falhar. Essa substituição é feita usando a gem i18n, e os espaços reservados devem corresponder exatamente, sem espaços são permitidos.

class Person < ApplicationRecord
  # Mensagem codificada
  validates :name, presence: { message: "deve ser fornecido, por favor" }

  # Mensagem com valor de atributo dinâmico. %{value} será substituído
  # pelo valor real do atributo. %{attribute} e %{model}
  # também estão disponíveis.
  validates :age, numericality: { message: "%{value} parece errado" }
end

Um valor Proc :message é fornecido com dois argumentos: o objeto sendo validado e um hash com pares de chave-valor :model, :attribute e :value.

class Person < ApplicationRecord
  validates :username,
    uniqueness: {
      # objeto = objeto de pessoa sendo validado
      # dados = { model: "Person", attribute: "Username", value: <username> }
      message: ->(object, data) do
        "Ei #{object.name}, #{data[:value]} já está em uso."
      end
    }
end

3.4 :on

A opção :on permite especificar quando a validação deve ocorrer. O comportamento padrão para todos os helpers de validação incorporados é ser executado ao salvar (tanto ao criar um novo registro quanto ao atualizá-lo). Se você deseja alterá-lo, pode usar on: :create para executar a validação apenas quando um novo registro é criado ou on: :update para executar a validação apenas quando um registro é atualizado.

class Person < ApplicationRecord
  # será possível atualizar o email com um valor duplicado
  validates :email, uniqueness: true, on: :create

  # será possível criar o registro com uma idade não numérica
  validates :age, numericality: true, on: :update

  # o padrão (valida tanto na criação quanto na atualização)
  validates :name, presence: true
end

Você também pode usar on: para definir contextos personalizados. Contextos personalizados precisam ser acionados explicitamente passando o nome do contexto para valid?, invalid? ou save.

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
end
irb> person = Person.new(age: 'trinta e três')
irb> person.valid?
=> true
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["já está em uso"], :age=>["não é um número"]}

person.valid?(:account_setup) executa ambas as validações sem salvar o modelo. person.save(context: :account_setup) valida person no contexto account_setup antes de salvar.

Passar um array de símbolos também é aceitável.

class Book
  include ActiveModel::Validations

  validates :title, presence: true, on: [:update, :ensure_title]
end
irb> book = Book.new(title: nil)
irb> book.valid?
=> true
irb> book.valid?(:ensure_title)
=> false
irb> book.errors.messages
=> {:title=>["não pode ficar em branco"]}

Quando acionadas por um contexto explícito, as validações são executadas para esse contexto, bem como quaisquer validações sem um contexto.

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
  validates :name, presence: true
end
irb> person = Person.new
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["já está em uso"], :age=>["não é um número"], :name=>["não pode ficar em branco"]}

Abordaremos mais casos de uso para on: no guia de callbacks.

4 Validações Estritas

Você também pode especificar validações para serem estritas e gerar ActiveModel::StrictValidationFailed quando o objeto for inválido.

class Person < ApplicationRecord
  validates :name, presence: { strict: true }
end
irb> Person.new.valid?
ActiveModel::StrictValidationFailed: O nome não pode ficar em branco

Também é possível passar uma exceção personalizada para a opção :strict.

class Person < ApplicationRecord
  validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
irb> Person.new.valid?
TokenGenerationException: O token não pode ficar em branco

5 Validação Condicional

Às vezes, fará sentido validar um objeto apenas quando um predicado dado for satisfeito. Você pode fazer isso usando as opções :if e :unless, que podem receber um símbolo, um Proc ou um Array. Você pode usar a opção :if quando você deseja especificar quando a validação deve ocorrer. Alternativamente, se você deseja especificar quando a validação não deve ocorrer, então você pode usar a opção :unless.

5.1 Usando um símbolo com :if e :unless

Você pode associar as opções :if e :unless com um símbolo correspondente ao nome de um método que será chamado antes da validação ocorrer. Essa é a opção mais comumente usada.

class Order < ApplicationRecord
  validates :card_number, presence: true, if: :paid_with_card?

  def paid_with_card?
    payment_type == "card"
  end
end

5.2 Usando um Proc com :if e :unless

É possível associar :if e :unless com um objeto Proc que será chamado. Usar um objeto Proc permite escrever uma condição inline em vez de um método separado. Essa opção é mais adequada para uma única linha.

class Account < ApplicationRecord
  validates :password, confirmation: true,
    unless: Proc.new { |a| a.password.blank? }
end

Como lambda é um tipo de Proc, também é possível usar para escrever condições inline aproveitando a sintaxe reduzida.

validates :password, confirmation: true, unless: -> { password.blank? }

5.3 Agrupando Validações Condicionalmente

Às vezes, é útil ter várias validações usando uma única condição. Isso pode ser facilmente alcançado usando with_options.

class User < ApplicationRecord
  with_options if: :is_admin? do |admin|
    admin.validates :password, length: { minimum: 10 }
    admin.validates :email, presence: true
  end
end

Todas as validações dentro do bloco with_options passarão automaticamente pela condição if: :is_admin?

5.4 Combinando Condições de Validação

Por outro lado, quando várias condições definem se uma validação deve ocorrer ou não, um Array pode ser usado. Além disso, você pode aplicar tanto :if quanto :unless à mesma validação.

class Computer < ApplicationRecord
  validates :mouse, presence: true,
                    if: [Proc.new { |c| c.market.retail? }, :desktop?],
                    unless: Proc.new { |c| c.trackpad.present? }
end

A validação só é executada quando todas as condições :if e nenhuma das condições :unless são avaliadas como true.

6 Realizando Validações Personalizadas

Quando os ajudantes de validação incorporados não são suficientes para suas necessidades, você pode escrever seus próprios validadores ou métodos de validação como preferir.

6.1 Validadores Personalizados

Validadores personalizados são classes que herdam de ActiveModel::Validator. Essas classes devem implementar o método validate, que recebe um registro como argumento e realiza a validação nele. O validador personalizado é chamado usando o método validates_with.

class MyValidator < ActiveModel::Validator
  def validate(record)
    unless record.name.start_with? 'X'
      record.errors.add :name, "Forneça um nome começando com X, por favor!"
    end
  end
end

class Person < ApplicationRecord
  validates_with MyValidator
end

A maneira mais fácil de adicionar validadores personalizados para validar atributos individuais é com o conveniente ActiveModel::EachValidator. Nesse caso, a classe do validador personalizado deve implementar um método validate_each que recebe três argumentos: registro, atributo e valor. Eles correspondem à instância, ao atributo a ser validado e ao valor do atributo na instância passada.

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless URI::MailTo::EMAIL_REGEXP.match?(value)
      record.errors.add attribute, (options[:message] || "não é um email")
    end
  end
end

class Person < ApplicationRecord
  validates :email, presence: true, email: true
end

Como mostrado no exemplo, você também pode combinar validações padrão com seus próprios validadores personalizados.

6.2 Métodos Personalizados

Você também pode criar métodos que verificam o estado dos seus modelos e adicionam erros à coleção errors quando eles são inválidos. Em seguida, você deve registrar esses métodos usando o método de classe validate, passando os símbolos para os nomes dos métodos de validação.

Você pode passar mais de um símbolo para cada método de classe e as respectivas validações serão executadas na mesma ordem em que foram registradas.

O método valid? verificará se a coleção errors está vazia, portanto, seus métodos de validação personalizados devem adicionar erros a ela quando você deseja que a validação falhe:

class Invoice < ApplicationRecord
  validate :expiration_date_cannot_be_in_the_past,
    :discount_cannot_be_greater_than_total_value

  def expiration_date_cannot_be_in_the_past
    if expiration_date.present? && expiration_date < Date.today
      errors.add(:expiration_date, "não pode estar no passado")
    end
  end

  def discount_cannot_be_greater_than_total_value
    if discount > total_value
      errors.add(:discount, "não pode ser maior que o valor total")
    end
  end
end

Por padrão, essas validações serão executadas sempre que você chamar valid? ou salvar o objeto. Mas também é possível controlar quando executar essas validações personalizadas, fornecendo uma opção :on para o método validate, com :create ou :update.

class Invoice < ApplicationRecord
  validate :active_customer, on: :create

  def active_customer
    errors.add(:customer_id, "não está ativo") unless customer.active?
  end
end

Consulte a seção acima para obter mais detalhes sobre :on.

6.3 Listando Validadores

Se você deseja descobrir todos os validadores para um determinado objeto, não procure mais do que validators.

Por exemplo, se tivermos o seguinte modelo usando um validador personalizado e um validador embutido:

class Person < ApplicationRecord
  validates :name, presence: true, on: :create
  validates :email, format: URI::MailTo::EMAIL_REGEXP
  validates_with MyOtherValidator, strict: true
end

Agora podemos usar validators no modelo "Person" para listar todos os validadores, ou até mesmo verificar um campo específico usando validators_on.

irb> Person.validators
#=> [#<ActiveRecord::Validations::PresenceValidator:0x10b2f2158
      @attributes=[:name], @options={:on=>:create}>,
     #<MyOtherValidatorValidator:0x10b2f17d0
      @attributes=[:name], @options={:strict=>true}>,
     #<ActiveModel::Validations::FormatValidator:0x10b2f0f10
      @attributes=[:email],
      @options={:with=>URI::MailTo::EMAIL_REGEXP}>]
     #<MyOtherValidator:0x10b2f0948 @options={:strict=>true}>]

irb> Person.validators_on(:name)
#=> [#<ActiveModel::Validations::PresenceValidator:0x10b2f2158
      @attributes=[:name], @options={on: :create}>]

7 Trabalhando com Erros de Validação

Os métodos valid? e invalid? fornecem apenas um status resumido sobre a validade. No entanto, você pode aprofundar cada erro individual usando vários métodos da coleção errors.

A seguir está uma lista dos métodos mais comumente usados. Consulte a documentação ActiveModel::Errors para obter uma lista de todos os métodos disponíveis.

7.1 errors

O gateway através do qual você pode aprofundar vários detalhes de cada erro.

Isso retorna uma instância da classe ActiveModel::Errors contendo todos os erros, cada erro é representado por um objeto ActiveModel::Error.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.full_messages
=> ["Name não pode ficar em branco", "Name é muito curto (mínimo: 3 caracteres)"]

irb> person = Person.new(name: "John Doe")
irb> person.valid?
=> true
irb> person.errors.full_messages
=> []

irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.first.details
=> {:error=>:too_short, :count=>3}

7.2 errors[]

errors[] é usado quando você deseja verificar as mensagens de erro para um atributo específico. Ele retorna uma matriz de strings com todas as mensagens de erro para o atributo fornecido, cada string com uma mensagem de erro. Se não houver erros relacionados ao atributo, ele retorna uma matriz vazia.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new(name: "John Doe")
irb> person.valid?
=> true
irb> person.errors[:name]
=> []

irb> person = Person.new(name: "JD")
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["é muito curto (mínimo: 3 caracteres)"]

irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["não pode ficar em branco", "é muito curto (mínimo: 3 caracteres)"]

7.3 errors.where e Objeto de Erro

Às vezes, podemos precisar de mais informações sobre cada erro além de sua mensagem. Cada erro é encapsulado como um objeto ActiveModel::Error, e o método where é a maneira mais comum de acessá-lo.

where retorna uma matriz de objetos de erro filtrados por vários graus de condições.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end

Podemos filtrar apenas o atributo passando-o como o primeiro parâmetro para errors.where(:attr). O segundo parâmetro é usado para filtrar o tipo de erro que queremos chamando errors.where(:attr, :type).

irb> person = Person.new
irb> person.valid?
=> false

irb> person.errors.where(:name)
=> [ ... ] # todos os erros para o atributo :name

irb> person.errors.where(:name, :too_short)
=> [ ... ] # erros :too_short para o atributo :name

Por último, podemos filtrar por quaisquer opções que possam existir no tipo de objeto de erro fornecido.

irb> person = Person.new
irb> person.valid?
=> false

irb> person.errors.where(:name, :too_short, minimum: 3)
=> [ ... ] # todos os erros de nome sendo muito curtos e o mínimo é 2

Você pode ler várias informações desses objetos de erro:

irb> error = person.errors.where(:name).last

irb> error.attribute
=> :name
irb> error.type
=> :too_short
irb> error.options[:count]
=> 3

Você também pode gerar a mensagem de erro:

irb> error.message
=> "é muito curto (mínimo: 3 caracteres)"
irb> error.full_message
=> "Name é muito curto (mínimo: 3 caracteres)"

O método full_message gera uma mensagem mais amigável ao usuário, com o nome do atributo em maiúscula anteposto. (Para personalizar o formato que full_message usa, consulte o guia I18n.)

7.4 errors.add

O método add cria o objeto de erro ao receber o atributo, o tipo de erro e um hash de opções adicionais. Isso é útil ao escrever seu próprio validador, pois permite definir situações de erro muito específicas.

class Person < ApplicationRecord
  validate do |person|
    errors.add :name, :too_plain, message: "não é legal o suficiente"
  end
end
irb> person = Person.create
irb> person.errors.where(:name).first.type
=> :too_plain
irb> person.errors.where(:name).first.full_message
=> "O nome não é legal o suficiente"

7.5 errors[:base]

Você pode adicionar erros que estão relacionados ao estado do objeto como um todo, em vez de estar relacionado a um atributo específico. Para fazer isso, você deve usar :base como o atributo ao adicionar um novo erro.

class Person < ApplicationRecord
  validate do |person|
    errors.add :base, :invalid, message: "Essa pessoa é inválida porque ..."
  end
end
irb> person = Person.create
irb> person.errors.where(:base).first.full_message
=> "Essa pessoa é inválida porque ..."

7.6 errors.size

O método size retorna o número total de erros para o objeto.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.size
=> 2

irb> person = Person.new(name: "Andrea", email: "[email protected]")
irb> person.valid?
=> true
irb> person.errors.size
=> 0

7.7 errors.clear

O método clear é usado quando você deseja limpar intencionalmente a coleção de errors. Claro, chamar errors.clear em um objeto inválido não o tornará válido: a coleção de errors agora estará vazia, mas na próxima vez que você chamar valid? ou qualquer método que tente salvar esse objeto no banco de dados, as validações serão executadas novamente. Se alguma das validações falhar, a coleção de errors será preenchida novamente.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.empty?
=> false

irb> person.errors.clear
irb> person.errors.empty?
=> true

irb> person.save
=> false

irb> person.errors.empty?
=> false

8 Exibindo Erros de Validação nas Views

Depois de criar um modelo e adicionar validações, se esse modelo for criado por meio de um formulário da web, provavelmente você deseja exibir uma mensagem de erro quando uma das validações falhar.

Como cada aplicativo lida com esse tipo de coisa de maneira diferente, o Rails não inclui nenhum helper de visualização para ajudá-lo a gerar essas mensagens diretamente. No entanto, devido ao grande número de métodos que o Rails oferece para interagir com validações em geral, você pode construir o seu próprio. Além disso, ao gerar um scaffold, o Rails colocará algum ERB no _form.html.erb que ele gera que exibe a lista completa de erros nesse modelo.

Supondo que temos um modelo que foi salvo em uma variável de instância chamada @article, ele se parece com isso:

<% if @article.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@article.errors.count, "error") %> impediram que este artigo fosse salvo:</h2>

    <ul>
      <% @article.errors.each do |error| %>
        <li><%= error.full_message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

Além disso, se você usar os helpers de formulário do Rails para gerar seus formulários, quando ocorre um erro de validação em um campo, ele gerará um <div> extra em torno da entrada.

<div class="field_with_errors">
  <input id="article_title" name="article[title]" size="30" type="text" value="">
</div>

Você pode estilizar esse div da maneira que desejar. O scaffold padrão que o Rails gera, por exemplo, adiciona essa regra CSS:

.field_with_errors {
  padding: 2px;
  background-color: red;
  display: table;
}

Isso significa que qualquer campo com um erro terá uma borda vermelha de 2 pixels.

Feedback

Você é incentivado a ajudar a melhorar a qualidade deste guia.

Por favor, contribua se encontrar algum erro de digitação ou factual. Para começar, você pode ler nossa contribuição à documentação seção.

Você também pode encontrar conteúdo incompleto ou desatualizado. Por favor, adicione qualquer documentação ausente para o principal. Certifique-se de verificar Guias Edge primeiro para verificar se os problemas já foram corrigidos ou não no branch principal. Verifique as Diretrizes dos Guias do Ruby on Rails para estilo e convenções.

Se por algum motivo você encontrar algo para corrigir, mas não puder corrigi-lo você mesmo, por favor abra uma issue.

E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem-vinda no Fórum oficial do Ruby on Rails.