Ruby rails: models
Rails models contain buiness logic, and abstract your database.
In code, they are represented as ActiveRecord::Base
subclasses.
If you want to change an existing model, see ruby rails: migrations.
Documentation
Docs activerecord docs https://guides.rubyonrails.org/active_record_basics.html activemodel docs https://guides.rubyonrails.org/active_model_basics.html association docs https://guides.rubyonrails.org/association_basics.html validation docs https://guides.rubyonrails.org/active_record_validations.html query docs https://guides.rubyonrails.org/active_record_querying.html callback docs https://guides.rubyonrails.org/active_record_callbacks.html API Docs ActiveRecord
(See "Included Modules" for options)https://api.rubyonrails.org/classes/ActiveRecord/Base.html column types https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column
Locations
{project}/config/database.yml
database connection details {project}/app/models/*.rb
models stored here
Example
Most model functionality is provided by
ApplicationRecord
(getters, setters, queries, etc). Normally, methods defined on models are for manipulating data of it's type.
id
column is created automatically- A large portion of the model metaprogramming the model itself
- how/when to validate
- translating between database/ruby types
- associations between other ActiveRecord classes (has_many, etc)
class User < ApplicationRecord # =============== # metaprogramming # =============== # convert 'legal_voting_age' to boolean when accessed, # from boolean on write. attribute :legal_voting_age, :boolean, default: false # on save (before INSERT/UPDATE), field will be validated # ``self.errors[]`` attribute will hold validation errors validates :firstname, format: {with: /[a-zA-Z]+/}, length: 3 # 'keyboards' attribute lists all 'keyboards' # table-columns associated with this user. # # you can also create keyboard objects from it, with 'user' # field already assigned: ``user.keyboards.create(...)`` has_many :keyboards # ======= # methods # ======= def calculate_age_in_dog_years # ... end endYou may also find ruby rails: migrations useful.
Commandline
NOTE:
You can automatically create a model and CRUD views/controllers using scaffolds
# create model 'User', # db-migration to create table 'users' rails generate model User \ `# 'id' as bigint primary key is automatic` \ first_name:string \ last_name:string \ bio:text # run migration rails db:migrate rails db:migrate RAILS_ENV=test # revert last migration rails db:rollbackWhen you generate a model, the database schema gets written to
db/schema.rb
. When changing migrations, you may want to regenerate this file.# rollback all migrations rake db:migrate VERSION=0 # regenerate schema.rb (if db:reset is failing after changes) bundle exec rake db:schema:dump # rebuild the db from scratch bundle exec rake db:reset
Configuration
config/database.yml
configures database connectionsdb/seeds.rb
defines data to be created with databaseSee ruby rails: configuration section on databases for details.
Column Datatypes
List of all common datatypes.
Specific database types may implement additional types.# misc :primary_key :boolean :binary # text :string :text # numbers :integer :bigint :float :decimal :numeric # dates :datetime :time :date
Instantiate/Create
YourObject.new(name: 'will') # instantiate (not saved to db) YourObject.create(name: 'will') # create (saved to db) YourObject.build(name: 'will') # build (not saved to db) instance.valid? # run validations, return true/false if valid instance.errors # array of validation errors (must validate first)You can also create through associations:
instance.associated_model.create(name: 'will') instance.associated_model.build(name: 'will') instance.create(associated_model: { ... }) instance.build(assiciated_model: { ... }) instance.create_{association_name}( ... ) instance.build_{association_name}( ... )
Tables/Classes
- activerecord classes are PascalCase their table is snake_case
- activerecord classes are singular, their table is plural
- subclasses refer to their parent's table
class User < ActiveRecord::Base # table: 'users' class UserPermission < ActiveRecord::Base # table: 'user_permissions' class SpecialUser < User # table: 'users' # override table name class UserGroupPermissions < ActiveRecord::Base self.table_name = 'group_permissions' endGet Table Info
User.columns # columns, type info, etc. User.column_names # column names
Columns/Attributes
Rails automatically creats a getter/setter for every column in the table.
Example Class
The table:CREATE TABLE users ( id INT NOT NULL PRIMARY_KEY AUTO_INCREMENT, first_name VARCHAR(50), last_name VARCHAR(50) );Referred to by:
class User < ActiveRecord::Base endIs magically made into something like
class User < Active def first_name=(val) self[:first_name] = val end def first_name self[:first_name] end def last_name=(val) self[:last_name] = val end def last_name self[:last_name] end def id=(val) self[:id] = val end def id self[:id] endCustom Getters/Setters
- rails automatically assigns a getter/setter for each field
- you may customize them, using this syntax
- this is implemented by overriding
write_attribute()
andread_attribute
methods.class User < ActiveRecord::Base # rails setter def name=(val) self[:name] = val end # rails getter def name self[:name] end endAttribute Types (and non-column attributes)
See https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute
- Translate between a database-column type, and one that is meaningful to your application
- Create attributes that are not database columns
CREATE TABLE users ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, first_name TINYTEXT, age FLOAT, # <-- float, not int );class User < ActiveRecord::Base attribute :age, :integer # <-- convert to int endThis article may also be useful (describing various uses for virtual attributes) https://jtway.co/rails-virtual-attributes-use-cases-cf33bd45e2a4
Reflection
Short Info
User.reflect_on_all_associations.map(&:name) # list associated tables User.column_names # list column namesDetailed Info
User.reflect_on_all_associations # list all associations User.columns # list all columns
Associations
Associations can map associations between database tables.
one to one
class Employee < ActiveRecord::Base has_one :office # assoc. stored on office: office.employee_id end class Office < ActiveRecord::Base belongs_to :employee # assoc. stored on office: office.employee_id endone to many
class Manager < ActiveRecord::Base has_many :employees end class Employee < ActiveRecord::Base belongs_to :manager # foreign key - manager_id endvariations
has_many :authored_posts, foreign_key: "author_id", class_name: "Post" has_many :edited_posts, foreign_key: "editor_id", class_name: "Post"
many to many
class Assignment < ActiveRecord::Base belongs_to :programmer # foreign key - programmer_id belongs_to :project # foreign key - project_id end class Programmer < ActiveRecord::Base has_many :assignments has_many :projects, through: :assignments end class Project < ActiveRecord::Base has_many :assignments has_many :programmers, through: :assignments endvariations
has_many :authored_posts, foreign_key: "author_id", class_name: "Post" has_many :edited_posts, foreign_key: "editor_id", class_name: "Post"polymorphic
Queries
Validation
validates
ActiveRecord::Base
eventually includesActiveModel::Validations::ClassMethods
e- The
validates
method lets you useActiveModel::EachValidator
subclasses defined/included within the same namespace by translating the name to{CheckName}Validator
- see https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates
class TitleValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) record.errors.add attribute, (options[:message] || "is not an email") unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i end end class UserModel < ActiveRecord::Base # NOTE: @username is a column validates :username, title: true # 'title:' refers to above 'TitleValidator' end
Save/Load
yourobject.save # commit changes to database yourobject.reload # reload from database
Errors
See https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-generate_message
user = User.new user.errors.add(:my_attribute, :not_implemented) user.errors.full_messagesCustom errors
errors = ActiveModel::Errors.new(SomeClass) errors.add(:my_attr, "must be assigned")
Hooks / Callbacks
There are various hooks/callbacks that you can use to alter model behaviour, perform validation, etc.
before_save :method_name