App Academy W5D4

Yizhe Wang
4 min readOct 28, 2020

--

Models, migrations & Associations

Rails: A server-side MVC web-application framework

  • ****full stack framework*****

如何建立一个新的rails project 都可以

ActiveRecord - an ORM Framework (The M in MVC)

Allows us to:

  • Represent models and their data
  • Represent associations between data
  • Validate models before they get persisted to the database
  • Perform database operations (CRUD) in OOP fashion
  • Migrations
  • Models
  • Associations

Migrations

  • Incremental and reversible changes made to a database schema, allowing it to evolve over time.
  • Not just a Rails thing - ubiquitous to app frameworks that work with relational DBs.
  • Rails allows you to use an easy Ruby DSL (domain-specific language) to describe changes to your tables, rather than write raw SQL.

Let's Migrate!

Note:

Migration demo

rails g migration createUsers 大小写,复数 convention

t.string column_name,

add_index :users, :username, unique: true

add_index :users, :email, unique: true

2.

rails g migration AddAgeToUsers

AddAgeToUsers class

def change   add_column :users, :age, :integer, null: falseenddb:migrate
check schema.rb
will see t.integer "age", null, false

bgg createChirps
class CreateChirps < ActiveRecord::Migration[5.2]
def change
create_table :chirps do |t|
t.text :body, null: false
# string <= 255, text longer
t.integer :author_id, null: false #这里的null false都是数据库层面的constraint而不是rails本身的validation。
end
add_index :chirps, :author_id # cannot unique this one, can have a lot of authors in the table
end
end
br db:migrate
tweets的表建立好了。

*****Common migration terminal commands*****

  • bundle exec rails g migration Create{TableName} #必须是复数
  • bundle exec rails g migration Add{ColumnName}To{TableName}
  • bundle exec rails g migration Remove{ColumnName}From{TableName}
  • bundle exec rails g migration AddIndexTo{TableName}

*****Common migration methods***************

  • create_table
  • add_column
  • change_column
  • remove_index

Changing existing migrations

  • You can't just edit the migration and run the migration again
  • Instead, you have two options:
  1. Write a new migration (much preferred) migration就是用来改变数据库的工具,你在model里面修改再多的class都没用,那些都是调用数据库的函数而已,并没有对数据库进行真正意义的修改,只有是做了一个migration,这才是对数据库进行改进,比如说加个column,改个表名啊这些,这些操作一旦进行,有的就很难撤回,所以要非常小心,关于表格的操作还是要更仔细的看一看。addtable会了,但是drop table啊什么的这些还没见过。
  2. Rollback: 这种操作其实非常不推荐,也不太现实,未来在真正的production环境下是不可能允许任何人rollback的,假如说某个数据库操作删掉了某个行,那就只能通过一些操作再把它加回来,rollback不是万金油,很多时候都不好用。

3. up down,在很多文档中看到过关于up和down的介绍,但是不知道怎么用,有时间要研究一下。

*****************************

  • rollback the migration (via rails db:rollback)
  • then edit your migration
  • run rails db:migrate to run the corrected version

Model

  • The central component of the MVC pattern
  • A class that represents and directly manages the data, logic, and rules for a table 必须是单数存在的
  • Typically contains: validations, associations, and custom methods
  • Inherits from ApplicationRecord (which, in turn, inherits from ActiveRecord) 不直接继承active record的原因是不想修改它,多一层保护。
  • There is a one-to-one correspondence between a model and a table,每个表对应一个class,每个instance对应一条数据。
  • An instance of this class / model represents a row in our table

Database Constraints vs Model Validations

  • Model validations are best used to provide error messages to users interacting with your app # validation更好,error更好看,初步防线,而model的contraints是最后防线
  • It's highly likely that:
  • You will interact with the database at some point outside of Rails
  • You will make a mistake in your code that causes invalid data
  • Database constraints are the last line of defense for data-integrity
  • Writing constraints is work, but they will save you a lot of pain

Common Validations

  • validates :some_column, presence: true
  • similar to null: false
  • validates :other_column, uniqueness: true
  • similar to unique: true
  • Custom Validations

Rails Models Demo

gem 'annotate'
能把schema里面的表关系以comment的形式放到相应的model里面去
create user的时候,id是postgres提供的。
rails c 已经load了所有的model
create a user
v.save!如果不成功会raise error
v.save 这个只会给你返回个true false
fakev.errors.full_messages #return array of full message
fakev.errors.messages #return as hash
Chirp.create(body:"msg",author_id: some_user.id)
增加自己的validation
def body_too_longif body && body.length > 140
errors[:body] << "is too long"
end
end
validate的时候,要注意,是errors[:class_name] << 'error message' 最好写的相关一点,这个如果validate不同多,errors就会直接停止数据库操作,并返回写好的数据信息了。
不再是validates了,而是validate******不要忘记这个部分。validate :body_too_long #后面也不用写什么true什么的

select user.id from users join likes on likes.liker_id = users.id join chirps on users.id = chirps.author_id

  • Connections between two Active Record models.
  • 两个model之间的关系了,has_many, belongs_to, has_one, has_many_through…
  • Make common operations simpler and easier in your code.
  • We don't have to write anymore SQL JOIN statements
  • Simply methods that we can call
class Chirp < ApplicationRecord
belongs_to :author, primary_key: :id, foreign_key: :author_id, class_name: :User
validates :body, presence: true
validates :author_id, presence: true
validate :body_too_long
def body_too_long
if body && body.length > 140
errors[:body] << "is too long"
end
end
end

Example Association

class Strike < ApplicationRecord
belongs_to :student,
primary_key: :id,
foreign_key: :student_id,
class_name: :Student
end
凡是这种belongs to的就说明这个表的级别要相对低一些,它存了别人的id,就说明有一种隶属关系,user可以有很多的transction,但是user表绝对不可能存那么多transaction id在里面,那样存储就会非常的费,而且没有意义,join之后正常就都出来了,但是transaction这种级别很低的表,就会存很多别人的id,users,product,shopper,market...因为它就是最granular的级别。数据库的建立有的时候就是要考虑,哪些东西是真正需要建立关系的,关系(id)存在哪个表里面,这些其实是费时间的。
有时间的话,尝试建立一个tlq的数据库吧。
  • belongs_to is an ActiveRecord method that takes the following arguments
def belongs_to(:name, options = {})
end

Associations Code Demo

*****Associations Recap*****

class Chirp < ApplicationRecord
#validations go here
belongs_to :user,
primary_key: :id,
foreign_key: :author_id,
class_name: :User
end

class User < ApplicationRecord
#validations go here
has_many :chirps,
primary_key: :id,
foreign_key: :author_id,
class_name: :Chirp
end
为什么primary——key都一样?暂时记住就是conversion吧
belongs to的foreign key同时也是has——one has——many的foreign key,都一样,虽然关系是相反的,我们就这样决定。

has_one

  • easily confused with belongs_to
  • only write them if you've already made the corresponding belongs_to
  • 这个反正还是看id存在哪吧,id存的那个model就是belongs——to,否则就是has one

Strategy when writing associations

  • Start with belongs_to
  • Write the corresponding has_many or has_one in the other model.
  • Write has_many throughs using only other associations in the model as the through, check the associated model for an association to be the source.
  • has many through这个东西很牛逼,就是把表和表相连接了,association级别的join,model中互相就通过某个id,就连在一起。

Terminal Commands Recap

  • rails new {project_name} -G -d postgresql or
  • rails _VERSION_ new {project_name} -G -d postgresql
  • bundle exec rails db:create
  • bundle exec rails g migration Create{TableName}
  • bundle exec rails db:migrate
  • bundle exec rails db:migrate:status
  • bundle exec rails g model {ModelName}

--

--