Help us understand the problem. What is going on with this article?

21日目:H30秋基本情報技術者試験の問3データベース

More than 1 year has passed since last update.

前回のアウトプットから、だいぶ間が空いたが、今回から暫く、基本情報技術者試験問題に向き合う。

コンサートチケットの予約販売サイト、特にチケット販売サブシステムを用いる問題だった。
特に、今回のスクールの大成すべく、問3は実際にDBのみならず、Appも作成したい。
出典: 平成30年度 秋期 基本情報技術者試験(FE)試験区分 午後 問1 問3

DB仕様、ER図

Cacooを用いてみた。カラム名が冗長な気がする。
Entity Relationship Diagram.png

販売サブシステムの(引用、一部編集)

  1. コンサートの席種はS,A,Bがあり、コンサート毎にその価格と発売席数は異なる。
  2. 取り扱うコンサートの席種(S,A,B)毎の販売可能な席数を管理する。
  3. 会員が購入申込すると、一意な販売IDを生成して販売表にレコード追加
  4. 会員が支払いをすると、決済処理として決済表に追加。
  5. 決済期限の翌日に、決済期限を過ぎた販売表中のレコードと販売IDが同じレコードが決済表にない場合、購入申込は取り消され、バッチ処理として、決済表に当該販売IDを主キーとして決済表に決済日をnull、決済額を-1としてレコード追加
  6. バッチ処理は毎夜0~4時の販売サイトのシステムメンテナンス時に行う
  7. 購入申込席数が販売可能な席数を上回る場合は、販売終了と表示し、購入申込を受け付けない 等々。。。

※注意事項
コンサート席種のカラムが、商品詳細表と販売表に1つずつあり、重複していて気持ち悪いが、
試験問題がそうなっており、敢えてそうしてる模様なので、そのままにする。
その意図を反映するため、席種の商品詳細表側はinteger、販売表側はstringにしておく。

使用環境

  • ホストOS: Windows10 Home
  • 仮想環境OS: Ubuntu Bento/Bionic
  • Ruby:2.51
  • Rails: 5.2.2
  • DB: PostgreSQL

rails gコマ

今回もdeviseを利用することを、頭に入れておきたい。

terminal
rails new concert_ticket -d postgesql
Gemfile
# miniracerをアンコメント
gem 'miniracer'
# authentication
gem 'devise'
terminal
bundle
database.yml
username: admin
password: 〇×△
host: localhost
terminal
rails db:create
terminal
# deviseのインストール
rails g devise:install
# Userモデルにdeviseを追加。モデルなので、単数形
rails g devise User name:string email:string point:integer

# 商品表と商品詳細表テーブルはscaffoldを用いる
rails g scaffold Concert concert_info:text concert_date:datetime
rails g scaffold ConcertDetail concert:references seat_grade:integer seat_price:integer tickets_total:integer

# Saleモデルの追加
# カラムが多い。販売表モデル 商品表外部キー 会員表外部キー 席種        席数         販売日        販売額        決済期限日         使用ポイント       
rails g model Sale concert:references user:references seat_grade:string seats_total:integer sale_date:datetime sales_total:integer payment_deadline:datetime used_point:integer

# 決済表モデルを追加
rails g model Payment sale:references payment_date:datetime payment_total:integer added_point:integer

関連付け

app/models/concert.rb
has_many :concert_detail
has_many :sale
app/models/concert_detail.rb
belongs_to :concert
app/models/user.rb
has_many :sale
app/models/sale.rb
has_one :payment
belongs_to :concert
belongs_to :user
app/models/payment.rb
belongs_to :sale

userテーブルにadminカラム追加

単に入れ忘れた。

terminal
rails g migration AddAdminToUser admin:boolean
/db/migrate/(timestanp)_add_admin_to_student.rb
class AddAdminToUser < ActiveRecord::Migration[5.2]
  def change
    add_column :students, :admin, :boolean,default: false
  end
end

validation追加

後からさらに追加していくと思うが、次のデータ入力の為に。
なお、ファイル中のvalidation前後のコードは省略。

app/models/user.rb
# name の空欄不可
validates :name, presence: true
# emailの空欄不可、一意指定
validates :email, presence: true, uniqueness: true
# emailの構成文字の大文字小文字の区別はしない
# あとで、正規表現込みのvalidation追加等するが、あとまわし。
validates :email, presence: { case_sensitive: false }
app/models/sale.rb
# 購入席数の空欄不可
validates :seats_total, presence: true
# 購入席数は4席まで。(試験問題には無いけど)
validates :seats_total, numericality: {less_than_or_equal_to: 4}

初期データ作成 (seeds.rb使用)

普段はコンソールからが多いが、rails tutorialをやって、seeds.rbの方が勝手が良かったので。
あと、gem Fakerは、SAOなど『遊び』があって、好きだが、rubyメソッドの勉強にならないので、今回はパス

ユーザ作成

カラムは氏名とemailアドレス

db/seeds.rb
# 管理者
User.create!(name: 'おりばー', email: 'ないしょ@gmail.com', password: 'ないしょ', admin: true)

# 一般ユーザ。とりあえず、20人作る。
20.times do |i|
  name = (1..5).map { ('あ'..'ん').to_a[rand(20)] }.join
  email = ('a'..'z').to_a.shuffle[1..5].join

  User.create!(name: name, email: "#{email}-#{i}@gmail.com", password: SecureRandom.base64)
end

商品表と商品詳細表の作成

(まだ足りないと思うが)リファクタリングの結果、コードが合わさったので、纏めて表示。
なお、qiita用に、一部コードの順序を変えてある、が、見づらい。

db/seeds.rb
# concert
# 20回分のコンサート、20組分のアーティストを作成
require 'active_support/all'

concert_name = %w[雅楽 令和 ジャズ ラスト ロック アニソン]
local = %w[東京 札幌 名古屋 大阪 広島 宮崎 台南 Cebu Perth]
artists = []

# 20回分のコンサート、20組分のアーティストを作成
(1..20).each do |n|
  # コンサート開催日として、期間s1 ~ s2までのランダムな年月日を作成
  s1 = Date.new(2019,5,31)
  s2 = Date.new(2020,3,31)
  s = Random.rand(s1..s2)

  # 出演アーティストをランダムで作成して、配列に入れる。
  # 作成した配列の並びをシャッフルし、先頭から1~5アーティストを取得し、カンマで繋げた文字列に。
  artists << (1..3).map { ('A'..'Z').to_a[rand(20)] }.join.to_s
  who_join = artists.shuffle.take(rand(5)+1).join('、')

  # 試験問題の"クリスマスコンサート2018 in 東京 出演:Xバンド・・・"の表記に沿ってあげる。
  infomation = "#{concert_name.sample}コンサート #{s.strftime('%Y')} in #{local.sample} 出演:#{who_join}"
  # コンサート情報の開催年と、開催日時が合致するようにした。
  Concert.create!(concert_info: infomation, concert_date: s.strftime('%Y-%-m-%-d'))

  concert = Concert.find(n)
  (0..2).each do |i|
    # seat_price S > A > B
    # prices = [price_s , price_a , price_b]
    # capacity= [sの席数、aの席数、bの席数]
    prices = [[40000, 35000, 30000], [28000, 25000, 23000, 19000], [18000, 15000, 13000, 11000, 9000]]
    capacity = [rand(10..50), rand(100..500), rand(500..1000)]
    ConcertDetail.create!(concert:concert,seat_grade:i, seat_price:prices[i].sample, tickets_total: capacity[i-1])
  end      
end

actrive_recordを使おう

参照

初期データ投入

terminal
ralis db:seed

カラム名を変更しよう

思った。非常に分かりづらい。controller作成時に疲れそう。
会員表(User)テーブルはパス。あと、コードは一部省略

商品表(Concert)テーブル
現状カラム:コンサート情報:concert_info , 開催日時:concert_date
=>infomation , date

db/migrate/(timestanp)_create_concerts.rb
    create_table :concerts do |t|
      t.text :infomation
      t.datetime :date
      t.timestamps
    end

商品詳細表(ConcertDetail)テーブル
現状:席種:seat_grade , 販売価格:seat_price , 販売席数:ticket_total
=> grade , price , capacity

db/migrate/(timestanp)_create_concert_details.rb
create_table :concert_details do |t|
      t.references :concert, foreign_key: true
      t.integer :grade
      t.integer :price
      t.integer :capacity
      t.timestamps
    end

販売表(Sale)テーブル
席種:seat_grade、席数:seats_total、販売日:sale_date、販売額:sales_total、決済期限日:payment_deadline、使用ポイント:used_point
=> grade , number_of_seats , date , amount , payment_deadline , used_point
このテーブルは安易にリネームすると尚更分かりづらくなるので、注意した。
販売額amountとごっちゃにならず、席数に最適な短い訳が思いつかないので、一応英語に従った。、

db/migrate/(timestanp)_create_sales.rb
create_table :sales do |t|
      t.references :concert, foreign_key: true
      t.references :user, foreign_key: true
      t.string :grade
      t.integer :number_of_seats
      t.datetime :date
      t.integer :amount
      t.datetime :payment_deadline
      t.integer :used_point
      t.timestamps
    end

決済表(Payment)テーブル
決済日:payment_date、決済額:payment_total、付与ポイント:added_point
=> date , amount , added_point

db/migrate/(timestanp)_create_payments.rb
create_table :payments do |t|
      t.references :sale, foreign_key: true
      t.datetime :date
      t.integer :amount
      t.integer :added_point
      t.timestamps
    end

seeds.rbファイルもカラム名を使用しているので、一部変更

terminal
rails db:migrate:reset
rails db:migrate
rails db:seed

ER図を修正

modified.png

あとは、controllerとviewを弄っていくか。

OriverK
農学学士。植物学等を専門に学び、所属研究室では画像処理による農作物列検出の研究。卒業後は食品系企業に就職したが幸か不幸か退職することになり、この機を逃すまいと勉強を本格的に再開し、現在の転職活動に至る。
https://oriverk.dev
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away