Skip to main content
This is a backend customization. You need to perform it in the backend directory if you used create-spree-app to set up your Spree application.
In this step we’ll create the Spree::Brand model with everything it needs: a name column, a rich text description, and an uploadable logo.

Step 1: Generate the Model

Spree ships a spree:model generator that produces a model and migration following all Spree conventions — Spree:: namespacing, prefixed IDs, presence validations, and API filtering allowlists. It understands Rails attribute types including the virtual ones, so rich text and file attachments are part of the same command:
Throughout this tutorial every command is shown in two forms. Spree CLI is for projects created with create-spree-app, where the app runs in Docker and spree <command> routes into the container. Without Spree CLI is for apps running Spree backend directly on your machine.
This creates two files:
  • app/models/spree/brand.rb — the model
  • db/migrate/XXXXXXXXXXXXXX_create_spree_brands.rb — the migration
Now apply the migration:
This creates the spree_brands table with an indexed name column. The description and logo attributes don’t add columns — rich text lives in Action Text’s action_text_rich_texts table, and uploads live in Active Storage’s tables.

Step 2: Understand the Generated Model

Open app/models/spree/brand.rb:
app/models/spree/brand.rb
Line by line:
  • Spree.base_class — inherits all Spree model functionality (multi-store scoping helpers, preferences, and more). The class is namespaced under Spree::, so it’s available as Spree::Brand.
  • has_prefix_id :brand — records get Stripe-style public IDs like brand_k5nR8xLq. APIs never expose raw database IDs.
  • has_rich_text :description — formatted content via Action Text.
  • has_one_attached :logo — file uploads via Active Storage, with image processing and direct-to-storage uploads.
  • validates :name, presence: true — generated automatically for required columns.
  • whitelisted_ransackable_attributes — controls which attributes API clients may filter and sort by. Only allowlisted attributes are queryable, so adding name here is what later makes ?q[name_cont]=nike work.

Step 3: Try It in the Console

We’ll upload the logo through the admin UI in the next step — the model side is already done.
The generator accepts more attribute types and options — references with auto-resolved class names (user:belongs_to), unique indexes (slug:string:uniq), soft delete (--paranoid), and custom fields support (--metafields). Run it with --help to see everything.

Next Step

The model is complete. Now let’s give admins a UI to manage brands: Admin Dashboard.