Study & Practice

北海道札幌市のプログラマによる技術とか雑記のブログ

AWS CLIでS3バケットを作ろうとしたら「The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.」を言われた時の対処法

CodeDeployのためにS3バケットを作るために

aws s3api create-bucket --bucket バケット名

というコマンドを実行すると

An error occurred (IllegalLocationConstraintException) when calling the CreateBucket operation: The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.

というエラーが返ってきた。

リージョンが指定されていないという内容のようなので以下のようにリージョンを指定してコマンドを実行したが同じエラーが出てきた。

aws s3api create-bucket --bucket バケット名 --region ap-northeast-1

公式ドキュメントのExamplesにも

aws s3api create-bucket --bucket my-bucket --region us-east-1

というコマンドが記載されているが動かなかった

と思ったらもう少し下にus-east-1以外のリージョンを使いたいならLocationConstraintっていうのを使えと書いてあった

LocationConstraintは--create-bucket-configurationというオプションで指定する項目でリージョンにap-northeast-1を指定する場合は以下のような使い方になる

aws s3api create-bucket --bucket バケット名 --region ap-northeast-1 --create-bucket-configuration LocationConstraint=ap-northeast-1

ちなみにこのコマンドだとリージョン2回指定してるじゃんと思って以下のようなコマンドを実行してみたところ正常にバケットが作られた。--regionオプションは使わなくても大丈夫みたい

aws s3api create-bucket --bucket バケット名 --create-bucket-configuration LocationConstraint=ap-northeast-1

ただ公式ドキュメントには--regionもLocationConstraintも両方指定するコマンドが載ってるしどっかで不具合が起きるかもしれないのでちゃんと両方指定しておくのが安全かなと思います。

WSLで使用する既定のディストリビューションを切り替える方法

コマンドプロンプトからwslコマンドで入れるLinuxディストリビューションを切り替えたいときに使える方法です。

打つのが面倒でなければ

wsl -d Ubuntu-18.04

のように

wsl -d ディストリビューション名

ディストリビューションを指定することができます。

既定に設定したい場合は

wsl -s Ubuntu-18.04

のように-sオプションを付けることで既定のディストリビューションに設定することができます。

Laravelのクエリビルダ基礎(SELECT編)

はじめに

Laravelでクエリビルダを使ったクエリを実行するコードを書くたびにドキュメントを見ていたので覚えるためにもいったん基本的な使い方をまとめて書いてみることにしました。

今回の内容はすべて
readouble.com
に含まれています。

テーブルはLaravelのデフォルトの認証機能を使用したときのusersテーブルをベースに以下のようなテーブルを想定しています。

カラム名 データ型
id bigint(20) unsigned
name varchar(20)
email varchar(20)

本記事で利用するコードはすべてIlluminate\Support\Facades\DBクラスを使用するため、以下のようにインポート文を追加する必要があります。

use Illuminate\Support\Facades\DB;

クエリビルダの使い方

まずは基本的なSELECTからです

$users = DB::table('users')->get();

このコードは

select * from users;

と同等のクエリが実行されます。これにWhere句を追加したい場合は

$users = DB::table('users')->where('name', '=', 'carametal')->get();

となります。ただ、where句のオペレーターが上記のように「=」の場合は第2引数を省略することができ

$users = DB::table('users')->where('name', 'carametal')->get();

でも同様に動作します。

Where句をid(primary key)で絞る場合は以下のようにfindメソッドが使えます

$user = DB::table('users')->find(1);

getメソッドは結果が1レコードであっても配列を取得するのに対してfindメソッドはオブジェクトを1つだけ取得します。

primary key以外でレコードを絞り込みたいがfindメソッドのようにオブジェクト1つだけを取得したい場合はfirstメソッドを使うことができます

$users = DB::table('users')->where('name', 'carametal')->first();

もしfirstメソッドを使用したのにSQLの結果が複数行あった場合は内部的に一番最初に見つかったレコードのみを返します。今回のテーブルでいうとidが小さいレコードが優先されます。

where句で条件を複数指定したい場合は配列で条件を渡すこともできます。

$users = DB::table('users')->where([['id', 1], ['name', 'carametal']])->get();

where句を複数していすることもできます。

$users = DB::table('users')->where('id', 1)->where('name', 'carametal')->get();

条件指定にorを使いたい場合はorWhereメソッドが用意されています

$users = DB::table('users')->where('id', 1)->orWhere('name', 'carametal')->get();

またSQLの結果を集計したい場合にはcountメソッドやmaxメソッドを使うこともできます。

$count = DB::table('users')->count();

join句はメソッドで用意されています

$user = DB::table('users')->join('contacts', 'user.id', '=', 'contacts.user_id')->select('users.*', 'contacs.phone')->get();

join句もwhere句と同様にオペレータを省略することができます。

$user = DB::table('users')->join('contacts', 'user.id', 'contacts.user_id')->select('users.*', 'contacs.phone')->get();

joinメソッドはinner join、left outer joinはleftJoinメソッド、right outer joinはrightJoinメソッドがそれぞれ用意されているので必要に応じて使い分けることができます。

orderBy句もメソッドで用意されています。

$users = DB::table('users')->orderBy('id', 'desc')->get();

第一引数にカラム名、第二引数に昇順か降順かを指定します。第二引数は省略可能で省略した場合は昇順(asc)になります

またtimestampを採用している場合はlatestメソッド()、oldestメソッドを使ってcreated_atの順にソートすることもできます。

$users = DB::table('users')->latest()->get();

これで基礎的なSELECT実行するクエリビルダの使い方はまとまったかと思います。

このほかにもサブクエリなど応用的なメソッドもたくさんあるので、もっと深めたい方は是非公式ドキュメントをご覧ください。
readouble.com

Laravelのroute関数にパラメータを渡したいとき

例えばweb.phpに以下のようなrouteを設定した場合

Route::get('/hello-world', function () {
    return 'Hello,World.;
})->name('hello-world');

Laravel内で「/hello-world」というURLにhelloWorldという名前が付けられ

route('hello-world');

という書き方で「/hello-world」のURLが取得できるようになる

この場合でURLパラメータを加えた場合、つまり

Route::get('/hello-world/{name}', function ($name) {
    return "Hello,$name.";
})->name('hello-world');

のようにrouteを設定した場合、routeの呼び出しが側では以下のような書き方になる

route('/hello-world', ['name' => 'carametal']);

route関数の第2引数に連想配列を設定することでurlにパラメータを含めることができる

ReactRouter~子コンポーネントでthis.porps.history.push()がTypeError: Cannot read property 'push' of undefinedになる

そこそこハマったけど意外と情報が出てこなかったので記録

react等のバージョンは以下(package.json)

{
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.2.0",
    "react-scripts": "3.4.3"
}

今回発生したパターンの発生条件としては子コンポーネントにpropsを渡すためにcomponentではなくrenderを使ったことで、解決策としては子コンポーネントのexport時にwithRouterを使うことでした

以下の手順でサンプルの環境を作りました

npm create-react-app trial-router
cd trial-router
npm install react-router-dom

src/App.jsを以下の内容に更新

import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import Base from './BaseComponent';
import Moved from './MovedComponent';

function App() {
  return (
    <div className="App">
      <h1>HOME</h1>
      <BrowserRouter>
        <Route
          exact path="/"
          render={() =>
            <Base text={"Base"} />
          }
        />
        <Route
          path="/moved"
          render={() =>
            <Moved text={"Moved"} />
          }
        />
      </BrowserRouter>
    </div>
  );
}
export default App;

src/ChildComponent.jsを以下の内容で作成

import React from 'react';

class Base extends React.Component {
  move = () => {
    this.props.history.push("/moved");
  }
  render() {
    return (
      <>
        <div>{this.props.text}</div>
        <button onClick={this.move}>
          Move
        </button>
      </>
    );
  }
}
export default Base);

src/MovedComponent.jsを以下の内容で作成

import React from 'react';
class Moved extends React.Component {
  move = () => {
    this.props.history.push("/");
  }
  render() {
    return (
      <>
        <div>{this.props.text}</div>
        <button onClick={this.move}>
          Back
        </button>
      </>
    );
  };
}

export default Moved;

ファイルを作成して

npm start

すると以下のようなページができるはず
f:id:carametal:20201008153129p:plain
このページでMoveボタンを押すと
f:id:carametal:20201008153217p:plain
みごと「TypeError: Cannot read property 'push' of undefined」が発生する

解決策としてはChildComponent.jsとMovedComponent.jsをそれぞれ以下のように変更する
v

// ChildComponent.js
import React from 'react';
// withRouterをimport
import { withRouter } from 'react-router-dom';

class Base extends React.Component {
  move = () => {
    this.props.history.push("/moved");
  }
  render() {
    return (
      <>
        <div>{this.props.text}</div>
        <button onClick={this.move}>
          Move
        </button>
      </>
    );
  }
}
// withRouterの引数にBaseを指定
export default withRouter(Base)
// MovedComponent.js
import React from 'react';
// withRouterをimport
import { withRouter } from 'react-router-dom';

class Moved extends React.Component {
  move = () => {
    this.props.history.push("/");
  }
  render() {
    return (
      <>
        <div>{this.props.text}</div>
        <button onClick={this.move}>
          Back
        </button>
      </>
    );
  };
}
// withRouterの引数にMovedを指定
export default withRouter(Moved);

2ファイルとも修正は同じ

withRouterを追加したらボタンを押すことでChildとMovedを行き来できるはず

ちなみにクラス構文を使わずにReact Hooksを使っても同様のエラーが発生した。

コンポーネントにpropsを渡しつつhistory.push()するときはwithRouterは必須みたいですね

ASP.NET Core MVCにPostgreSQLを導入する

本記事はASP.NET Core は3.1、PostgreSQLは12.0で行っています
ASP.NET CoreでPostgreSQLを使えるようにするまでなのでPostgreSQLのインストールなどは割愛してあります。

こちらのチュートリアルをベースに進めていきます
docs.microsoft.com

ASP.NET Core 3.0に更新してないと書いてあるのでこちらの内容も織り交ぜてあります。
docs.microsoft.com

プロジェクトの作成

まずはVisual Stadioで新しいプロジェクトの作成→ASP.NET Core Webアプリケーションと選択します。
プロジェクト名はPostgreSQLTrialとしました。

フレームワークが「.NET Core」バージョンが「ASP.NET Core 3.1」になっていることを確認し、Webアプリケーション(モデルビューコントローラー)を選択します。

作成ボタンを押せばプロジェクトが作成されます。

パーケージのインストール

Entity Framework CoreとEntity Framework CoreでPostgreSQLを扱うためのパッケージをインストールします。
パッケージマネージャーコンソールで下記のコマンドを実行

Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Npgsql.EntityFrameworkCore.PostgreSQL

正常にインストールされれば完了です。

コードの追加

モデルの作成を作成します。
プロジェクト直下のModels配下に以下の内容でStudent.csを追加

namespace PostgreSQLTrial.Models
{
    public class Student
    {
        public int Id { get; set; }
        public string FirstMidName { get; set; }
        public string LastName { get; set; }
    }
}

次にContextを作成します。
プロジェクト直下にDataディレクトリを作成し、Dataディレクトリ配下に以下の内容でShoolContext.csを追加します。

using Microsoft.EntityFrameworkCore;
using PostgreSQLTrial.Models;

namespace PostgreSQLTrial.Data
{
    public class SchoolContext: DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {

        }

        public DbSet<Student> Students { get; set; }
    }
}

ConnectionStringの設定

次はPostgreSQL接続用のConnectionStringを設定します。ConnectionStringはappsettings.jsonとappsettings.Development.jsonで定義します。appsettings.jsonは本番構成時、appsettings.Development.jsonデバッグ構成時に利用されます。

それぞれに以下を追加します。

"ConnectionStrings": {
    "SchoolContext": "Host=my_host;Database=my_db;Username=my_user;Password=my_pw"
}

追加後のappsettings.jsonは私の環境では以下のようになっています。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
    "ConnectionStrings": {
        "SchoolContext": "Host=my_host;Database=my_db;Username=my_user;Password=my_pw"
    }
}

my_host、my_db、my_user、my_pwとなっている箇所は自分のPostgreSQLの設定に合わせて適宜変更が必要です。

次にStartup.csにSchoolContextを追加する処理を加えます
以下のコードをConfigureServicesに追加してください。

services.AddDbContext<SchoolContext>(options => 
{
    options.UseNpgsql(Configuration.GetConnectionString("SchoolContext"));
});

追加後のConfigureServicesは以下のようになります。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddDbContext<SchoolContext>(options => 
    {
        options.UseNpgsql(Configuration.GetConnectionString("SchoolContext"));
    });
}

Migrationの作成と実行

まずはPostgreSQLにSchoolデータベースを作成します。

PostgreSQLで以下のSQLを流します

create database school;

データベースの作成もコードファーストで行いたい方は
docs.microsoft.com
こちらを参考にしてください。

データベースの作成が完了したら以下のコマンドをパッケージマネージャーコンソールで流します。
このコマンドで移行の作成とデータベースへの適用が完了します。

Add-Migration InitialMigration
Update-Database

スキャフォールディングでControllerとViewを追加する

ソリューションエクスプローラーでControllersディレクトリを右クリック→追加→新規スキャフォールディングアイテムと選択します。

「新規スキャフォールディングアイテムの追加」ダイアログでは「Entity Framework を使用したビューがあるMVCコントローラー」を選択し、「追加」を押します。

モデルクラスに「Student」、データコンテキストクラスに「SchoolContext」を選択します。

コントローラー名は任意でいいですが本記事ではデフォルトの「StudentsController」としました。

「追加」を押します。

スキャフォールディングが完了するとControllers配下に「StudentsController.cs」がViews配下に「Students」ディレクトリがあり、Index、Create,Edit、Details、Deleteのcshtmlファイルがあるはずです。

動作確認

F5を押す、もしくはIIS Expressボタンを押してデバッグ実行します。

ブラウザが立ち上がったらアドレスバーに「/Students/Index」を追加します。

追加後のアドレスは私の環境では「https://localhost:44345/Students/Index」となりました。

Studentの一覧画面が表示されていると思いますので追加、編集、削除を実行します。

それぞれ正常に動作すればASP.NET Core MVCへのPostgreSQL導入は完了です