This guide assumes you’ve completed the Model, Admin, and File Uploads tutorials.
What We’re Building
By the end of this tutorial, you’ll have:- Products associated with Brands
- A brand selector in the Product admin form
- Understanding of how to safely extend Spree core models
Understanding Decorators
When working with Spree, you’ll often need to add functionality to existing models likeSpree::Product or Spree::Order. However, you shouldn’t modify these files directly because:
- Upgrades - Your changes would be lost when updating Spree
- Maintainability - It’s hard to track what you’ve customized
- Conflicts - Direct modifications can conflict with Spree’s code
How Decorators Work
In Ruby, classes are “open” - you can add methods to them at any time. Decorators leverage this by:- Creating a module with your new methods
- Using
Module#prependto inject your module into the class’s inheritance chain - Your methods run first, and can call
superto invoke the original method
Product.prepend(ProductDecorator) - this inserts your module at the beginning of the method lookup chain, so your methods are found first.
Step 1: Create the Migration
First, add abrand_id column to the products table:
db/migrate/XXXXXXXXXXXXXX_add_brand_id_to_spree_products.rb
We intentionally don’t add a foreign key constraint. This keeps the association optional and avoids issues if brands are deleted. Spree follows this pattern for flexibility.
Step 2: Generate the Product Decorator
Spree provides a generator to create decorator files with the correct structure:app/models/spree/product_decorator.rb:
app/models/spree/product_decorator.rb
Step 3: Add the Brand Association to Product
Update the decorator to add thebelongs_to association:
app/models/spree/product_decorator.rb
Understanding the Code
self.prepended(base)- This callback runs when the module is prepended to a class. Thebaseparameter is the class being decorated (Spree::Product)base.belongs_to- We call class methods onbaseto add associations, validations, scopes, etc.optional: true- Products don’t require a brand (thebrand_idcan beNULL)
Step 4: Add Products Association to Brand
Now update your Brand model to define the reverse association:app/models/spree/brand.rb
We use
dependent: :nullify instead of dependent: :destroy. When a brand is deleted, products will have their brand_id set to NULL rather than being deleted. This is safer for e-commerce data.Step 5: Permit the Brand Parameter
For the admin form to save the brand association, we need to permit thebrand_id parameter.
Add to your Spree initializer:
config/initializers/spree.rb
Step 6: Add Brand Selector to Product Admin Form
Create a partial to inject the brand selector into the product form. Spree’s admin product form has injection points for customization. Create the partial:app/views/spree/admin/products/_brand_field.html.erb
- Spree 5.2+
- Spree 5.1 and below
config/initializers/spree.rb
Step 7: Add Translation
Add the translation for the brand label:config/locales/en.yml
Testing the Association
Verify everything works in the Rails console:Decorator Best Practices
Use prepended callback
Always use
self.prepended(base) for class-level additions like associations, validations, and scopes.Keep decorators focused
Each decorator should have a single responsibility. Create multiple decorators if needed.
Call super when overriding
When overriding methods, call
super to preserve original behavior unless you intentionally want to replace it.Test decorated behavior
Write tests specifically for your decorated functionality to catch regressions.
Common Decorator Patterns
Adding Validations
Adding Scopes
Adding Callbacks
Adding Class Methods
Complete Files
Product Decorator
app/models/spree/product_decorator.rb
Brand Model (Updated)
app/models/spree/brand.rb
SEO features like slugs, meta titles, and FriendlyId are covered in the SEO tutorial.
Spree Initializer Additions
config/initializers/spree.rb
- Spree 5.2+
- Spree 5.1 and below
config/initializers/spree.rb
Related Documentation
- Model Tutorial - Creating the Brand model
- Admin Tutorial - Building the admin interface
- Customization Overview - More customization patterns
- Products - Product model documentation

