vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

SinatraとSQLiteと 2

大晦日ハッカソン、続く正月ハッカソンで、 前回作成したプロジェクトを元にブログサイトに挑戦してみました。

やっぱり時間や目標をある程度決めて開発するというのは良いもので、まだまだ課題は多いとはいえ、ブログ記事を表示する部分に関してはそれっぽくなった気がします。

今回はActiveRecordSQLiteのテーブル連結や検索などをメモ代わりに書いてみることにします。

ファイル構成

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

PrimaryKeyの指定

SQLite