- 投稿日:2020-02-27T23:49:22+09:00
多対多テーブルの作り方
データベース設計
投稿(postテーブル)に対してタグ(tagテーブル)付けができる機能を作るとします
一つの投稿は複数のタグを持つことができ、一つのタグは複数のタグを持つことができる場合、そのままアソシエーションを組んでしまうとデータベースの中身が複雑になってしまいます。
そのためどの投稿がどのタグに結びついているのかを記憶するテーブルを別に作り、整理します。内容
postテーブルを作る
ターミナルrails g model Post image:texttagテーブルを作る
ターミナルrails g model Tag name:string中間テーブルを作る
中間テーブルは各モデルの複数形を名前をスネーク型で繋げます
今回の場合はPosts_tags
postとtagの結びつきを記憶するテーブルのため各idのカラムを作ります
refarencesの型はこの後記述するマイグレーションファイルに記載するforeighn_keyの制約に対応した型になります。
foreighn_keyはpost_idやtag_idカラムに対してpost_idに対応したpostsテーブルのidがあることを保証する制約です。
例)post_id:1 tag_id:1 であればpostsテーブルにid:1の投稿があり、tagsテーブルにもid:1があることを保証しています。ターミナルrails g model Posts_tags post_id:refaremces tag_id:refarencesアソシエーション
多対多テーブルのアソシエーションはthroughを使用します。
postとtagは関連しているがposts_tagsを経由して関連することを表します。ppst.rbhas_many :posts_tags has_many :tags, through: :posts_tagstag.rbhas_many :posts_tags has_many :posts, through: :posts_tagsposts_tag.rbbelongs_to :post belongs_to :tagマイグレーションファイル
マイグレーションファイルclass CreatePostsTags < ActiveRecord::Migration[5.2] def change create_table :posts_tags do |t| t.refarence :post_id, foreign_key: true t.refarences :tag_id, foreign_key: true t.timestamps end end end完成
これで多対多テーブルの作成が完了します。
※sqliteでrefarences型を使用しようとしたところマイグレーションができませんでした。integer型に変更したところマイグレーションできたので、sqliteではrefarences型は使用できないようです。
- 投稿日:2020-02-27T20:30:47+09:00
MySQL UNIQUE KEYの変更
概要
MySQLのUNIQUE KEYの変更がすんなりいかないということだけは記憶に残っていましたが、方法が記憶に残っていなかったので、備忘録的に記載します。
前提
以下のようなTableを想定します。
mysql> SHOW CREATE TABLE articles; CREATE TABLE `articles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `tab_id` int(10) unsigned NOT NULL, `articles` mediumtext NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_tab_id` (`tab_id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 mysql> ALTER TABLE articles ADD COLUMN experiment_sign varchar(64) AFTER tab_id; Query OK, 0 rows affected (0.27 sec) Records: 0 Duplicates: 0 Warnings: 0このarticles Tableにexperiment_sign Columnを追加したとします。
mysql> SHOW CREATE TABLE articles CREATE TABLE `articles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `tab_id` int(10) unsigned NOT NULL, `experiment_sign` varchar(64) NOT NULL, `articles` mediumtext NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_tab_id` (`tab_id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4experiment_sign Columnがvarchar(64)で追加されているのが確認できると思います。
そして、UNIQUE KEYがtab_idのみになっていますが、これにexperiment_signを追加して、下記のような変更を行いたいと仮定します。UNIQUE KEY `idx_tab_id (`tab_id`)↓
UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`)さあ、このときどうするかというのが、今回のお話です。
失敗例
何も考えず、とりあえずUNIQUE KEY追加すればええやろと、安易に考えると、下記のようなALTER文を書いてしまいます。私です。
ALTER TABLE articles ADD UNIQUE `idx_tab_id_experiment_sign (`tab_id`, `experiment_sign`)こうすると、どうなるかというと
CREATE TABLE `articles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `tab_id` int(10) unsigned NOT NULL, `experiment_sign` varchar(64) NOT NULL, `articles` mediumtext NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_tab_id` (`tab_id`) UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4とまあ、UNIQUE KEYが二つつきますね。はい。
これでは、わけわからんことになりますね。見た目も、無駄に誤解を招きます。正解例
ちゃんと既存UNIQUE KEYを潰しましょう。
mysql> ALTER TABLE articles ADD UNIQUE `idx_tab_id_experiment_sign (`tab_id`, `experiment_sign`); mysql> ALTER TABLE articles DELETE INDEX `idx_tab_id;こうすると、理想の形ができあがります。
CREATE TABLE `articles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `tab_id` int(10) unsigned NOT NULL, `experiment_sign` varchar(64) NOT NULL, `articles` mediumtext NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4終わり
ちゃんと、理想の形になっているか確認しましょう。以上です。
ああ、もちろん、UNIQUE KEYはNOT NULL制約をつけましょう。
- 投稿日:2020-02-27T16:26:14+09:00
dependent: :destroyをつけていても削除できなかった原因
長時間調べたが解決方法が見つからないという状態が続きました。
私のミスだったので、同じような状態の人に私の経験を伝えれればと思い書きます。内容
紐付けしたオブジェクトを削除するために、
dependent: :destroy
をつけても、削除できない時に気をつけて欲しいことを書きます。問題点
ある特定のUserのみ削除できない
モデルの関係性
User
とCart
が親子関係で、
Cart
とProduct
がCart_products
と 1対多 です。
Users ー Carts → Cart_products ← Products
class User < ApplicationRecord has_one :cart, dependent: :destroy end class Cart < ApplicationRecord belongs_to :user has_many :cart_products, dependent: :destroy has_many :products, through: :cart_products end class Product < ApplicationRecord has_many :cart_products, dependent: :destroy end class CartProduct < ApplicationRecord belongs_to :cart belongs_to :product end行っていたこと
関連付けられたオブジェクトもdestroy削除できるように
has_many
,has_one
にdependent: :destroy
をつけていた。エラー文
Cannot delete or update a parent row: a foreign key constraint fails (`app_name_development`.`cart_products`, CONSTRAINT `fk_rails_a4f3e327f3` FOREIGN KEY (`cart_id`) REFERENCES `carts` (`id`))原因
User has_one Cart
でUser
にはCart
がは1つのみ紐づくようにしていたのですが、
削除できないUser
には2つのCart
が紐つかれていた。そのため
User has_one Cart
をUser has_many Carts
と変更すると削除できた。+----+---------------------+---------------------+---------+ | id | created_at | updated_at | user_id | +----+---------------------+---------------------+---------+ | 3 | 2020-02-21 07:07:57 | 2020-02-21 07:07:57 | 4 | | 12 | 2020-02-21 07:29:37 | 2020-02-21 07:29:37 | 7 | | 13 | 2020-02-21 07:34:26 | 2020-02-21 07:34:26 | 7 | | 16 | 2020-02-22 07:51:47 | 2020-02-22 07:51:47 | 11 | +----+---------------------+---------------------+---------+今後の対処法
モデルを作成する時に、オプションとして
unique: true
を付け、user
の重複をなしにする。XXXXXXX_create_cart.rbclass CreateCarts < ActiveRecord::Migration[5.2] def change create_table :carts do |t| t.references :user, foreign_key: true, unique: true #ここ t.timestamps end end end結論
User has_one Cart
関係の
User
に1つのみのCart
が紐づくようにしていたのですが、
削除できないUser
には2つのCart
が紐つかれていた。そのため、以下のように
User has_one Cart
をUser has_many Carts
と変更すると削除できた。class User < ApplicationRecord has_one :cart, dependent: :destroy end ⬇︎ class User < ApplicationRecord has_many :cart, dependent: :destroy end
- 投稿日:2020-02-27T14:33:05+09:00
DockerでMySQLを立ち上げる時に文字コードを指定する
docker run --restart=always -d -e MYSQL_ROOT_PASSWORD=mysql \ -p 3306:3306 --name testdb mysql:8.0 --character-set-server=utf8mb4
- 投稿日:2020-02-27T11:11:46+09:00
【MySQL】自動起動の意外な落とし穴
はじめに
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)ログインする際のエラーを解決するためにサーバーを一旦停止したかった時のことです。
自動起動設定をしたことをすっかり忘れていて、時間がかかったのでまとめておきます。そもそもの自動設定は、どうやってた?
brewのhomebrew-servicesを使っていた。
公式サイトはこちら。
https://github.com/Homebrew/homebrew-services使い方
MySQLを自動起動にする。
$ brew services start mysql ==> Successfully started `mysql` (label: homebrew.mxcl.mysql)これだけ。
自動起動を止めたい時は、
$ brew services stop mysql Stopping `mysql`... (might take a while) ==> Successfully stopped `mysql` (label: homebrew.mxcl.mysql)一覧を取得したい。
$ brew services list Name Status User Plist mysql started ------ /Users/------/Library/LaunchAgents/homebrew.mxcl.mysql.plist postgresql started ------ /Users/------/Library/LaunchAgents/homebrew.mxcl.postgresql.plist自動起動がオンになっていると、停止コマンドは成功するが確認すると止まってない。。。
% mysql.server stop Shutting down MySQL . SUCCESS! % mysql.server status SUCCESS! MySQL running (21754)
パッと見「サクセス!!!」って完全に停止できてると信じていたけれど、騙されました。
いざコマンドで確認したところ、普通に動いてて、意味がわからなかった。。。
そして、自動起動について書かれてる記事をみた時に、ハッと思い出して、、、
今に至ります(笑)おわりに
便利なものをインストールして、効率を上げるのはいいことだけど、しっかり把握しておかないと今回のようなことになってしまうので注意します。