14日目：Scaffoldで作成したサイトにgem devise等を組み合わせていく

今週からは、scaffoldで作成した大学データと、gemのdevise、Bootstrap等を組み合わせる。

#やった事
- Railsの命名規則(単数形と複数形)
- DBのカラム定義を後から変更
- render partial: 部分テンプレの参照
- validation
- **UNSIGNEDという型が存在しないPostgreSQL**

#使用環境
- ホストOS: Windows10 Home
- 仮想環境: Ubuntu Bento/Bionic
- Ruby：2.51
- Rails: 5.2.2
    - gem 'devise' :　ログイン等の機能用
    - gem 'kaminari' : ページネーション
- DB: PostgreSQL


#Railsの命名規則(単数形と複数形)
rails gコマンドで、controller名やmodel名を指定する際に、混乱した。

```
# rails generate scaffold model名の単数形　フィールド名の型と並び
# rails g controller controller名の複数形
# カラムの追加
# rails generate migration AddカラムToモデル名の複数形 フィールド名と並び
```
- model名は単数形で、頭文字を大文字にする
    - scaffoldの場合、modelが基準
    - modelは設計書であり、（テーブル1つに付き）1つなため
- controller名は複数形で、頭文字を大文字にする。
    - 1つのcontrollerに複数のactionが含まれるため


#DBのカラム定義を後から変更
rails g scaffoldコマンド時に、ClubStudentの外部キーの定義をreferecesとミスタイプしていた。
###修正方法：app/db/migrate下のファイルを修正
```db/migrate/20190326030303_create_club_students.rb
class CreateClubStudents < ActiveRecord::Migration[5.2]
  def change
    create_table :club_students do |t|
     #スペリングミス
     #t.refereces :student
     #訂正分
      t.references :student
      t.references :club, foreign_key: true
      t.timestamps
    end
  end
end
```

なお、ALTTER TABLEコマンドを使って、あとから修正する方法は
DB内のデータを書き換えるだけで、アプリ自体のファイル等は編集されない。

```sql:mysql
# ALTER TABLE テーブル名 MODIFY COLUMN カラム名 新しい定義
ALTER TABLE ClubStudent MODIFY COLUMN student references
```
つまり、原因の根本的な部分を修正できないので、駄目

#render partial: 部分テンプレの参照
[render レンダリング(render) - railsドキュメント](http://railsdoc.com/references/render)

全てのページのヘッダー(上部）に、ログアウトや他のstudentやclubs等のリンクを乗せる
![tempsnip.jpg](https://qiita-image-store.s3.amazonaws.com/0/294402/2bd10526-e610-0345-62aa-bc33270eafbc.jpeg)


共通して表示させるので、/app/views/layouts/application.html.erb　を編集する。
なお、部分テンプレファイル名は『_』アンダーバー始まり

```erb:/app/views/layouts/application.html.erb
<body>
# <%= render :partial => '部分テンプレ名' %>
  <%= render :partial => 'shared/header' %>
</body>
```

表示させたいリンクを書きこむ。

```erb:/app/views/shared/_header.html.erb
<%= link_to 'Student list', students_path %> 
<%= link_to 'subjects list', subjects_path %> 
<%= link_to 'clubs list', clubs_path %> 
<%= link_to 'exam_result list', exam_results_path %>
<%= link_to 'club_stdent list', club_students_path %>
<%= link_to 'Log Out', destroy_student_session_path, method: :delete %>
```

#validation
[参考リンク　Active Record Validations](https://guides.rubyonrails.org/active_record_validations.html)
バリデーションは有効なデータだけをDBに保存するのを確実にするための最善策。

今回の実装先：clubの新規作成ページ
![newclub_form.JPG](https://qiita-image-store.s3.amazonaws.com/0/294402/7d4b09ce-2f84-9616-2a78-e0ceb6b779f1.jpeg)

##validate条件
###空でないこと
```rb:
validates :name, presence: true
# 因みに、空が条件ならば
# validates :name, absence: true
```
###入力文字の長さ
**文字の最大長は、データ型を要参照。varcharなら255文字まで**

```/app/models/club.rb
# 2文字以上
validates :name, length:{minimum:2}
# 255文字以上
validates :name, length:{maximum:255}
```
###exclusion含まない
授業ではしなかったが、クラブ名末尾に『部』を入れない、という条件を追加してみる

```/app/models/club.rb
validates :name, exclusion: { in: %w(部 サークル) }
# 『含む』ならinclusion
```
##実装結果
空白や文字列長、『サークル』という語には、validatesが発動した
![validate-clubnew.JPG](https://qiita-image-store.s3.amazonaws.com/0/294402/ed56c5ae-e53c-493a-efd2-2b32b6a7c003.jpeg)


ただ、現状だと、『テニスサークル』の様に文字列と連結すると、validateが動かない
あとまわし

```
Club was successfully updated.

Name: サークル部
```
#type "unsigned" does not exist (※Postgresql)
validatesの実装していく最中に、エラーに気づいた

studentのeditページで更新すると、

```
ActiveRecord::StatementInvalid in StudentsController#show

PG::UndefinedObject: ERROR: type "unsigned" does not exist LINE 1: ...id as subject_id, CAST(AVG(exam_results.score) as unsigned) ... ^ : SELECT subjects.id as subject_id, CAST(AVG(exam_results.score) as unsigned) as avg_score, MAX(exam_results.score) as max_score, MIN(exam_results.score) as min_score FROM "students" INNER JOIN "exam_results" ON "exam_results"."student_id" = "students"."id" INNER JOIN "subjects" ON "subjects"."id" = "exam_results"."subject_id" GROUP BY subjects.id ORDER BY subjects.id
```
と、エラーを吐き、因みに、ブラウザの戻るボタンで戻ると、更新されている。
また、エラー原因であると思わる、StudentController#showは

```rb:app/controllers/students_controller.rb

def show
    @students = Student.joins(:subjects)
                       .select('students.name, students.email, students.age, students.gender, students.opinion, subjects.id as subject_id')
                       .select('exam_results.name as exam_result_name, subjects.name as subject_name, exam_results.score')
                       .select('CAST((exam_results.score / subjects.max_score) * 100 as unsigned) as ratio')
                       .where(id: params[:id])

    avg_result = Student.joins(:subjects)
                        .select('subjects.id as subject_id')
                        .select('CAST(AVG(exam_results.score) as unsigned) as avg_score')
                        .select('MAX(exam_results.score) as max_score')
                        .select('MIN(exam_results.score) as min_score')
                        .group('subjects.id')
                        .order('subjects.id')
(以下略)
```
因みに、このcontrollerは、以前の大学データのcontrollerからコピーしてきたものだ。
つまり、MySQLで動くアプリのcontroller。

##unsigned　(MySQL)
- MySQLにおいては正と負の整数を扱うことができる。
- unsignedを指定すると、正の数しか格納できなくなり、代わりに範囲が2倍になる。
- **unsignedにした値が負になると、エラーを起こす**
    - UNSIGNEDは、マイナス値が入らないだけでなく、マイナスになる計算もできない。
    - CASTで一時的に型を変える事で回避は可能。

## `Postgresqlにはunsined型は存在しない(最重要)`
###対応策
まだ、試験結果のデータを入れてないので、功を奏すか分からないけれども

- unsignedをint等の型に置き換える
    - 今回は試験点数を扱っていて、intで事足りると思われる。
    - ただ、MySQLでint unsignedだと、範囲が正の方向に2倍になっている。
    - **扱う数によっては、intより1つ上ののbigintに変える必要がある**
- CAST as unsignedの部分を消す
    - MySQLでCAST as unsingedは、一時的に型を指定している

前回の大学データに倣って、今回はcast as intに変更した

```app/controllers/students_controller.rb
# (該当部分だけ抜き出し）
.select('CAST((exam_results.score / subjects.max_score) * 100 as int) as ratio')

.select('CAST(AVG(exam_results.score) as int) as avg_score') 
```
正常に、studentデータのedit、updateが機能した。

#データ入力にはpassword情報が必要
deviseの関係上、パスワード情報入りのデータでないと、コンソールから入力できない。
##passwordカラムの追加
deviseのモデル等がある、Studentテーブルに、パスワードカラムを追加した。

```:terminal
# rails generate migration AddカラムToモデル名の複数形 フィールド名と並び
rails g migration AddPasswordToStudents password:string
```
db/migrate下にファイルが生成される

```/db/migrate/20190327144825_add_password_to_students.rb
class AddPasswordToStudents < ActiveRecord::Migration[5.2]
  def change
    add_column :students, :password, :integer
  end
end
```
これで、パスワード情報入りの生徒データをDBに入力できる。

##データ入力
未だデータの無い、生徒データと試験結果データをコンソールで入力した。

```rb:
(1..100).each do |num|
  if num % 2 == 0 && num % 3 ==0
    gen = 0
    ag = 1
    elsif num % 2 == 0
    gen = rand(2)
    ag = rand(3)
  else
    gen = 1
    ag = 0
  end
  op = (1..10).map{('あ'..'わ').to_a[rand(26)]}.join
  nm = (1..3).map{('あ'..'わ').to_a[rand(26)]}.join

  user = Student.create!(name: "#{nm}", email: "#{nm}-#{rand(98)}@gmail.com", gender: gen, age: ag, opinion: op,password: 'password')
  end
```
```rb:

  (1..100).each do |i|
  student = Student.find(i)
  1.upto(rand(0..4)) do
    student.clubs << Club.find(rand(1..14))
    student.save
  end
end
```