Live
Black Hat USAAI BusinessBlack Hat AsiaAI BusinessAmazon, Apple, and Nvidia can't make AI chips without this company. Here's why its growth stock could soar. - MSNGNews AI NVIDIAI am building a Notebook Environment for SQL Inside a Database ClientDEV CommunityA Production Readiness Checklist for Remote MCP ServersDEV CommunityNginx + PHP + MySQL Optimisations and Parameter CalculationsDEV CommunityDo You Actually Need an AI Gateway? (And When a Simple LLM Wrapper Isn’t Enough)DEV CommunityPowerShell Scripts Every MSP Should UseDEV CommunityThe way I see it — The development of autonomous vehicles is fraught with ethical concerns. And: The notion that the separatiDev.to AIFull-Stack E-Commerce App - Part 1: Project setupDEV CommunityThe Architect’s Reflection: The 5D MiddlewareMedium AII Am a Software Engineer Teaching Myself AI Engineering. Here Is Where I Am Starting.Medium AIShow HN: AI tool to merge people from two photos into one realistic group photoHacker News AI Top20 Meta-Prompts That Boost AI Response Quality by 300%Dev.to AIBlack Hat USAAI BusinessBlack Hat AsiaAI BusinessAmazon, Apple, and Nvidia can't make AI chips without this company. Here's why its growth stock could soar. - MSNGNews AI NVIDIAI am building a Notebook Environment for SQL Inside a Database ClientDEV CommunityA Production Readiness Checklist for Remote MCP ServersDEV CommunityNginx + PHP + MySQL Optimisations and Parameter CalculationsDEV CommunityDo You Actually Need an AI Gateway? (And When a Simple LLM Wrapper Isn’t Enough)DEV CommunityPowerShell Scripts Every MSP Should UseDEV CommunityThe way I see it — The development of autonomous vehicles is fraught with ethical concerns. And: The notion that the separatiDev.to AIFull-Stack E-Commerce App - Part 1: Project setupDEV CommunityThe Architect’s Reflection: The 5D MiddlewareMedium AII Am a Software Engineer Teaching Myself AI Engineering. Here Is Where I Am Starting.Medium AIShow HN: AI tool to merge people from two photos into one realistic group photoHacker News AI Top20 Meta-Prompts That Boost AI Response Quality by 300%Dev.to AI
AI NEWS HUBbyEIGENVECTOREigenvector

Forms & Validations in Rails

DEV Communityby AgentQApril 1, 20268 min read0 views
Source Quiz

<p>Forms are where users hand your app messy, incomplete, or malicious input. Validations are how your app refuses bad data before it reaches the database. If you’re building AI features, this matters even more. Prompts, uploaded text, settings, and API-driven forms all need guardrails.</p> <p>In this post, we’ll build a simple document form in Rails and validate it properly.</p> <h2> Generate a resource </h2> <p>If you’ve been following along, you already have a <code>Document</code> model. Let’s assume it looks like this:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight ruby"><code><span class="k">class</span> <span class="nc">Document</span> <span class="o"><</span> <span class="no">ApplicationRecord</span> <span class="n">belongs_to</span> <span class="ss">:proj

Forms are where users hand your app messy, incomplete, or malicious input. Validations are how your app refuses bad data before it reaches the database. If you’re building AI features, this matters even more. Prompts, uploaded text, settings, and API-driven forms all need guardrails.

In this post, we’ll build a simple document form in Rails and validate it properly.

Generate a resource

If you’ve been following along, you already have a Document model. Let’s assume it looks like this:

class Document < ApplicationRecord  belongs_to :project end

Enter fullscreen mode

Exit fullscreen mode

And the table has these columns:

  • project_id

  • title

  • body

  • status

Now create a controller if you don’t already have one:

bin/rails generate controller Documents new create edit update

Enter fullscreen mode

Exit fullscreen mode

Add routes in config/routes.rb:

Rails.application.routes.draw do  resources :projects do  resources :documents, only: [:new, :create, :edit, :update]  end end

Enter fullscreen mode

Exit fullscreen mode

Nested routes make sense here because a document belongs to a project.

Add model validations

Start with the model. Put the rules close to the data.

app/models/document.rb

class Document < ApplicationRecord  belongs_to :project

validates :title, presence: true, length: { maximum: 120 } validates :body, presence: true, length: { minimum: 20 } validates :status, presence: true, inclusion: { in: %w[draft published archived] } end`

Enter fullscreen mode

Exit fullscreen mode

This gives you three useful protections:

  • no blank titles

  • no tiny document bodies

  • no random status values like done or weird

Test it in the console:

bin/rails console

Enter fullscreen mode

Exit fullscreen mode

doc = Document.new(title: "", body: "short", status: "wat") doc.valid?

=> false

doc.errors.full_messages

=> [blocked]

"Title can't be blank",

"Body is too short (minimum is 20 characters)",

"Status is not included in the list"

]`

Enter fullscreen mode

Exit fullscreen mode

That’s the contract your form will rely on.

Build the controller with strong parameters

Strong parameters are Rails’ way of saying: only accept the fields I explicitly allow.

app/controllers/documents_controller.rb

class DocumentsController < ApplicationController  before_action :set_project  before_action :set_document, only: [:edit, :update]

def new @document = @project.documents.new(status: "draft") end

def create @document = @project.documents.new(document_params)

if @document.save redirect_to @project, notice: "Document created." else render :new, status: :unprocessable_entity end end

def edit end

def update if @document.update(document_params) redirect_to @project, notice: "Document updated." else render :edit, status: :unprocessable_entity end end

private

def set_project @project = Project.find(params[:project_id]) end

def set_document @document = @project.documents.find(params[:id]) end

def document_params params.require(:document).permit(:title, :body, :status) end end`

Enter fullscreen mode

Exit fullscreen mode

The line that matters most:

params.require(:document).permit(:title, :body, :status)

Enter fullscreen mode

Exit fullscreen mode

If the user submits extra fields, Rails ignores them. That prevents mass-assignment bugs like someone trying to set user_id, admin, or other attributes you never meant to expose.

Build the form

Create a partial so both new and edit can share it.

app/views/documents/form.html.erb

<%= form_with model: [@project, @document] do |form| %>  <% if @document.errors.any? %>

<%= pluralize(@document.errors.count, "error") %> prevented this document from saving:

<% @document.errors.full_messages.each do |message| %> <%= message %> <% end %>

<% end %>

<%= form.label :title %> <%= form.text_field :title %>

<%= form.label :body %> <%= form.text_area :body, rows: 10 %>

<%= form.label :status %> <%= form.select :status, [["Draft", "draft"], ["Published", "published"], ["Archived", "archived"]] %>

<%= form.submit %>

<% end %>`

Enter fullscreen mode

Exit fullscreen mode

Then render it from new.html.erb:

New Document <%= render "form" %>

Enter fullscreen mode

Exit fullscreen mode

And from edit.html.erb:

Edit Document <%= render "form" %>

Enter fullscreen mode

Exit fullscreen mode

Now when validation fails, Rails re-renders the form, keeps the submitted values, and shows the error messages.

Why unprocessable_entity matters

When validation fails, don’t redirect. Render the form again with status 422.

render :new, status: :unprocessable_entity

Enter fullscreen mode

Exit fullscreen mode

That keeps the in-memory object and its errors. If you redirect, you lose the errors and the user has to start over.

Add a custom validation

AI apps often need domain-specific rules. For example, maybe you don’t want documents that are too large for your current embedding pipeline.

app/models/document.rb

class Document < ApplicationRecord  belongs_to :project

validates :title, presence: true, length: { maximum: 120 } validates :body, presence: true, length: { minimum: 20 } validates :status, presence: true, inclusion: { in: %w[draft published archived] } validate :body_not_too_large

private

def body_not_too_large return if body.blank? return if body.length <= 10_000

errors.add(:body, "is too large for inline processing") end end`

Enter fullscreen mode

Exit fullscreen mode

That’s a real pattern. Put business rules in the model when they protect the integrity of the record.

A common mistake: trusting the form too much

Never rely on the HTML form alone.

This is not enough:

<%= form.select :status, [["Draft", "draft"], ["Published", "published"]] %>

Enter fullscreen mode

Exit fullscreen mode

Why? Because users can still send custom HTTP requests. The browser UI is not a security boundary. The model validation is.

What to practice next

Try these changes:

  • Add validates :title, uniqueness: { scope: :project_id }

  • Add a checkbox field like featured:boolean

  • Permit the new field in document_params

  • Add a custom validation that blocks banned words in title

Once forms and validations click, you’re ready for authentication. That’s where you stop building anonymous demos and start building real applications with users, sessions, and access control.

Was this article helpful?

Sign in to highlight and annotate this article

AI
Ask AI about this article
Powered by Eigenvector · full article context loaded
Ready

Conversation starters

Ask anything about this article…

Daily AI Digest

Get the top 5 AI stories delivered to your inbox every morning.

More about

modelupdateapplication

Knowledge Map

Knowledge Map
TopicsEntitiesSource
Forms & Val…modelupdateapplicationfeaturepublishedDEV Communi…

Connected Articles — Knowledge Graph

This article is connected to other articles through shared AI topics and tags.

Knowledge Graph100 articles · 198 connections
Scroll to zoom · drag to pan · click to open

Discussion

Sign in to join the discussion

No comments yet — be the first to share your thoughts!

More in Products