SinatraとSQLiteと 2
大晦日ハッカソン、続く正月ハッカソンで、 前回作成したプロジェクトを元にブログサイトに挑戦してみました。
やっぱり時間や目標をある程度決めて開発するというのは良いもので、まだまだ課題は多いとはいえ、ブログ記事を表示する部分に関してはそれっぽくなった気がします。
今回はActiveRecordやSQLiteのテーブル連結や検索などをメモ代わりに書いてみることにします。
ファイル構成
views/blog.slimなど、名前を変えたファイルがあること以外は前回とほぼ同じです。
プロジェクトのルートディレクトリ Lapp.rb Lconfig.ru LGemfile LRakefile Ldb Lmigrate L20141231050512_create_contents.rb Lblogposts.db Ldatabase.yml Lshema.rb Lmodels LdbAccesser.rb Lvendor Lbundle L... LViews Llayout.slim Lblog.slim Lcss Lstylesheet.sass Ljs Lscript.coffee
テーブルのPrimaryKeyを変更する
マイグレーションファイル(db/migrate/20141231050512_create_contents.rb)を使ってテーブルを作成すると、自動的にPrimaryKeyとして[id]というカラムが作成されます。
普段は別に問題ないのですが、後述するjoinsで複数のテーブル間で同じカラム名を使うときなど、名前を変更したい場合があります。
テーブル作成時に[id: false]としてやることで、[id]というカラムの作成をキャンセルできます。
20141231050512_create_contents.rb
class CreateContents < ActiveRecord::Migration def change create_table :posts, id: false do |tblpost| tblpost.string :post_id tblpost.primary_key :post_id tblpost.string :post_title tblpost.string :post tblpost.timestamps end create_table :tags, id: false do |tbltags| tbltags.string :tag_id tbltags.primary_key :tag_id tbltags.string :tag_name tbltags.timestamps end create_table :taglinks do |tbltaglinks| tbltaglinks.integer :post_id tbltaglinks.integer :tag_id end end end
- [taglinks]テーブルについては、カラム名が[id]であっても特に問題がないため変更はしていません。
テーブル連結
joinsを使用することでテーブルの連結ができます。ただし、models/dbAccesser.rbであらかじめDBの関係性を指定しておく必要があります。
DBの関係性を指定する
今回は以下の3つのテーブルを作成しています。 * posts --- ブログの記事やタイトルを保存する * tags --- ブログに設定するタグを保存する * taglinks --- ブログ記事にどのタグが結び付けられているかの情報を保存する
[posts]と[taglinks]、[tags]と[taglinks]はそれぞれ1対多の関係にあるので、その内容を記述します。
dbAccesser.rb
class Post < ActiveRecord::Base has_many :taglinks end class Tag < ActiveRecord::Base has_many :taglinks end class Taglink < ActiveRecord::Base belongs_to :post belongs_to :tag end
テーブルを連結する
例えば[tags]と[taglinks]を連携するには以下のようにすればOKです。
@aryTags = Tag.joins(:taglinks)
ただし、このコードで発行されるSQLは以下の通りで、[tags]にはなく[taglinks]だけが持つカラム[post_id(記事ID)]を使おうとするとエラーになります。
SELECT "tags".* FROM "tags" INNER JOIN "taglinks"
[post_id]を利用したい場合は、selectを使ってデータを取得するカラムを指定する必要があります。
@aryTags = Tag.joins(:taglinks).select(:tag_name, :post_id, :tag_id)
※各カラム名を「postid」のようにアンダースコア抜きで作成すると、SQL発行時にカラム名が「post_id」と指定されてしまい該当のカラムが見つからないとエラーになります。
※また、joinsで連携させるカラム名は、両方のテーブルで共通させる必要がありそうです
includes
テーブルを連結する方法として、joinsと並んでincludesが挙げられますが、その役割は異なっているようです。
今回やりたいことに近いと思えたのはjoinsだったため、そちらを使っています。
検索
whereを使ってデータを検索することができます。
aryPostId = Post.where(post_id: 1)
取得したデータはテーブルの各カラムが含まれているため、以下のようにしてそれぞれのデータを使用します。
aryPostId.each do |postId| puts "PostID:" + postId.post_id.to_s end
連結したテーブルのカラムを指定したい場合
テーブルを連結して、連結先のカラムを検索条件に指定したい場合は以下のようにします。
@aryPosts = Post.joins(:taglinks).where(taglinks: {tag_id: 1})
ちなみにselectやwhereをつける順番は決まっていないようで(見やすさの問題はあると思いますが)、どちらを先にしても同じSQLが発行されていました。
データの並びを指定する
joinsやwhereなど、発行されるSQLと見比べるとだんだんわかってくる気もしますが、データを昇順・降順で取得するにはorderを使用します。
ASCやDESCは文字列として指定できました。
@aryPosts = Post.all.order(post_id: 'desc')
取得するデータ数を指定する
limitを使って指定します。
@aryPosts = Post.all.limit(10)
テーブルのn行目からm行目までかを指定して取得する
@aryPosts = Post.all.offset(1)
データの数(テーブルの行数)をカウントする
intPostCount = Post.count
PostgreSQLについて
Gemfileの[pg]をインストールして、現在のSQLiteと同じようにPostgreSQLを使用しようとしたところ、以下のような問題が発生しました。 1. (bundle exec rackupで)サーバーを立ち上げていないとエラー 2. (サーバーを立ち上げたあと)/tmpにアクセス出来ない旨のエラー
ググったところ開発環境ではダメだったが本番環境では問題がない、という方もいるようなので、これについてはもう少しあとで考えてみたいと思います。
参考
ActiveRecord
- ActiveRecordの関連のすべて - StoneDot の Ruby on Rails 講座
- 似ているようで全然違う!?Activerecordにおけるincludesとjoinsの振る舞いまとめ - Qiita
- ActiveRecord4でこんなSQLクエリどう書くの? Arel編 - TIM Labs
- 俺のRails力が火を吹くぜ! RailsのActive Record発行方法もろもろ。その1。 - srockstyle
- order - リファレンス - Railsドキュメント