Rails: Preparing scopes with values for creation

Posted . Visible to the public. Repeats.

Calling create or build on relations

When you call create (or build) on a relation, the newly created record inherits values from the relation’s scoped attributes:

# User(id: integer, name: string)
user = User.where(name: "hello").create

user.name # => "hello"

This only works if the scope condition is a hash with scalar values, that materialize as equality comparisons. If your scope uses an SQL snippet, or any operator other than equality, the created record will not inherit any attribute value:

User.where("age >= 18").build.age # => nil
User.where("name ILIKE '%foo%'").build.name => nil
User.where(name: ['Bob', 'Alice']).build.name => nil

default_scope

This behavior is especially relevant when using default_scope. Despite what the name might suggest, default_scope has no special mechanism for assigning default values. It simply applies given scopes by default, which can indirectly provide values for newly created records.

class User < ApplicationRecord
  default_scope { where(name: "hello") }
end

user = User.create
user.name # => "hello"

create_with

Sometimes you may want to apply a condition by default without using the same value when creating new records.

For example, suppose a record has a boolean visible flag. We want queries to return only visible posts by default, while newly created posts should remain hidden unless explicitly made visible:

# Post(id: integer, visible: boolean)
class Post < ApplicationRecord
  default_scope { where(visible: true).create_with(visible: false) }
end

post = Post.create
post.visible # => false

Post.all
# SELECT * FROM posts WHERE visible IS TRUE
Profile picture of Niklas Hä.
Niklas Hä.
Last edit
Niklas Hä.
License
Source code in this card is licensed under the MIT License.
Posted by Niklas Hä. to makandra dev (2026-07-22 14:37)