とーますメモ

Ruby on Rails / Goなどの学習メモ

【Vue.js】v-forの配列を削除すると、最後の行が消される問題

v-forで配列内をループで回して、表示させており
その中で以下のように、削除を行う機能を実装したのだが
削除すると選択した行が消されず、最後の行が消されるという変な現象が発生した。

[HTML]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="index"
    v-on:remove="remove_item"
  ></tr>
</tbody>

[JS]

this.items.splice(index, 1);

原因を調べてみると、「:key」属性に「index」を設定していたのが原因だった。

In scenarios like this the most common problem is the v-for item keys. Your are keying each item by the index, but the index of each item is not constant because items can be removed from the list. Try using unique keys that are associated with item in the list.

by V-for and deleting components - Get Help - Vue Forum

なので設定する:keyをコンポーネントに属する唯一の値にすることで問題が解消された。
v-forのkeyにはindexを設定してはいけません!

[解決済みコード]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="item.id"
    v-on:remove="remove_item"
  ></tr>
</tbody>

【Vue.js】v-forの配列を削除すると、最後だけが消されていく問題

v-forで配列内をループで回して、表示させており
その中で以下のように、削除を行う機能を実装したのだが
削除すると選択した行が消されず、最後の行が消されるという変な現象が発生した。

[HTML]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="index"
    v-on:remove="remove_item"
  ></tr>
</tbody>

[JS]

this.items.splice(index, 1);

原因を調べてみると、「:key」属性に「index」を設定していたのが原因だった。

In scenarios like this the most common problem is the v-for item keys. Your are keying each item by the index, but the index of each item is not constant because items can be removed from the list. Try using unique keys that are associated with item in the list.

by V-for and deleting components - Get Help - Vue Forum

なので設定する:keyをコンポーネントに属する唯一の値にすることで問題が解消された。
v-forのkeyにはindexを設定してはいけません!

[解決済みコード]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="item.id"
    v-on:remove="remove_item"
  ></tr>
</tbody>

【MacOS:Catalina】"default interactive shell is now zsh" を非表示にする方法

とりあえずはbashを継続して使用したいのにターミナルを開くたびに、タイトルのメッセージが表示されるのは鬱陶しいので情報を探したところ以下の記事がヒット。

www.addictivetips.com

単純に以下を「.bash_profile」に書けばOK

export BASH_SILENCE_DEPRECATION_WARNING=1

またzshに変更したくなれば以下のコマンドを叩けばOK

chsh -s /bin/zsh

【Rails】migrationファイル実行時に同時にSQLを実行する方法

作成するmigrationファイルの処理は、1つのカラムを追加するというシンプルな処理のみを行う。
しかしdefault値は設定せず、全レコード上の作成されるカラムには、特定の値を埋め込みたいという要件があったため
migration実行時にカラムを追加後、その特定値を埋め込む処理も同時に入れたくなった。

以下のページが参考になった。
qiita.com

class ChangeTests < ActiveRecord::Migration
  def change
    add_column :sample_tables, :test_column, :string, :after => :previous_column

    begin
      ActiveRecord::Base.connection.execute("START TRANSACTION")

      ActiveRecord::Base.connection.execute("
        UPDATE
          sample_tables
        SET
          new_column = 'default_value'
        WHERE
          new_column IS NULL;
      ")

      ActiveRecord::Base.connection.execute("COMMIT")
    rescue => e
      ActiveRecord::Base.connection.execute("ROLLBACK")
      raise "Error: #{e.to_s}"
    end
  end
end

【MySQL】フランス語などのアクセントが正しく取得できない。

以下が参考になった。

mysql – selectクエリを使用してアクセント記号のない文字でカラムをフィルタリングする方法 - コードログ
utf 8 - How to conduct an Accent Sensitive search in MySql - Stack Overflow

以下のように「COLLATE utf8_bin」を付ければ良い。

WHERE col_name = 'abád' COLLATE utf8_bin

【Rails】ActiveRecordの属性メソッドを上書きする

使用しているWebアプリの1つに
Railsが管理しているDBではない、他のDBからのデータを取得する必要があった。

当たり前の話だが、Railsではapplication.rbのtime_zoneの設定のため
日付取得の際に自動で、タイムゾーン変換が行われる。

thoames.hatenadiary.jp

そのため、以下の状況になってしまった。

①Railsが管理しているDBの日付は、自動タイムゾーン変換をしてほしい。
②Rails外のDBの日付は、自動タイムゾーン変換をしてほしくない。(*このDBの日付はDATETIME型)

前提として、このWebアプリではconfig.time_zoneは指定しているが、config.active_record.default_timezoneは指定していない。
その理由は以下の記事で詳しく解説されている。
Rails と時刻 - @kyanny's blog

この対応策としてタイトルにもある通り
Rails外のDBテーブルを管理するActiveRecordモデルの属性メソッドを上書きする」という方法で対応することにした。

その方法としては以下の記事が役に立った。

ActiveRecordのattribute methodをオーバーライドするとき
Active Recordで属性にアクセスするメソッドを上書きする - 飲んだり寝たり

読み込み専用の「read_attribute」のaliasである[]を利用することで、属性メソッド呼び出しの際に発生してしまうループ状況を回避し上書きすることができるようになる。
Railsの設定で「config.active_record.default_timezone」は設定していないのでRailsはDBの日付をUTCとして解釈し保存する。
これで元の日付データを読み込んだ後(この時点で日付は既にタイムゾーン変換が行われている)、UTCに戻してあげれば変換前の日付を取得できる。

UTCタイムゾーンの日付に変更する方法としては以下の方法があるが、それぞれ違いがある
1)to_s(:db)・・・返り値がString型 ex) self[:xxx_date].to_s(:db)
2)utc・・・返り値がTime型 ex) self[:xxx_date].utc
3)in_time_zone('UTC')・・・返り値がActiveSupport::TimeWithZone型 ex) self[:xxx_date].in_time_zone('UTC')

1)の方法を使用した場合、String型として返されるので、後ほど「strftime("%d/%m/%Y")」などを使用しフォーマットを変更したい場合などに
融通が効かないので不採用。問題は2)か3)なのだが、Time型とTimeWithZone型は互換性があるため、どちらにするか迷ったが
この記事(RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い - Qiita)でも紹介されている通り、自分も極力TimeWithZone型を使用する方針で
行きたいのでここは3)を使用することにした。
※3)は内部で2)を行い、TimeWithZone型にラップしているっぽい。コードの詳細は以下。
rails/zones.rb at 98a57aa5f610bc66af31af409c72173cdeeb3c9e · rails/rails · GitHub


そして最終的に作成したコードは以下。このコードではxxx_dateがActiveRecordの属性メソッド。

class Hoge < ActiveRecord::Base

  establish_connection "xxxxxx".to_sym
  self.table_name = 'yyyyyy'

  def xxx_date
    self[:xxx_date].in_time_zone('UTC')
  end
end

長かった...

[参考]
TimeとTimeWithZoneの違いについて - 研鑽の日々
ruby - Rails 4 - in_time_zone unexpected behavior - Stack Overflow
When using time zones, beginning_of_day / end_of_day is broken in Rails 2 for any Date or DateTime - makandra dev

【Rails】難しすぎる!? Timezone(タイムゾーン)についてのメモ

自分用メモ。

大事なことは全て以下のページにあった。
qiita.com
qiita.com

上記の内容から重要なポイントを抜き出すと以下の通り。

1)タイムゾーンの設定

システムまたは環境変数のタイムゾーンとapplication.rb(time_zone)が同じならば、
Time.nowでもTime.current(Time.zone.now)でも同じ。

2)Rails経由でDBに保存されるtimeについて

[config.active_record.default_timezon]を[:local]に設定していなければ、RailsはMySQLのタイムゾーンを無視して時間をUTCで保存する。

3)Datetime.newはUTCタイムゾーンを返す。

parse や new では、環境変数のタイムゾーンでもapplication.rbのタイムゾーンでもなく、UTCのタイムゾーンになる点も注意が必要です。(UTCからのオフセットを指定しない場合)

RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い - Qiita

4)MySQLのDateTime型はタイムゾーンを情報を持っていない。

Railsと周辺のTimeZone設定を整理する (active_record.default_timezoneの罠) - Qiita

5)MySQLのTIMESTAMP型は厄介?

TIMESTAMP型は書き込む時にサーバのタイムゾーン設定を確認してUTCに変換してからデータを保存する。取得する時はUTCからサーバのタイムゾーンに変換してから表示する。
なので、記録時と異なるタイムゾーンに変更すると取得した時の時間がズレてしまう。

by Railsと周辺のTimeZone設定を整理する (active_record.default_timezoneの罠) - Qiita

またTIMESTAMP型は「2038年問題」などの問題もあるため
将来的なことを考えた場合、時間を保持する型としては「Datetime型」一択だと思う。(個人的見解)
Railsのcreated_atやupdated_atもDatetime型だし、migrationにtimestampを選択できるけど、rake db:migrateしたらDatetime型になってるし。

他にも「CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP」のせいで、レコードが更新されると勝手に日付が更新されてしまうといった仕様ですし。

MySQLの timestamp型が、なかなか厄介。
MySQL TIMESTAMP 2つ以上のCURRENT_TIMESTAMP - Oboe吹きプログラマの黙示録

結論

上記の記事で言ってることと同じだが
Time.nowやDateTime.nowなどの環境タイムゾーンを使用している関数を使うよりも
Time.currentなどのapplication.rbで使用しているタイムゾーンを元にした関数を使うほうが良い。

備考

DateTime型のカラムに対して、Date型で検索をかけるときは十分注意すること。

リファレンスマニュアルに書かれているとおりで、最近のMySQLではDateTime型のカラムに対してDate型で検索をしようとすると自動的に '00:00:00' が付与されてDateTime型として計算されます。

by MySQLでDateTime型のカラムをDate型で検索するときに気をつけること - (゚∀゚)o彡 sasata299's blog