tapitapi’s blog

1日1杯タピオカ!エンジニア

【Nuxt.js】 Metaタグを静的に埋め込む

ページごとに違ったMetaタグを設定するため、下記の記事を参考に設定しました

qiita.com

 

ここで問題発生!generateで作成したdist内のファイルをサーバにおいたところ、

 

ディベロッパーツールで確認

<title>各ページのscript内で指定したtitle</title>
<meta data-n-head="ssr" data-hid="description" name="description" content="各ページのscript内で指定したdescription">


ページのソースを表示

<title>configで設定したデフォルトtitle</title>

<meta data-n-head="ssr" data-hid="description" name="description" content="configで設定したデフォルトdescription">

 

つまり、Nuxtがメタタグも動的に設定してしまっていることが判明。

そのため、ページをSNSなどで共有したりすると、デフォルトのタイトルやdescriptionが表示されてしまう、、、

 

distの中の各ページhtmlをみてみると、案の定全てのページで下記のようになっていました。

<title>configで設定したデフォルトtitle</title>

<meta data-n-head="ssr" data-hid="description" name="description" content="configで設定したデフォルトdescription">

 

じゃあ、generateした後、各ページのhtmlのメタタグを書き換えればいいのでは?!

->上手くいきました!!!

 

generateするたびに手動で1ページ1ページ書き換えるのもいいけど、自動化しちゃえ!ってことで、Pythonでコード書いたので、よかったら使ってください

 

まず、ページごとのtitleをまとめて管理するdeploy.yaml

path:
  source: "dist"
seo:
  root:
    title: "このページはホームです"
    description: "ホームの説明です"
  page1:
    title: "ページ1"
    description: "ページ1の説明です"
  page2:
    title: "ページ2"
    description: "ページ2の説明です"
  page2/page3:
    title: "ネストしたらこういう感じで書いてね"
    description: "ページ3の説明です"

 

deploy.py

import yaml
import sys
import os
import re

# judge check if seo exists in yaml
 def __judgeSeoParam(yml:str):
  if not yml.get('seo'):
   print('ERROR!!! seo param does not exist in deploy.yaml.')
   sys.exit()

 

def __rewriteSeo(filename: str, title:str, description: str):
 if os.path.exists(filename):
  with open(filename, encoding='utf-8') as html:
   data_lines = html.read()
   data_lines = re.sub(r'(?P<top>\<title\>).+?(?P<bottom>\<\/title\>)', r'\g<top>%s\g<bottom>' % title, data_lines)
   data_lines = re.sub(r'(?P<top>data\-hid\=\"description\" name\=\"description\" content=\").+?(?P<bottom>\"\>)', r'\g<top>%s\g<bottom>' % description, data_lines)

  with open(filename, mode="w", encoding='utf-8') as html:
   html.write(data_lines)
 else:
   print('ERROR!!! ' + filename + '.html does not exist.')
   sys.exit()

 

def deploy():
 print('************ start rewrite ************')

 with open('deploy.yml') as file:
   yml = yaml.load(file, Loader=yaml.BaseLoader)
   __judgeSeoParam(yml)
  for page in yml['seo']:
   filename = yml['path']['source'] + '/index.html'
   if page != 'root':
    filename = yml['path']['source'] + '/' + page + '/index.html'
   __rewriteSeo(filename, yml['seo'][page]['title'], yml['seo'][page]['description'])
 print('************ end rewrite ************')


if __name__ == "__main__":
  deploy()

 

以上ですー!

下記にコードをアップしてるので、ダウンロードもできます

github.com

 

明日は金曜日!

おやすみなさい

 

【python】 dict型でKeyの存在チェックにgetを使う

dict型でKeyの存在チェックにinを使っていたのですが、getが便利!ってことに気づいたので紹介します

 

データA

{"修学旅行先": ”長野”, "先生": [山田,高橋], "注意事項": ”登山靴を持ってくる”}

 

データB

{"修学旅行先": ”京都”, "先生": [小山], "注意事項": ””}

 

データC

{"修学旅行先": ”北海道”, "先生": [細谷]}

 

上記のようなdict型データがあった場合、"注意事項"のKeyの値があるかどうかを調べる時、今までは下記を使っていました。

 

if  "注意事項" in data and len(data["注意事項"]) > 0:

    print(data["注意事項"])

または

if "注意事項" in data and data["注意事項"] != "":

   print(data["注意事項"])

 

でも、getを使えば、andを使わなくてOK

if data.get("注意事項"):

   print(data["注意事項"])

 

これは、データBのように、Key”注意事項”の値が空文字なら、

 data.get("注意事項")は空文字が返ってくる

データCのように、Key”注意事項”が存在しなければ、

data.get("注意事項")はNoneが返ってくる

 

PythonのIf文は空文字もNoneも、falseとして扱ってくれるので、getを使うとスッキリ書けるよ!という話でした。

 

おやすみなさい

 

【Nuxt.js】 画像はassetsに置く?staticに置く?

Nuxt.jsで画像を置く時に使えるフローチャート作りました。

f:id:kayo445:20200913190305p:plain

 

なんで?って思った方は、下記を読んでみてください^^

 

まず、staticとassetsに置いた場合の違いについて。

Nuxtはフォルダ構成が下記のようになっており、画像ファイルはassetsフォルダか、staticフォルダに置くことができます。

各フォルダについて詳細はこちら

f:id:kayo445:20200913190718p:plain

*assetsに置いた場合、

下記のように画像を指定することで、assets/image.png/を参照することができます。

<img src="@/assets/image.png" />

 

npm run generateなどでコンパイルしたファイル(.dist)をみてみると、下記のようにコンパイルされたindex.htmlまたはindex.xxxx.js内に、コンパイルされたimage.pngが参照されます

<img src="/_nuxt/image.437d4c0.png">

 

*staticに置いた場合、

下記のように画像を指定することで、static/image.pngを参照することができます。

<img src="image.png" />

 

npm run generateなどでコンパイルしたファイル(.dist)をみてみると、下記のようにコンパイルされたindex.htmlまたはindex.xxxx.js内に、そのままimage.pngが埋め込まれます

<img src="image.png">

 

なので、フローチャートに記載した通り、普通に文字列でsrcを指定する場合は、assetsに置くべきです(コンパイルされた画像を読み込むため、読み込みが早い)

 

次に、文字列ではなく、変数でsrcを指定したい場合があると思います。

例えば、下記のようなセレクトボックスで選択した国の国旗を表示する場合を考えてみます。

f:id:kayo445:20200913204154p:plain

 

*国旗の画像をassetsに置いた場合、 

下記のように画像を指定することで、assets/australia.pngなどの国別画像を参照することができます。(srcではなく:srcにすることで、変数やメソッドを埋め込める。下記countryは変数)

 

 

<img :src="require('@/assets/' + country + '.png')" />

 

 

ここで注意なのが、assetsの場合はrequireで画像を指定するため、もし、指定した画像がassetsフォルダ内に存在しない場合はコンパイルエラーとなってしまいます。

つまり、存在しない画像をrequireしているコードが入っているvueのページが丸ごと表示されなくなってしまいます。

 

なので、フローチャートに記載した通り、変数で指定した画像が必ず存在する場合のみ、assetsに入った画像を変数で指定してOK

 

*国旗の画像をstaticに置いた場合、

下記のように画像を指定することで、static/australia.pngなどの国別画像を参照することができます。

 

<img :src="country + '.png'" />

 

この場合はもし指定した名前の画像がstatic内になくても、ブラウザ上で404エラーが出るだけでコンパイルエラーにはなりません。

 

下記のようにすることで、画像がない場合にno-image.pngを代わりに表示させることができます(※no-image.pngがない場合は無限ループになるので注意)

<img @error="showNoImage()" :src="country + '.png'" >

 

下記scriptのメソッド内

showNoImage(){
event.target.src = require('@/assets/no-image.png');
}

 

なので、フローチャートに記載した通り、変数で指定した画像が存在しない可能性がある場合、static内の画像を変数で指定する

 

※ただし、Nuxtで@error (onerror)は、ローカル環境では上手く動作しない場合があります。ステージングのサーバなどで動作確認することをお勧めします

以上ですーーー!

 

コンパイルに関して、webpackについてもっと深く知りたいな!と思ったので、今後記事で取り扱えればと思います。

 

最近涼しくなってきました。お体に気をつけて。おやすみなさい

 

【Ruby on Rails】Includesを使おう!N+1問題対策

Ruby on RailsでIncludesを使おう!という紹介。

Includesを使用することで、データベースとのやりとりを少なくすることができます

 

今回は、お店のオーナーを管理するテーブル(Owner)と

お店を管理するテーブル(Shop)があり、

オーナーとお店をDBから取ってくる場合を想定してみます

 

Owner table(ジョンさんとケンさんとアリスさん)

id:100, name: ジョン

id:101, name: ケン

id:105, name: アリス

 

Shop table(ジョンさんはマック、ケンさんはケンタ、アリスさんはスタバのオーナー)

id:1, name: マクドナルド, owner_id:100

id:2, name: ケンタッキー, owner_id:101

id:3, name: スターバックス, owner_id:105

 

Includeを使わずにオーナーとお店を取ってくるコード

@owners = Owner.all
@owners.each do |owner|
  shop = owner.shops
  p shop

end

 

これで、データベースクエリは4回実行される

SELECT "owners".* FROM "owners"

// オーナー全員が検索される

SELECT "shops".* FROM "shops" WHERE "shops"."user_id" = 100

// id:1, name: マクドナルド, owner_id:100 が検索される

SELECT "shops".* FROM "shops" WHERE "shops"."user_id" = 101

// id:2, name: ケンタッキー, owner_id:101が検索される

SELECT "shops".* FROM "shops" WHERE "shops"."user_id" = 105

// id:3, name: スターバックス, owner_id:105が検索される

 

オーナーがN人いれば、N+1回のクエリが実行されてしまう、、、!!!

これがN+1問題と言われ、検索に時間がかかってしまう原因となってしまいます。

 

ここで、Includesを使ったコードを見てみます。

@owners = Owner.includes(:shops).all
@owners.each do |owner|
 p owner.shops
end

どんなにオーナーが増えても、データベースクエリは2回実行で済みます

SELECT "users".* FROM "users"

// オーナー全員が検索される

SELECT "shops".* FROM "shops" WHERE "shops"."user_id" IN (100, 101, 105)

// オーナーに紐づいたお店全てが検索される

 

@owners = Owner.includes(:shops).where("id > ?", 101)

の様に、オーナーを選別してから、それに紐づくお店を検索することもできます

 

以上ですー!!

 

今後もIndexについてなど、DB関連のTipsについてもシェアできればと思います^^

 

おやすみなさい

【AWS DynamoDB】Search item (query) をPythonで行う

今回はPythonでテーブルデータを検索する方法をご紹介します。

(テーブル削除についてはこちら

(テーブル作成についてはこちら

 (テーブルにデータ挿入についてはこちら

 

リファレンス

boto3.amazonaws.com

 

まず、AWS CLIを使用できるようにする

tapitapi.hatenadiary.com

 

boto3を使用できるように、pip install

pip install boto3

 

テーブル作成

(下記では例として、「table2」というtableを作成します。カラムは「column1」「column2」「column3」, インデックスは「column1」と「column3」のインデックスを一つ作成します)

tapitapi.hatenadiary.com

 

テーブルにデータ挿入

tapitapi.hatenadiary.com

 

挿入後のテーブル(table2)の状態は下記のようになります

f:id:kayo445:20200721192008p:plain

 

 

query_items.py作成

import boto3

 

if __name__ == "__main__":

 session = boto3.Session(profile_name='default')
 client = session.client('dynamodb', region_name='us-east-2など、地域名')

 

   result = [] // 結果格納用配列

   response = client.query(
      TableName="Table2",
      KeyConditionExpression="column1 = :column1 and column2 = :column2",
      ExpressionAttributeValues={
           ":column1":{"S":"Data3"},
           ":column3":{"S":"BB"},
           ":column2":{"S":"Data4"}
      },
      FilterExpression="column3 = :column3",
      ProjectionExpression="column3, column7"
  )


  result.extend(response) // 結果をresultに格納

 

  if "LastEvaluatedKey" in response:
    nextKey = response["LastEvaluatedKey"]
    while nextKey:
           query_result = client.query(
             TableName="Table2",
             KeyConditionExpression="column1 = :column1 and column2 = :column2",
             ExpressionAttributeValues={
                          ":column1":{"S":"Data3"},
                          ":column3":{"S":"BB"},
                          ":column2":{"S":"Data4"}
             },
             FilterExpression="column3 = :column3",
             ProjectionExpression="column3, column7",
             ExclusiveStartKey=nextKey
             )
             result.extend(query_result) // 結果をresultに格納


             if not "LastEvaluatedKey" in query_result:
                break
             else:
               nextKey = query_result["LastEvaluatedKey"]
lg.info(result)

 

 

結果:

'Items':

[{'column3': {'S': 'BB'}, 'column7': {'L': [{'S': 'Cookies'}, {'S': 'Coffee'}, {'N': '3.14159'}]}}],

'Count': 1,

'ScannedCount': 1,

'ResponseMetadata': {'RequestId': 'XXX', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'Server', 'date': 'Tue, 28 Jul 2020 10:29:48 GMT', 'content-type': 'application/x-amz-json-1.0', 'content-length': '126', 'connection': 'keep-alive', 'x-amzn-requestid': 'xxx', 'x-amz-crc32': '648438377'}, 'RetryAttempts': 0}}

 

解説:

【検索結果Key】

Items:

検索でヒットしたデータが格納される

 

LastEvaluatedKey:

一度の検索で返ってくる結果は1MBまで。今回の検索ではこのKeyは出現しないが、検索結果が1MBを超えると、ここに、次の検索を始める最初のKeyが格納される

 

【Queryに指定するパラメタ】

TableName:

検索したいテーブル名を指定。

今回はTable2テーブルに対して検索

 

KeyConditionExpression:

テーブルを作成時にKeySchemaに指定したカラムに対するQueryを記載。

今回はcolumn1カラムの値がData3、かつ、column2カラムの値がData4のデータを検索。:column1と:column2は変数になっており、ExpressionAttributeValuesで変数の定義を行う

 

ExpressionAttributeValues:

検索用に使用する変数(値用)を定義。

今回は、:column1=Data3(文字列)、:column3=BB(文字列)、:column2=Data4(文字列)の三つの変数を定義。

 

FilterExpression:

KeySchemaに指定したカラム以外に対するQueryを記載。

今回は、KeyConditionExpressionで検索されたデータの中から、column3カラムの値がBBのデータを検索。

 

ProjectionExpression:

検索結果で必要なカラムを指定。指定しない場合は、全てのカラムが検索結果として出てくる。

今回はcolumn3とcolumn7を指定。

 

ExclusiveStartKey:

前回の検索でLastEvaluatedKeyが返された場合、ここにLastEvaluatedKeyの値をセットすると、続きの検索ができる

 

以上ですー!!

 

下記レポジトリでデータを入れるツール(dynamoDB/search_dynamodb_tables_json.py)を作成しましたので、使ってみてください^^

github.com

 

今後もDynamoDBに限らず、 LambdaやAPI Gatewayなど他のAWSサービス用ツールも作成していく予定です。

プルリクなどもいただけると嬉しいです!

 

おやすみなさい。

SCPコマンドで、ローカルのフォルダをXserverにコピー

ローカルのファイルをフォルダごとXserver上にコピーしたいな、、いちいち管理画面で操作したくないし、、

 

ということで、SCPコマンドを使ってローカルフォルダをXserver上にコピーする方法を紹介します

 

1.エックスサーバーでsshの設定

下記を参考に、秘密鍵(今回はexample.keyとします)をダウンロード

www.xserver.ne.jp

 

2. 秘密鍵配置と権限変更

// .sshフォルダがまだない場合は作成

$ mkdir ~/.ssh

 

// 1でダウンロードしたKeyを.sshフォルダへ移動

$ mv ~/Downloads/example.key ~/.ssh/

 

// .sshフォルダへ移動

$ cd ~/.ssh/

 

// Keyの名前を変更

$ mv example.key xserver_ssh_key

 

//  権限を変更

$ chmod 700 ~/.ssh

$ chmod 600 ~/.ssh/xserver_ssh_key

  

 

3. サーバへSSH接続ができるか試す

$ ssh -l サーバーID -i  ~/.ssh/xserver_ssh_key サーバーID.xsrv.jp -p 10022

 

ログインできたらexitコマンドで、ローカルに戻る

 

4. ローカルのフォルダをXserverへコピー

scp -i ~/.ssh/xserver_ssh_key -P 10022 -C -r コピーしたいローカルフォルダパス ユーザ名@サーバーID.xsrv.jp:/home/ユーザ名/サーバーID.xsrv.jp/public_html/

 

*-P は必ず大文字で(-p はNG)

*-C  でフォルダが圧縮されます

*フォルダではなくファイルをコピーしたい場合は、-rオプションなしで直接ローカルファイルパスを記載すればOK

 

以上ですー!!

 

-P でポート10022を指定しないとXserverにコピーできなかったので、(Pオプションないと、ポート22で接続しにいってしまい失敗する)ここでハマってしまって時間がかかってしまいました、、、

 

おやすみなさい

 

【AWS DynamoDB】Put itemをPythonで行う

作ったDynamoDBのテーブルに大量にデータを入れたい、でもGUIで手で入れるのはめんどくさい、、、って時ありますよね

 

今回はPythonでテーブルにデータを入れる方法をご紹介します。

(テーブル削除についてはこちら

(テーブル作成についてはこちら

 

リファレンス

boto3.amazonaws.com

 

まず、AWS CLIを使用できるようにする

tapitapi.hatenadiary.com

 

boto3を使用できるように、pip install

pip install boto3

 

テーブル作成

(下記では例として、「table2」というtableを作成します。カラムは「column1」「column2」「column3」, インデックスは「column1」と「column3」のインデックスを一つ作成します)

tapitapi.hatenadiary.com

 

put_items.py作成

import boto3

 

if __name__ == "__main__":

 session = boto3.Session(profile_name='default')
 client = session.client('dynamodb', region_name='us-east-2など、地域名')

   items =[
   {
    "column1": {"S": "Data1"},
    "column2": {"S": "Data2"},
    "column3": {"S": "AA"},
    "column7": {"L": [ {"S": "Cookies"} , {"S": "Coffee"}, {"N": "3.14159"}]}
  },
  {
    "column1": {"S": "Data3"},
    "column2": {"S": "Data4"},
    "column3": {"S": "BB"},
    "column7": {"L": [ {"S": "Cookies"} , {"S": "Coffee"}, {"N": "3.14159"}]}
  },
  {
    "column1": {"S": "Data5"},
    "column2": {"S": "Data6"},
    "column3": {"S": "DD"},
    "column7": {"L": [ {"S": "Cookies"} , {"S": "Coffee"}, {"N": "3.14159"}]}
  }
  ]

  for item in items:
    result = client.put_item(
     TableName="Table2",
     Item=item
   )

   print(result)

)

 

"column1": {"S": "Data5"}の”S”などはデータの型指定のために必要です。

”S”はString型を指定しています。

他の型については下記を参考にしてみてください

boto3.amazonaws.com

 

put_items.pyを実行後にGUIツールでみてみると、以下のようにデータが入っていることがわかります。

f:id:kayo445:20200721192008p:plain

 

以上ですー!!

 

下記レポジトリでデータを入れるツール(dynamoDB/put_items_dynamodb_tables_json.py)を作成しましたので、使ってみてください^^

github.com

 

今後もDynamoDBに限らず、 LambdaやAPI Gatewayなど他のAWSサービス用ツールも作成していく予定です。

プルリクなどもいただけると嬉しいです!

 

おやすみなさい。