🙌

おれのubuntu(WSL2)設定メモ_(2つ目)

2024/09/25に公開

\large{Zennは、8万字を超えると、保存できないようなので分けました}
https://zenn.dev/tazzae999jp/articles/07bed12c3ae6c0
\large{上記が、1つ目です}

storageディレクトリの所有者をapache実行ユーザにするのはLaravelの開発環境構築のお作法

1つ目のサイトの
Laravelでの画像のアップロードおよび表示のお作法

目次項目で、
dockerコンテナ内のapacheの実行ユーザが
ユーザ名 : www-data
グループ名 : www-data
であることを確認したうえで、

# chown -R www-data:www-data /var/www/html/laravelapp/storage
# chmod -R 775 /var/www/html/laravelapp/storage

をすると書いた、これは別に
アップロードの実装をアプリで「する/しない」にかかわらず必要である

ホスト側の一般ユーザでgit cloneした時に、
storageフォルダができあがるので
基本的には、
gitコマンドを打ち込んだ一般ユーザ所有で
755や、775などになってるはずなんです。
この状況では、www-dataでは書き込み権限がありません。
だから、必要なのです

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
<<一部のMacユーザが、これをしなくても動くのは、Docker for Macの設計不具合です>>
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
別にDockerに関係なく、aplfsとext4の間でvirtioFSによるファイル共有した場合
両者の権限設定をあわせることがうまくできないバグが報告されているようです。
virtioFSは、権限を厳しく見ず、緩和的に緩くする挙動で、ファイル共有の高速化を図るような
動きをするようです。
Docker for MacはこのvirtioFSを使い
Mac(BSDのUnix)と、docker(VMのLinuxカーネル)との間のバインドマウントを解決しているが
ゆえに、
本来、権限がないwww-dataで書き込みができて動いてしまう状況になってます
osxfsなどの低速だが厳密に権限を見る、ファイル共有の仕組みを
使う設定にDocker for Macしてる環境では、Ubuntu(WSL2)と同様に
storageの所有者をwww-dataに変更しないと動かないようです。

問題は、virtioFSなどを使う設定のDocker for Macで
権限設定せずに動いてしまってるMacユーザが、ここの権限に関して
意識が希薄になってないか、と思うです

どのみち、本番デプロイはLinux環境なのですから、
Macユーザも、意識しておさえておくべきポイントではないか

権限問題と言えば、当サイトで、Ubuntus(WSL2)側は、POSIX ACLで解決したほうがよい
状況に起因する問題も、結局は、多数派のMacユーザが、
BSDのUnixであるがゆえに発生する特殊事情の結果、楽ができる設定や、やり方を
チーム内のLinuxユーザに対して、提供してくるのが問題なのです
何事も、Linux側で問題がないように意識すれば、誰もなにも問題起きません
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

Visual Studio Codeで既存の「.vscode/launch.json」が認識されない場合

F1 キーを押して「Launch」で検索し、「Open launch.json」
で解決できる!!
理由は知らん。

「wslu : WSL用のユーティリティコレクション」が無いとCLIからWebブラウザの起動ができない

fly.ioへのデプロイ作業時に、
下記の「flyctl auth login」コマンドなどを打ち込んだ時

$ flyctl auth login
failed opening browser. Copy the url (https://fly.io/app/auth/cli/xxxx) into a browser and continue
Opening https://fly.io/app/auth/cli/xxxxx ...

Waiting for session...⣯

のようになり
「Waiting for session...⣯」で固まっている

failed opening browser. Copy the url
のメッセージもあり

CLIより、ブラウザの起動などの操作をしようとして失敗している
fly.ioはこれができないと先に進めない箇所があったように記憶している

CLIより、ブラウザの起動などの操作を可能にするため

Ubuntu(WSL2)に「wslu : WSL用のユーティリティコレクション」をインストールする必要がある

まず、

dpkg -l | grep wslu

で何も返ってこない状況だと、
「wslu : WSL用のユーティリティコレクション」がインストールされていない状況だと言える

それを確認したうえで

sudo apt update
sudo apt install wslu

でインストールできます

下記でインストールできてることが確認できる

$ dpkg -l | grep wslu
ii  wslu                                 3.2.3-0ubuntu3                           amd64        collection of utilities for the Windows 10 Linux Subsystem

久々にJavaをやるときの覚書

以前、Javaの開発を久々にやった時に
eclipse上の下記の設定が思い出せずに、
それを思い出しなおすまでの
数日間、作業効率が下がってしまったことがあったので
そんなことが無いように、ここにメモっておくことにした

★★★★★★★★
★★ その1 ★★
★★★★★★★★
ソースコードは、
Ctrl + Shift + r
で起動した窓で
ソースコード名の部分一致検索での
インクリメンタルが検索できる
★★★★★★★★
★★★★★★★★

★★★★★★★★
★★ その2 ★★
★★★★★★★★
ビルドパスが設定されている
.jarの内部や、.classなどの
コンパイル済みのクラスは、
Ctrl + Shift + t
で起動した窓で
クラス名の部分一致検索での
インクリメンタルが検索できる
★★★★★★★★
★★★★★★★★

★★★★★★★★
★★ その3 ★★
★★★★★★★★
ツリービューの上部にある


「ボタンのエディタにリンク」のトグルボタンを押しこんで有効にしておく

開いているソースコードに連動して、ツリービューが該当のものが選択状態になる
★★★★★★★★
★★★★★★★★

★★★★★★★★
★★ その4 ★★
★★★★★★★★
ウィンドウ - ビューの表示 - その他

で、
progress
と入力し、検索結果の
進行状況 のビューを、
のタブ表示に追加しておくのがよい

問題
と入力し、検索結果の
問題 のビューを、
のタブ表示に追加しておくのがよい

表示 または、Debug Shell
と入力し、検索結果の
表示 のビュー
まはた
Debug Shell のビュー
のタブ表示に追加しておくのがよい

eclipseだと「表示」ビュー、STSだと「Debug Shell」ビューなど
eclipseの亜種がいろいろあり、名称が異なるが、機能は同じビューである。
★★★★★★★★
★★★★★★★★

★★★★★★★★
★★ その5 ★★
★★★★★★★★
ビルド方式として、Gradleを使っているケースは、
プロジェクトを右クリして、
Gradle - Gradleプロジェクトのリフレッシュ
をする必要があったりする
いつも、必要ではないが
通常のビルトとは別にそれも
をしないと、初期のビルドや、
何かの実装の変更時などで
ビルドエラーが消せないことがある
ということだけ、心得ておくこと
★★★★★★★★
★★★★★★★★

Ubuntu(WSL2)でjavaを使えるようにした時のメモ

sudo apt update
sudo apt install default-jdk
$ java -version
openjdk version "21.0.4" 2024-07-16
OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu224.04)
OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu224.04, mixed mode, sharing)

Extension Pack for Java v0.29.0
をVisualStudioCodeに入れたら
VisualStudioCodeで動かせた
複雑なことをしたかったわけでなく
以前、ソースコードの状況を比較したりする
ツールを個人的にjavaで作っててそれがが動けばよし
だったので、一旦、現時点では、これ以上の深追い調査はしない。

Node.jsとnpm

npm i
を打ち込んだら、エラーが連発し、
which npm
で確認したら、Windows側にインストールしたNode.js見に行ってて
そりゃ、エラーなるよ
ということだった。

なので、Ubuntu側にもインストールする
PATH変数はUbuntu側が優先的な値のため
Ubuntuにも入れたら、そっちを見に行ってくれる

別件 Node.jsのインストール、( npm コマンドが使いたかった )

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -

をやってから

sudo apt install nodejs -y

をする

node -v

npm -v

で、確認する

例)

$ node -v
v22.6.0
$ npm -v
10.8.2






Node.jsとnpmはnodebrewでインストールしたほうがよい

1つ上の「Node.jsとnpm」を書いてた時に、直でインストールしていた

$ node -v
v22.6.0
$ npm -v
10.8.2

$ which node
/usr/bin/node
$ which npm
/usr/bin/npm

を一旦、削除してから、
nodebrewで同じバージョンのNode.jsとnpmを入れなおす方向性で作業しました。
その作業のついでに、この目次項目の記事を書くことにしました。

直でインストールしたものなので、
sudo apt-get remove --purge nodejs npm
下記のコマンドで普通に削除します

$ sudo apt-get remove --purge nodejs npm
[sudo] password for 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Package 'npm' is not installed, so not removed
The following packages will be REMOVED:
  nodejs*
0 upgraded, 0 newly installed, 1 to remove and 81 not upgraded.
After this operation, 219 MB disk space will be freed.
Do you want to continue? [Y/n] y
(Reading database ... 49912 files and directories currently installed.)
Removing nodejs (22.6.0-1nodesource1) ...
Processing triggers for man-db (2.12.0-4build2) ...

$ node -v
-bash: /usr/bin/node: No such file or directory
$ npm -v
-bash: /usr/bin/npm: No such file or directory
$
$ which node
$
$ which npm
/mnt/c/Program Files/nodejs//npm
$

nodeは、not foundで
npmは、Ubuntu(WSL2)のものはなくなり、環境変数PATHの後ろのほうに指定してる
Windows側のものがwhichコマンドででてくるような状況になりました。

なお、
sudo apt-get remove --purge nodejs npm
--purge は、
関連する設定ファイル(/etc/ディレクトリなどに保存されている設定情報)も全て削除します。
システムにNode.jsやnpmに関する痕跡を残さないために、完全なクリーンアップ
の意味だそうです。




ここからは、nodebrewでのNode.jsとnpmのインストール

https://github.com/hokaccha/nodebrew

のサイトに行き、nodebrewのインストール方法を見る

nodebrewは、一般ユーザでインストールするので
rootになって作業したり、sudoを使って作業はしない

curl -L git.io/nodebrew | perl - setup
を実行する

$ curl -L git.io/nodebrew | perl - setup
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
100 26039  100 26039    0     0  10950      0  0:00:02  0:00:02 --:--:--   99k
Fetching nodebrew...
Installed nodebrew in $HOME/.nodebrew

========================================
Export a path to nodebrew:

export PATH=$HOME/.nodebrew/current/bin:$PATH
========================================
$

最後に出力された

export PATH=$HOME/.nodebrew/current/bin:$PATH


~/.bashrc

~/.zshrc

~/.profile
などログイン時に実行されるシェルに追記したあと
( 開発端末など、開発作業するユーザが1つと決めてるならば
~/.profileに追記しておけば、使ってるシェルが変わっても継続反映できて便利だろう )

source ~/.bashrc

source ~/.zshrc

source ~/.profile
などで、上記の追記したものを
再読み込みする(または、ターミナルの起動からやり直す)

nodebrew -v
を打ち込む
下記のように出力されたら、nodebrew自体のインストールはできている

$ nodebrew -v
nodebrew 1.2.0

Usage:
    nodebrew help                         Show this message
    nodebrew install <version>            Download and install <version> (from binary)
    nodebrew compile <version>            Download and install <version> (from source)
    nodebrew install-binary <version>     Alias of `install` (For backward compatibility)
    nodebrew uninstall <version>          Uninstall <version>
    nodebrew use <version>                Use <version>
    nodebrew list                         List installed versions
    nodebrew ls                           Alias for `list`
    nodebrew ls-remote                    List remote versions
    nodebrew ls-all                       List remote and installed versions
    nodebrew alias <key> <value>          Set alias
    nodebrew unalias <key>                Remove alias
    nodebrew clean <version> | all        Remove source file
    nodebrew selfupdate                   Update nodebrew
    nodebrew migrate-package <version>    Install global NPM packages contained in <version> to current version
    nodebrew exec <version> -- <command>  Execute <command> using specified <version>
    nodebrew prune [--dry-run]            Uninstall old versions, keeping the latest version for each major version

Example:
    # install
    nodebrew install v8.9.4

    # use a specific version number
    nodebrew use v8.9.4
$

公式サイトにいろいろと書いてて、見つけにくいが
nodebrew install-binary
でインストールするのがオススメとされている

nodebrew install-binary では、ビルド済みのバイナリをダウンロードするだけなので、
ソースからビルドする場合に必要な make、gcc、g++ などのビルドツールが不要です。
これにより、インストールに際して余計なパッケージを追加する手間が省け、
システムをシンプルに保つことができます。

ソースからビルドする場合、環境依存のエラーや、ビルドツールのバージョンによる互換性問題が発生することがありますが、
install-binary は既にビルドされているため、こうしたエラーが発生しにくいです。
結果として、より安定して確実にインストールが完了します。

通常は、

nodebrew install-binary latest
(最新版)のインストール

nodebrew install-binary stable
(安定版の中での最新版)のインストール

でインストールするとのこと

ここでは、(この文章を書いてる時点では)
先ほどnodebrewでない方式でインストールしてて、削除した
メモしていたバージョンの復活のため

nodebrew install-binary v22.6.0

を実行することにした

$ nodebrew install-binary v22.6.0
Fetching: https://nodejs.org/dist/v22.6.0/node-v22.6.0-linux-x64.tar.gz
########################################################################################################################################################################################################## 100.0%
Installed successfully

その後

$ node -v
Command 'node' not found, but can be installed with:
sudo apt install nodejs
$ npm -v
10.8.2
$

となった
Node.jsは、v22.6.0を
nodebrew install-binary v22.6.0
で、インストールしたはずなのに、
node -v は、not found

npm -vは、対応するnpmのバージョン10.8.2
が使える状況となっている

なぜ、node -v はnot foundか?

nodebrew ls
で、インストール済のNode.jsのバージョンを表示できる

$ nodebrew ls
v22.6.0

current: none

current: none となってる状況では
なにも、選択状態になっていない

だから、node -v はnot foundになるのである

nodebrew use v22.6.0
を打ち込むと

今度は下記のようになった

$ nodebrew use v22.6.0
use v22.6.0

$ nodebrew ls
v22.6.0

current: v22.6.0

$ node -v
v22.6.0
$ npm -v
10.8.2
$

$ which node
/home/myuser/.nodebrew/current/bin/node
$
$ which npm
/home/myuser/.nodebrew/current/bin/npm
$

これで使える
なお、
nodebrew install-binaryで、node.jsをインストールすると
対応するバージョンのnpmもインストールしてくれるようだ

nodebrewは、node.jsのバージョンをnodebrew useで切り替えると
環境にnpmがあれば、切替先のnode.jsにあったバージョンのnpmに切り替えもしてくれるとのことだ

これで、

nodebrewを使う前に、入れてたバージョンと同じ
node.jsと、npmがそろったので

nodebrewを使う前のnode.jsと、npmのときに作った環境のプロジェクトフォルダを
VisualStudioCodeで開いて
npm run dev
npm run build
npm run preview
などが動くことが確認された

<おまけ>
nodeと打ち込んだら、nodeのプロンプトが打てる状況となる
そこでhello worldしてる例

$ node
Welcome to Node.js v22.6.0.
Type ".help" for more information.
>
> console.log('hello world')
hello world
undefined
>
>
(To exit, press Ctrl+C again or Ctrl+D or type .exit)
>


nodebrewを久しぶりに触ったとき

詳細は前項目の「Node.jsとnpmはnodebrewでインストールしたほうがよい」
を参照のことだが

nodebrew install-binary stable
(安定版の中での最新版)のインストール
をする
( 新しいバージョンがあるかもなので )

$ nodebrew install-binary stable
Fetching: https://nodejs.org/dist/v22.12.0/node-v22.12.0-linux-x64.tar.gz
##################################################################################################################################################### 100.0%
Installed successfully

nodebrew ls
で、確認し、
nodebrew use なにがし
で、インストール済の中で最新のものを選ぶ
再度、
nodebrew ls

で、確認する。

node -v

npm -v
で確認する

$ nodebrew ls
v20.18.0
v22.6.0
v22.12.0

current: v22.6.0

$ nodebrew use v22.12.0
use v22.12.0

$ nodebrew ls
v20.18.0
v22.6.0
v22.12.0

current: v22.12.0
$


$ node -v
v22.12.0

$ npm -v
10.9.0

reactとtypescriptが使えるテンプレートのプロジェクトを簡単に作成する方法

一旦、前項目の「nodebrewを久しぶりに触ったとき」を参考に
環境を最新化しとくのがよい。(もし、久しぶりにnodeを使うならば)

create-react-app-my
という空フォルダを作った後に、そちらに移動後
code .
でVSコードを起動した

$ mkdir create-react-app-my
$ cd create-react-app-my
$ code .
$ npm create vite@latest
Need to install the following packages:
create-vite@6.1.1
Ok to proceed? (y) y


> npx
> create-vite

✔ Project name: … .
✔ Select a framework: › React
✔ Select a variant: › TypeScript

Scaffolding project in /udemydir/understanding-typescript-jp/create-react-app-my...

Done. Now run:

  npm install
  npm run dev
$ npm install

added 181 packages, and audited 182 packages in 2m

43 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
$ npm run dev

  VITE v6.0.6  ready in 373 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

$ npm install --save react-router-dom

added 7 packages, and audited 189 packages in 6m

43 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities


$ npm install --save-dev @types/react-router-dom

added 3 packages, and audited 192 packages in 2m

43 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities


上記のように、追加のものをインストールできる

周辺モジュールをTypescriptで利用する手順
(1) パッケージの公式ドキュメントを確認する。
(2) 試しにインストールして、型定義があるか確認する。
(3) 型定義ファイルが無ければtypesパッケージを追加する。

追記)
npm なにがし
npx なにがし
のコマンドは、「直下に、package.jsonがあるフォルダ」で打ち込むのが基本。
開発時は「直下に、package.jsonがあるフォルダ」をカレントディクトリにして、
npm run devで動かすのが基本。

Typescriptのトランスパイルをするためviteのインストール方法

インストールしたいフォルダにcdコマンドで移動する

code .

にて、そのフォルダで、VisualStudioCodeを移動する。

以下、VisualStudioCode内のターミナル上での作業を行う

npm create vite@latest

どこにインストールか聞かれるので、

.

とだけ打ち込んでエンターキーを押す
あらかじめインストールしたいフォルダにcdコマンドで移動しているためそれでよい

Select a framewarod:
と表示され
Vanilla
Vue
React
Lit
Svelte
Solid
Qwik
Others
の選択肢が出てくる
Vanillaを選択するとframeworkなし

その後、
TypeScript
Javascript
を選択するようになっているが、そこで
TypeScript
を選択する

ここまでの流れは、下記のようなコンソールログとなる

$ npm create vite@latest
Need to install the following packages:
create-vite@5.5.2
Ok to proceed? (y) y


> npx
> create-vite

✔ Project name: … .
✔ Select a framework: › Vanilla
✔ Select a variant: › TypeScript

Scaffolding project in /udemydir/ts_jik/test...

Done. Now run:

  npm install
  npm run dev

npm notice
npm notice New patch version of npm available! 10.8.2 -> 10.8.3
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.8.3
npm notice To update run: npm install -g npm@10.8.3
npm notice

npm install
を実行する

$ npm install

added 6 packages, removed 43 packages, changed 6 packages, and audited 12 packages in 7s

3 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

npm run dev
を実行する

$ npm run dev

> test@0.0.0 dev
> vite


  VITE v5.4.3  ready in 251 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

Vite + Typescript
と書かれたwebページが、

http://127.0.0.1:5173/

でブラウザ起動する形となる

Typescriptに必要な設定ファイルや、フォルダが一通り出来上がるため
srcフォルダの下に

*.tsのファイルを作りTypescriptの実装が行える状況となる

Typescriptだけの環境でプロジェクトルートよりwatchモードでコンパイルする環境にしたい

プロジェクトのルートフォルダにて、
tsc --init
を打ち込む

$ tsc --init

Created a new tsconfig.json with:                                                                                       
                                                                                                                     TS 
  target: es2016
  module: commonjs
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true


You can learn more at https://aka.ms/tsconfig

これで、
tsconfig.json
が自動的に作成される。
tsc --initは、打ち込んだカレントディレクトリに、tsconfig.jsonを
自動で作るコマンドである。
tsconfig.jsonのファイルの意味は、tsconfig.jsonがあるフォルダおよび、
そのサブフォルダの末端まで、typescriptの管理下に置きます
という意味である。
なので、tsconfig.jsonが自動で作られた状況で
たとえ、tsconfig.json自体が自動で作られたデフォルトの状況でも、
プロジェクトのルートでtscコマンドを打ち込めば、
末端まで「*.ts」を探して自動的コンパイルできるようになるのである。
tsc
で全体コンパイルできるし、
tsc --watch
または、
tsc -w
でwatchモードが使える。

viteを使わなくても、.tsを.jsにコンパイルを、*.tsの保存で自動で行うだけであれば、
この方法で十分ではある。

tsconfig.jsonで、"experimentalDecorators": true と書くことでデコレーターが使える

typescriptでデコレーターを使いたかったら
tsconfig.jsonで、
"experimentalDecorators": true
を指定する必要があります。

tsconfig.jsonでexcludeを書く場合は、"node_modules"の指定を含める必要がある。

node_modulesの中にある「ts」から「js」へのコンパイルまでされてしまうと
プロジェクトが壊れてしまうからやりたくない

tsconfig.jsonに、
excludeの指定がない場合は、
デフォルトで

  "exclude": ["node_modules"]

の指定がなされているという意味になってる。

もし、なにか、「ts」から「js」へのコンパイルの対象外としたい
ものがあり、excludeの指定をすると
デフォルトの指定を上書きしてしまう
そのため、
excludeの指定を明示的に書く場合は、
必ず、"node_modules"も含めなければならない

{
  "compilerOptions": {
  ・
  ・
  (中略)
  ・
  ・
  },
  "exclude": [
    "**/*.dev.ts", "node_modules"
  ]
}

追加したい除外項目だけを見れば、

  "exclude": [
    "**/*.dev.ts"
  ]

だった場合でも、
デフォルトで

  "exclude": ["node_modules"]

の指定が上書きされて
node_modulesが「ts」から「js」へのコンパイル対象になってしまうのでまずい
だから、"node_modules"も指定して

  "exclude": [
    "**/*.dev.ts", "node_modules"
  ]

を追加しておくべきなのである。

なお、例示されている
"**/*.dev.ts"の部分は、プロジェクトルート内のどのフォルダ階層でも
*.dev.tsにマッチするファイル名については、
「ts」から「js」へのコンパイル対象から除外するという意味である。
「 **/ 」記述方法の覚書のため、この例を書いての説明とした。

tsconfig.jsonのincludeとexcludeのお話

includeの指定がなければ、
excludeで除外されたものは除いて、すべての「ts」が「js」へのコンパイル対象

includeの指定があれば、
includeの指定があったものだけが、「ts」が「js」へのコンパイル対象となる

includeの指定と、excludeの指定の両方がある場合は、
includeの指定があったもののうち、excludeの指定があったものを除いた
残りが、「ts」が「js」へのコンパイル対象となる
この意味として解釈される形で、
includeの指定と、excludeの指定の両方を指定することは可能である。

tsconfig.jsonでの「 "sourceMap": true, 」の指定のお話

tsconfig.jsonで

"sourceMap": true,

を指定すると、コンパイル時に .js.map が作成される
これが .jsと、.tsのマッピングをとってくれる
モダンなブラウザでは、これを理解してくれるため、
ブラウザのデバッガーのステップ実行を「
.ts」のコードにて
行えるようになる。

なお、
tsc --initデフォルトで作成されたtsconfig.jsonで該当箇所がコメントアウトされているので
それのコメントをやめて有効化するイメージである。

tsconfig.jsonでの「 "outDir": "./dist/", 」と「 "sourceRoot": "./src/", 」の指定のお話

例として、tsconfig.jsonで

"outDir": "./dist/",
"sourceRoot": "./src/",

と指定すると「*.ts」の置き場所を src フォルダの中
コンパイルされた「 *.js 」の出力先を dist フォルダの中
という設定にすることができる。

なお、
tsc --initデフォルトで作成されたtsconfig.jsonで該当箇所がコメントアウトされているので
それのコメントをやめて有効化して、
値の部分に、"./dist/"や、"./src/"を指定するイメージだ
distや、srcのフォルダ名は、慣習的に、この目的を表すフォルダ名であるようだ。

tsconfig.jsonでの「 "outFile": "./dist/bundle.js", 」の指定のお話

Typescriptでファイル分割し、

参照元の*.tsの先頭部で、
/// <reference path="hoge.ts" />
にて、参照先の*.tsを参照する設定し、コンパイルを取り除いても

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
この方式は、レガシーであり、コンパイラに怒ってもらいにくい。危ういやり方なので
次の目次項目の「typescriptでのES6モジュールのimport方式が使いたい場合」
を参照のこと。
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

実行時に、吐き出されたjavascriptが参照を解決できず、
実行時にエラーになってしまうようなときは、

1つのバンドルした*.jsに吐き出すようにすることで、解決できることがある。

tsconfig.json
で、

"outFile": "./",
をコメントインして、

"outFile": "./dist/bundle.js",

のようにする。

##################
<追記>
##################
<script src="dist/app.js" defer></script>
などしているところがあれば、
<script src="dist/bundle.js" defer></script>
などに変更する必要がある。
##################

ただし、これをすると、

"module": "amd"
などにしとかないと怒られる

それから、
"outFile": "./",
を使う方式は、あまりモダンな方法ではない

"module": "esnext"
にして、

Viteや、Webpackのモジュールハンドラーを使うのが、推奨される

typescriptでのES6モジュールのimport方式が使いたい場合

Typescriptの各ソースコードの先頭位置など、

import { Project, ProjectStatus } from "../models/project.js";

このような構文で、importする方式は、es6モジュールによるimportのやり方であるとされていて
モダンなブラウザではサポートされている

補足:
ES6モジュールについての補足事項
(1) ES6モジュール=モダンブラウザのみ可
(2) ES6モジュールを古いブラウザ(IE9等)でも動かす場合、
Webpack等のモジュールバンドラが必要

モジュールバンドラを使う理由
(1) 古いブラウザのサポート
(2) JSファイルを1つにまとめる(バンドルにする)

これをするは、

エントリーポイントでの*.htmlでは、

<script type="module" src="dist/app.js"></script>

のような形で、type="module"として、( deferもつけない書き方とする必要がある )

ただし、htmlに、type="module"でのブラウザでの読み込むケースは、

import { Project, ProjectStatus } from "../models/project.js";

のように、「.js」をまで書いて、コンパイル後のjavascriptのソースファイルをインポートするように書く必要がある。

angularなどのフレームワークでは、
importなどで、「.js」を書いていない。それは、webpackなどのビルドツールを使う場合は、書かないのである

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
VSコードでの補完機能を使って、怒られた箇所の「クイックフィックス」などで自動で
import文を書いたケースで「.js」が付かないケースがある。
webpackなどのビルドツールを使っていないような環境で、「.js」まで必要なケースは、
自動で追加されたimport文の末尾に自分で「.js」を補っていく必要がある。
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

tsconfig.jsonについては、

"module": "ES2015" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,

のように、ES2015
を書く

これは、Ecmascript 2015が、ES6のことであり、

ES6モジュールでのインポート方式を他の方式に変更しないでください

と指示をするための記述である。

グループ化してのimportのやり方

import { Validatable, validate } from "../util/validation.js";
のようなimportについて、

import * as Validation from "../util/validation.js";
のようなimport文に変更し、

validation.jsにあるモジュールの利用箇所に、

「Validation.」をつけていく方式をとると、

この方式には、下記のメリットがある。
・該当のものがどこのモジュールに属しているのかが、わかりやすくなる
・利用箇所での他のモジュールとの名前の衝突を防ぐことができる

import時に別名にすることができる

import { autobind } from "../decorators/autobind.js";
で、importしていて、
@autobind
private hogeogeHandler(event: Event) {

のように利用していたが

autobindの先頭文字を、大文字にした
Autobindで使いたかったとする

import { autobind as Autobind } from "../decorators/autobind.js";
で、importすれば、

@Autobind
private hogeogeHandler(event: Event) {

のように利用することができる

ただ、これは、あくまでimportした箇所で別名にしているだけで、
import元のautobindは、元のままです。

import先での名前の衝突を回避するために、局所的に別名にして逃げる、やり方としても使える。

「名前付きのexport」と「デフォルトexport」のお話

export class HogeHogeManager {

などは、「名前付きのexport」と呼ばれる

なぜなら、importするときに、{ }の中に、そいつの名前を記述するように、

import { HogeHogeManager } from xxxxxx

ことを要求しているようなexportだからだ

それに対して、

export default class HogeHogeManager {

「デフォルトexport」と呼ばれるexport方式がある

1つのソースコードの中に、
「名前付きのexport」
も、
「デフォルトexport」
の両方があってもよいが

「デフォルトexport」
のほうは、1つのソースコードに付き最大1つまでしか許されない

「デフォルトexport」したもは、

import Hoge from xxxxxx

のように、{ }で囲まず、省略的な名前にできる

typescriptでのVSコードのプラグイン

https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint

https://marketplace.visualstudio.com/items?itemName=loiane.ts-extension-pack

https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

Typescriptでprettierでコードのフォーマットをする。

VSコードに、
https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode
のプラグインを入れて、有効にした状況で、
.vscode/settings.json
を下記のようにして、(VSコードの再起動が必要)
保存時にprettierによるフォーマットを実行する

{
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "[javascript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "editor.formatOnSave": true
}

"editor.formatOnSave": true
保存時にコードのフォーマットをしたければ、指定する。

手動でするには、
yarn prettier --write src
か、
npx prettier --write src
を実行する。

Typescriptでバニラのjavascriptライブラリについて型情報が欲しいケース(lodashの例)

たとえば、lodashを下記のようにインストールしたとする。

$ npm install --save lodash

added 1 package, and audited 340 packages in 27s

55 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

そのうえで、下記のようなコードを書いたとしても

import _ from 'lodash';
console.log(_.shuffle([1, 2, 3]));

Typescriptには、理解できず、コンパイルエラーになってしまう

ただ、lodashのような有名なライブラリなどは、

types lodash
で、ぐぐれば、

https://www.npmjs.com/package/@types/lodash

のようなサイトがヒットする。

本家は、
https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/lodash

のようである

ここには、いろいろな、バニラのjavascriptのライブラリについて、
Typescriptのコンパイラに認識させるための型情報の定義がある

*.d.ts
デクレラレーションファイル、型定義ファイル
の実装だけであり。処理部分は実装していないもで
各種のバニラのjavascriptについてのそれらの定義を集めたような
githubのリポジトリである。

lodashについて、ここから、型情報の定義をインストールする方法として、

npm install --save @types/lodash
でのインストール方法が書かれていたりするのであるが

npm install --save-dev @types/lodash
のように、「--save-dev」でインストールしたほうがよい

その理由は、
lodashは、本番環境には、最終的に入りに、webpackにバンドルされて入る
開発中だけ、その型情報をtypescriptに理解させるため欲しいので
「--save-dev」でインストールしたほうがよい

これで、下記のようなコードをTypescriptが認識できるようになる。

import _ from 'lodash';
console.log(_.shuffle([1, 2, 3]));

Typescriptのアンビエント宣言

index.htmlなどに

    <script>
      let GLOBAL = 'グローバル変数です';
    </script>

などの定義があって、それを参照するコードを

console.log(GLOBAL);

と書いても、Typescriptが理解できず、怒られる

declare let GLOBAL: string;

console.log(GLOBAL);

にすると怒られなくなる。

declare let GLOBAL: string;
は、アンビエント宣言
と言って、プログラマがjavascriptの世界で見た時に、
これは、実行時に、この形で存在することがわかってますので、
Typescriptに、「この形であること前提で解釈してください」
と伝えるようなやり方ならしい。

先ほどの目次項目でのtypesが見当たらないようなバニラのjavascriptライブラリを
使っていたり、自分で書いたjavascriptコードの部分などで、
局所的にでも、このようにして、Typescriptに怒られるのを回避したいケースが
あった場合に、使える方法である。

Typescriptの「class-transformer」

product.tsでの

export class Product {
    title: string;
    price: number;

    constructor(t: string, p: number) {
        this.title = t;
        this.price = p;
    }

    getInfomation() {
        return [this.title, `${this.price}円`];
    }
}

のような定義があり

例として、productsがバックエンドから取得したJsonだったとしたとき

import { Product } from "./product";

const products = [
  {
    title: "商品1",
    price: 100,
  },
  {
    title: "商品2",
    price: 200,
  },
];

// const p1 = new Product("商品1", 100);

const loadedProducts = products.map((item) => {
  return new Product(item.title, item.price);
});
for (let p of loadedProducts) {
  console.log(p.getInfomation());
}
const loadedProducts = products.map((item) => {
  return new Product(item.title, item.price);
});

に相当するような変換処理の実装部分を自動化したい

そのときに、class-transformerが使えて

本家のサイトは、
https://github.com/typestack/class-transformer

であるが、それだJsonからインスタンスにマッピングできる

ただし、
tsconfig.json
で、
"module": "ES2015",
などになっていたら、
それを
"module": "CommonJS",
に変更する必要がある。

CommonJSは、主にNode.jsなど、サーバーサイドのモジュール規格です。
Webpackを使う場合は、CommonJSを設定しても問題ないとのこと。

npm install class-transformer --save

npm install reflect-metadata --save

でのインストールをする

$ npm install class-transformer --save

added 1 package, and audited 338 packages in 1m

55 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

$ npm install reflect-metadata --save

added 1 package, and audited 339 packages in 1m

55 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

すると実装は、下記のように
import "reflect-metadata";
import { plainToInstance } from "class-transformer";
をしたうえで、
let loadedProducts = plainToInstance(Product, products);
のようなマッピングができる。

import "reflect-metadata";
import { plainToInstance } from "class-transformer";

import { Product } from "./product";

const products = [
  {
    title: "商品1",
    price: 100,
  },
  {
    title: "商品2",
    price: 200,
  },
];

let loadedProducts = plainToInstance(Product, products);

for (let p of loadedProducts) {
  console.log(p.getInfomation());
}

Typescriptでのclass-validator

class-validatorは、Typescriptのデコレーターを使ったライブラリなので、

tsconfig.json
で、
"experimentalDecorators": true
を指定している必要があります。

それから、class-validatorについては、

https://qiita.com/3062_in_zamud/items/f6b1dad4b3ecb3d5ca52

の記事などを見てもらうとして、

npm install class-validator --save
でインストール

$ npm install class-validator --save

added 4 packages, and audited 343 packages in 3m

55 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

product.ts

import { IsNotEmpty, IsNumber, IsPositive } from "class-validator";

export class Product {
  @IsNotEmpty()
  title: string;

  @IsNumber()
  @IsPositive()
  price: number;

  constructor(t: string, p: number) {
    this.title = t;
    this.price = p;
  }

  getInfomation() {
    return [this.title, `${this.price}円`];
  }
}

で、

import { validate } from "class-validator";

const newProd = new Product("", -100);

validate(newProd).then((errors) => {
  if (errors.length > 0) {
    console.log("バリデーションエラー");
    console.log(errors);
  } else {
    console.log(newProd.getInfomation());
  }
});

で、
下記のようなコンソールが出る


[
    {
        "target": {
            "title": "",
            "price": -100
        },
        "value": "",
        "property": "title",
        "children": [],
        "constraints": {
            "isNotEmpty": "title should not be empty"
        }
    },
    {
        "target": {
            "title": "",
            "price": -100
        },
        "value": -100,
        "property": "price",
        "children": [],
        "constraints": {
            "isPositive": "price must be a positive number"
        }
    }
]

Vue.jsの環境を作る例

https://www.youtube.com/watch?v=Oyr0sr6l3SQ&t=46s
の動画を参考に、vue.jsの環境を作ったときのメモ書き

前述の目次項目では、先にフォルダを作成し
そのフォルダ内へcdコマンドで移動してから
インストール作業をして、Project nameのところで、. を打ち込む方式
だったが、vue.jsのとき、そのやり方だとエラーになってうまくいかなかったため
ひとつ上のフォルダで、インストール作業をして、
Project nameのところで、vue-lesson のフォルダ名を打ち込んだ

npm create vue@latest

$ npm create vue@latest

> npx
> create-vue


Vue.js - The Progressive JavaScript Framework

RangeError: Incorrect locale information provided

✔ Project name: … vue-lesson
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
✔ Add Vue DevTools 7 extension for debugging? (experimental) … No / Yes

Scaffolding project in /udemydir/vue-lesson...

Done. Now run:

  cd vue-lesson
  npm install
  npm run format
  npm run dev

npm install

$ npm install
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated @humanwhocodes/config-array@0.11.14: Use @eslint/config-array instead
npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead

added 150 packages, and audited 151 packages in 2m

33 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

npm run format

> vue-lesson@0.0.0 format
> prettier --write src/

src/App.vue 197ms (unchanged)
src/assets/base.css 42ms (unchanged)
src/assets/main.css 44ms (unchanged)
src/components/HelloWorld.vue 92ms (unchanged)
src/components/icons/IconCommunity.vue 31ms (unchanged)
src/components/icons/IconDocumentation.vue 16ms (unchanged)
src/components/icons/IconEcosystem.vue 19ms (unchanged)
src/components/icons/IconSupport.vue 9ms (unchanged)
src/components/icons/IconTooling.vue 11ms (unchanged)
src/components/TheWelcome.vue 47ms (unchanged)
src/components/WelcomeItem.vue 36ms (unchanged)
src/main.js 10ms (unchanged)

★上記までのコマンドは、VSコードのターミナルで実行するより、Ubuntus(WSL2)のターミナルで実行するほうが安定していた★

その後、
Project nameのところで、vue-lesson のフォルダ名に、
cdコマンドで移動した後、
code .
でVSコードを起動して
VSコード内のターミナルで

npm run dev
で、vite経由で

http://127.0.0.1:5173/

での初期画面を起動した

npm run dev
でviteを起動しながら、開発作業をすると、ホットデプロイのような形で
ソースコードの修正と同時にローカル動作に反映される

npm run buid
を行うと、distフォルダに静的にvue.jsの内容がjavascriptのみの形に変換された
静的なコードになる

npm run preview
は、distフォルダに出力された内容での動作を見るための
ローカルサーバーを起動するコマンドのようである

npm run lint
は、Linter

npm run format
は、コードのフォーマット

.vscode/extensions.json

{
  "recommendations": [
    "Vue.volar",
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode"
  ]
}

は、おススメの拡張機能
これを入れると効率がよい

.eslintrc.cjs

  'extends': [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-prettier/skip-formatting'
  ],


'plugin:vue/vue3-essential',
は、
'plugin:vue/vue3-recommended',
にして

  'extends': [
    'plugin:vue/vue3-recommended',
    'eslint:recommended',
    '@vue/eslint-config-prettier/skip-formatting'
  ],

にしておくのがよい

pythonの基礎

1)軽いもの
https://www.rabbitriver.page/ja/python-osarai/

2)がっつり系
その1)
https://utokyo-ipp.github.io/
その2)
https://repository.kulib.kyoto-u.ac.jp/dspace/bitstream/2433/285599/1/Version2023_10_17_01.pdf

pythonのタプルの注意点

list_display = ('question_text', 'pub_date',)

は要素が2つのタプルを作ってるが
要素が1つのタプルだった場合

# 注意!!!、★これは間違い★
list_display_ng = ('question_text')  # ダメなやり方、タプルになってない

# 要素が1つの時でも、最後「,」で終わるようにする
list_display_ng = ('question_text',)  # OKなやり方、タプルになる

上記のように、要素が1つの時に、末尾に「,」がないと、単に
()で囲っただけと認識された、タプルと認識されない
そのため、
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
★ 要素が1つのときでもタプルにしたいときは、末尾に「,」を入れるのが重要 ★
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
これを知らなければ、いつか、一回は、ハマるはずだ。
なぜ、()みたいなものをタプルの構文に採用したのか。本当に紛らわしい

ですので、要素数に限らず、タプルを意識しとくときは、

list_display = ('question_text', 'pub_date',)

常に末尾に「,」で終わらせるようにくせずけとけばよろしいだろう。

venv環境

venvはpipでのインストールなしで、使える。はじめから標準でついている。
VisualStudioCodeで、code .で開きたいと思ってるルートフォルダで
下記のコマンドにて、venv環境を作る
venv環境名をvdirとした例を示す。

python -m venv vdir

なお、Mac環境ではデフォルトで最初から入ってる古いバージョンのpythonと区別するため
python3 -m venv vdir
のように「python3」の「3」を付けた方がよいとのこと

上記のコマンドでvdirのフォルダが作成され、仮想環境が作成される。

下記のコマンドでvdirの仮想環境を有効かする

source venv/bin/activate

Ubuntu(WSL2)と、Macの場合は、上記のコマンドでよいとのこと

windowsの場合は、git bashなどで
source venv/Scripts/activate
であるとのこと

コンソールのプロンプトの部分に
(vdir)と表示されるようになったら、その仮想環境が有効な状況で作業ができている

deactivate

で無効化できる。

Djangoの日本語のドキュメント

https://docs.djangoproject.com/ja/5.1/

Djangoのインストール

仮想環境が有効な状況などで、

pip index versions Django

で現在インストール可能なDjangoのバージョンを確認することができる

pip install Django==<バージョン番号>

で、バージョンを指定してインストールできる

pip install Django

だと最新版をインストールすることになる。

$ python -m django --version
5.1.3

これでDjangoがインストールされているか、されているならば、そのバージョンを確認できる。

Djangoプロジェクトのひな型を作成する

プロジェクト名をmysiteとしたときに、

django-admin startproject mysite

このコマンドは、ls -aをしたときに、直下にvenvなどの仮想環境のフォルダがある位置にて
打ち込むのが基本であるとのことだ

上記、コマンドを打ち込むと、上記の例では、mysiteのフォルダが作成されて
その中に、Djangoプロジェクトのひな型が作成されている。

django-admin startproject mysite
だと、mysiteのフォルダの中に、mysiteフォルダを作成し
いろんなファイルが作成される構成になる
manage.pyは、外側のmysiteフォルダの直下に作成される。

外側のmysiteのフォルダ名は重要ではないため、後から変更もできる
内側のmysiteは、プロジェクト名で重要であるため、そのままにしたほうがよい。

一方、

django-admin startproject mysite .
のように最後に「 .」をつけてコマンドを実行すると
mysiteの中に、いろんなファイルが作成される。

  • mysiteフォルダの中に、mysiteフォルダはできない
  • manage.pyは、コマンドを打ち込んだカレントディレクトリにできて、mysiteの中にはできない
    つまり、内側のプロジェクト名を表すmysiteフォルダだけ作成されるイメージである。

ただし、チュートリアルで説明されているのは、「 .」をつけないやり方なので
特別な事情がなければ、「 .」をつけないやり方で、プロジェクトのひな型を
作成すればよろしいだろう。

Django プロジェクトの中に新しくアプリを作る

Djangoでは、1つのプロジェクトの中で、複数のアプリを作成することができる

例として、
pollsという名前のアプリケーションを新しく作る場合
manage.pyがある位置にて、
python manage.py startapp polls
を打ち込むと、pollsフォルダができあがり、中身にひな型のファイルが作成されることになる。

開発用サーバーの起動

python manage.py runserver

を打ち込むのであるが、
manage.pyがあるフォルダをカレントディレクトリとするようにcdコマンドで移動後に
打ち込む必要がある。
また、
VisualStudioCode内のターミナルで打ち込まないとうまく開発サーバーが起動してくれない
ことがあるため。それを心得ておくこと。

python manage.py runserver
は、
http://127.0.0.1:8000/
のようにポートが8000をlistenする形で開発サーバーが起動するが、
それを8080など別のポートをlistenする形で開発サーバーを起動したい場合は、
python manage.py runserver 8080
のようにすると
http://127.0.0.1:8080/
となる。
複数のアプリを開発しているような状況で、ポートをわけて競合しないように
したいケースなどで使えばよろしいだろう。

Djangoのマイグレーションの考え方

例として「polls」という名前のアプリケーションを作ってるケースで

python manage.py migrate
をして、元々
settings.py

INSTALLED_APPS = [
に記載があった分の基礎的なものに必要なテーブル定義をDB上に行う。

その後、pollsアプリの実装に必要なモデルクラスの定義を行う。
mysite\polls\models.py

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

settings.py

INSTALLED_APPS = [
に、
'polls.apps.PollsConfig',
を追加する。

python manage.py makemigrations polls
を実行する

python manage.py makemigrations polls
Migrations for 'polls':
  polls\migrations\0001_initial.py
    + Create model Question
    + Create model Choice

mysite\polls\migrations
の配下に

0001_initial.py

が作成され、そこに、モデルクラスの定義らしきものがある

ただ、まだ、この段階では、マイグレーションが作成されただけであり、

データベースの定義は作られてはいない。

再度、
python manage.py migrate
を実行して

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK

にて、データベースに反映する。

Django環境を意識した対話モード

普通にpythonとだけターミナルで打ち込めば、pythonの対話モードになる。
python manage.py shell
と打ち込んで対話モードにすれば、Djangoの機能を用いることができるpyhonの対話モードになる。

使ってみた例

$ python manage.py shell
Python 3.13.0 (tags/v3.13.0:60403a5, Oct  7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet []>
>>> from django.utils import timezone
>>> q = Question(question_text='好きなフルーツは?', pub_date=timezone.now())
>>> q.save()
>>> q.id
1
>>> q.question_text
'好きなフルーツは?'
>>> q.pub_date
datetime.datetime(2024, 11, 27, 4, 10, 16, 401245, tzinfo=datetime.timezone.utc)
>>> q.question_text = '好きなフルーツはなんですか?'
>>> q.save()
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
>>> quit()
now exiting InteractiveConsole...

datetime.datetime(2024, 11, 27, 4, 10, 16, 401245, tzinfo=datetime.timezone.utc)
の箇所は、日本時間の「2024/11/27 13時10分16秒」時点での実行。

<QuerySet [<Question: Question object (1)>]>
の object (1) の1は、idの1だと思われるが、
この文字列の出方を変えたいので、特殊メソッドの__str__を定義した

また、
was_published_recentlyメソッドもついでに追加した。

mysite\polls\models.py

from django.db import models

import datetime
from django.utils import timezone

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    
    def __str__(self):
        return self.question_text
    
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    
    def __str__(self):
        return self.choice_text

再度、対話モードで作業すると

$ python manage.py shell
Python 3.13.0 (tags/v3.13.0:60403a5, Oct  7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from polls.models import Question
>>> Question.objects.all()
<QuerySet [<Question: 好きなフルーツはなんですか?>]>
>>> quit()
now exiting InteractiveConsole...

先ほど、
<QuerySet [<Question: Question object (1)>]>
だった表示内容が
<QuerySet [<Question: 好きなフルーツはなんですか?>]>
に変わった。

さらに、対話モードの例を示す。
DB値の参照や、保存などの例もここにはある。

$ python manage.py shell
Python 3.13.0 (tags/v3.13.0:60403a5, Oct  7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from polls.models import Question, Choice
>>> Question.objects.all()
<QuerySet [<Question: 好きなフルーツはなんですか?>]>
>>> Question.objects.filter(id=1)
<QuerySet [<Question: 好きなフルーツはなんですか?>]>
>>> Question.objects.filter(question_text__startswith='好きな')
<QuerySet [<Question: 好きなフルーツはなんですか?>]>
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: 好きなフルーツはなんですか?>
>>> Question.objects.get(id=2)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\USER\Desktop\django-tutorial\venv\Lib\site-packages\django\db\models\manager.py", line 87, in manager_method   
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\Desktop\django-tutorial\venv\Lib\site-packages\django\db\models\query.py", line 649, in get
    raise self.model.DoesNotExist(
        "%s matching query does not exist." % self.model._meta.object_name
    )
polls.models.Question.DoesNotExist: Question matching query does not exist.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
>>>
>>> q
<Question: 好きなフルーツはなんですか?>
>>> q.choice_set.all()
<QuerySet []>
>>> q.choice_set.create(choice_text='りんご', votes=0)
<Choice: りんご>
>>> q.choice_set.create(choice_text='バナナ', votes=0)
<Choice: バナナ>
>>> c = q.choice_set.create(choice_text='みかん', votes=0)
>>> c.question
<Question: 好きなフルーツはなんですか?>
>>> q.choice_set.all()
<QuerySet [<Choice: りんご>, <Choice: バナナ>, <Choice: みかん>]>
>>> q.choice_set.count()
3
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: りんご>, <Choice: バナナ>, <Choice: みかん>]>
>>> c = q.choice_set.filter(choice_text__startswith='バ')
>>> c
<QuerySet [<Choice: バナナ>]>
>>> c.delete()
(1, {'polls.Choice': 1})
>>> Choice.objects.all()
<QuerySet [<Choice: りんご>, <Choice: みかん>]>
>>> quit()
now exiting InteractiveConsole...

Djangoの管理者サイトについて

まず、下記のように
python manage.py createsuperuser
で管理者ユーザを作る必要がある

$ python manage.py createsuperuser
ユーザー名 (leave blank to use 'user'): admin
メールアドレス: admin@example.com
Password: 
Password (again):
Superuser created successfully.

そうすると、
python manage.py runserver
で開発サーバーを起動した後に、

http://127.0.0.1:8000/admin

で管理者サイトにログインする。

http://127.0.0.1:8000/admin

で管理者サイトにアクセスできる理由は、

プロジェクト名のフォルダの直下にある
urls.py
で、

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]

のように、
path('admin/', admin.site.urls),
の記述があるため。

Djangoの管理者サイトでアプリで作成したテーブルのデータを編集するには

例として、pollsという名前のアプリの場合

mysite\polls\admin.py
で下記のようなコードを書く

from django.contrib import admin

from .models import Question, Choice

admin.site.register(Question)
admin.site.register(Choice)

この例では、モデルクラスのQuestion、Choiceを
admin.site.register(Question)
admin.site.register(Choice)
により、登録している

これで管理者サイトにログイン時に、
Question、Choiceのデータを編集できるようになる。

Djangoのorder_byは、-記号なしが昇順、-記号ありが降順

pub_dateの昇順の例

latest_question_list = Question.objects.order_by('pub_date')

pub_dateの降順の例

latest_question_list = Question.objects.order_by('-pub_date')

複数項目の並び替えの例

latest_question_list = Question.objects.order_by('-pub_date', 'title')

Djangoにて「pk取得」、「1件取得」、「複数件取得」に関すること

PKでの取得

question = Question.objects.get(pk=question_id)

なお、djangoでは、複合主キーはサポートされていない

複数項目での抽出

try:
    question = Question.objects.get(field1=value1, field2=value2, field3=value3)
except Question.DoesNotExist:
    print("一致するレコードがありません")
except Question.MultipleObjectsReturned:
    print("複数のレコードが見つかりました")

ただし、このやり方は結果が1行になることを想定しており、
複数行になるケースはエラーになる

ゼロ件時に、Noneでほしい場合は、下記のような配慮をする

def get_or_none(model, **kwargs):
    try:
        return model.objects.get(**kwargs)
    except model.DoesNotExist:
        return None

question = get_or_none(Question, field1=value1, field2=value2, field3=value3)

複数件を想定している場合は、

questions = Question.objects.filter(field1=value1, field2=value2, field3=value3)

結果がゼロ件時は、空のQuerySetが返る

# 全行取得
for question in questions:
    print(question)
# 最初の行だけを取得する:
first_question = questions.first()

# questionsが「空のQuerySet」の時、first_questionにはNoneが入ります。
個数を確認する:
count = questions.count()
print(f"一致した行数: {count}")

Djangoの「コンテキスト変数」について

from django.views import generic

をしていると
generic.ListView
generic.DetailView

views.pyにて

class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    # context_object_name = 'latest_question_list'

    def get_queryset(self):
        return Question.objects.order_by('-pub_date')[:5]

とすることで
テンプレート側で、

polls/index.html
は、

{% if object_list %}
    <ul>
    {% for question in object_list %}
        {% comment %} <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li> {% endcomment %}
        <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

にすることができる
この
「object_list」がコンテキスト変数

この例では
Question.objects.order_by('-pub_date')[:5]
であり、
Questionモデルのリストを取得しているため
「question_list」でもよい

もし、テンプレート側で使う名前を変更したければ

    context_object_name = 'latest_question_list'

をすればよい。

また、

class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'

class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'

では、
model = Questionをしていることから
このモデル名を単純に
Pythonの標準的な小文字化(str.lower())
を行った「question」、または「object」を
コンテキスト変数として用いることができる

polls/detail.html
は、

<form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    <fieldset>
        <legend><h1>{{ question.question_text }}</h1></legend>
        {% if error_message %}
            <p><strong>{{ error_message }}</strong></p>
        {% endif %}
        {% for choice in question.choice_set.all %}
            <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}"></input>
            <lavel for="choice{{ forloop.counter }}">{{ choice.choice_text }}</lavel><br>
        {% endfor %}
    </fieldset>
    <input type="submit" value="投票">
</form>

polls/results.html
は、

<h1>{{ question.question_text }}</h1>

<ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
    {% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">再投票?</a>

Djangoのurls.pyについて

urls.py

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    # ex: polls/
    path('', views.IndexView.as_view(), name='index'),
    # ex: polls/5/
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    # ex: polls/5/results/
    path('<int:pk>/results', views.ResultsView.as_view(), name='results'),
    # ex: polls/5/vote/
    path('<int:question_id>/vote', views.vote, name='vote'),
]

views.pyの

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.urls import reverse
from .models import Question, Choice
from django.views import generic

class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    # context_object_name = 'latest_question_list'

    def get_queryset(self):
        return Question.objects.order_by('-pub_date')[:5]

class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'

class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        context = {
            'question': question,
            'error_message': '選択できていません。',
        }
        return render(request, 'polls/detail.html', context)
    else:
        selected_choice.votes += 1
        selected_choice.save()

    return HttpResponseRedirect(reverse('polls:results', args=(question_id,)))

Djangoのget_object_or_404について

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist as e:
        print(f'エラー:{e}')
        raise Http404('質問が存在しません')
    
    context = {
        'question': question,
    }
    return render(request, 'polls/detail.html', context)

のような実装は、
from django.shortcuts import get_object_or_404
をしたうえで、

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    context = {
        'question': question,
    }
    return render(request, 'polls/detail.html', context)

のように書き換えることができる。

Djangoの順方向、逆方向のリレーション名の話

外部キーでのリレーションがある場合

Questionが親で
Choiceが子でQuestionのidを外部キーとして保持している状況で

子のChoiceから親のQuestionへのリレーションは順方向のリレーションで
choice.question
という形でのアクセスとなる。

親のQuestionから子のChoiceへのリレーションは逆方向のリレーションで

    {% for choice in question.choice_set.all %}
    {% endfor %}

の構文のように、
choice_set
で、「_set」が付くのである

外部キーを定義時に、下記のように「 related_name='choices' 」を指定しリレーション名を変更することもできる

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='choices')

この場合は、
question.choice_set
でなく
question.choices

ということになる。

Djangoの「 forloop.counter 」の話

        {% for choice in question.choice_set.all %}
            <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}"></input>
            <lavel for="choice{{ forloop.counter }}">{{ choice.choice_text }}</lavel><br>
        {% endfor %}

上記のように、djangoのテンプレート構文のforの中で、
{{ forloop.counter }}は、1はじまりでの、1, 2, 3, ...の形で
数値を取得できるので、上記のように、
id="choice{{ forloop.counter }}"
文字列連結してid属性値を作るような使い方が可能である。

Djangoの「 request.POST['choice'] 」の話

上記の例のradioは、name属性が"choice"のため
これをPOSTで送信時は、
request.POST['choice']
で、選択したものについての value="{{ choice.id }}"
の値が取得できる。

Djangoでのテンプレート内でのurl表記

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

のような表記よりも、

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

のような表記を使うことが推奨されている

urls.py

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    # ex: polls/
    path('', views.index, name='index'),
    # ex: polls/5/
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: polls/5/results/
    path('<int:question_id>/results', views.results, name='results'),
    # ex: polls/5/vote/
    path('<int:question_id>/vote', views.vote, name='vote'),
]

app_name = 'polls'
としたうえで、
このように、name='detail' でnameを指定し、'detail'がルーティング定義されているので
'polls:detail'
という表記にて、

{% url 'polls:detail' question.id %}

と記述できるから

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

と書けるのだよ。
ということは、言うまでもないことだろう。

pythonの文字列のフォーマットのやり方のいろいろ

$ python manage.py shell
Python 3.13.0 (tags/v3.13.0:60403a5, Oct  7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> question_id = 12
>>> t = "You're looking at question %s." % question_id
>>> t
"You're looking at question 12."
>>> type(t)
<class 'str'>
>>> t2 = f"You're looking at question {question_id}."
>>> t2
"You're looking at question 12."
>>> t3 = "You're looking at question {}.".format(question_id)
>>> t3
"You're looking at question 12."
>>> quit()
now exiting InteractiveConsole...

DjangoのVSコードでのデバッグ実行

Microsoftのプラグインの
Python Extension Pack
を入れて
Python Debugger
も入った状態にしておく
それと、これもか・・
https://marketplace.visualstudio.com/items?itemName=batisteo.vscode-django

★★ 重要 ★★
・ 直下に、「 manage.py 」があるフォルダを「Visual Studio Code」で開く
・ そこに、「 .vscode 」がある状況とする。
例)プロジェクト名「 mysite 」に、アプリ名「 polls 」がある環境で、
直下に、下記のものがある状況であるフォルダを「Visual Studio Code」で開く
.vscode フォルダ
mysite フォルダ
polls フォルダ
db.sqlite3 ファイル
manage.py ファイル

上記の状況にて、
.vscode/launch.json
.vscode/settings.json
を下記のようにする。
djangoでデバッガー働くようにするための設定値

launch.jsonは下記のようにする。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python デバッガー: Django",
            "type": "debugpy",  // 最新の推奨設定
            "request": "launch",
            "program": "${workspaceFolder}/manage.py",  // manage.py を指定
            "args": ["runserver", "127.0.0.1:8000"],   // 開発サーバーを起動
            "django": true,                            // Django 用のデバッグモード
            "console": "integratedTerminal",           // 統合ターミナルを使用
            "justMyCode": false                        // 外部コードもデバッグ可能
        }
    ]
}

settings.jsonは下記のようにする。

{
    "emmet.includeLanguages": {
        "html": "html",
        "django-html": "html",
    },
    "emmet.triggerExpansionOnTab": true,
    "htmlhint.enable": true,
    "files.associations": {
        "*.html": "django-html"
    }
}
project_root/
├── .vscode/          # Visual Studio Code の設定フォルダ(VS Codeを使っている場合)
├── mysite/           # Djangoプロジェクトの設定フォルダ(settings.py, urls.py などが含まれる)
├── polls/            # Djangoアプリケーションフォルダ(1つ以上のアプリが含まれる)
├── db.sqlite3        # SQLiteのデータベースファイル(開発中の簡易的なデータベースとして使用)
├── manage.py         # Django管理コマンドを実行するスクリプト
├── .git/             # Gitのリポジトリフォルダ(バージョン管理のメタデータが含まれる)
└── .gitignore        # Gitで追跡しないファイルやフォルダを指定する設定

のようなフォルダ構成で、.gitignore に下記のように書かれている
ような構成は一般的であるとのこと。

# Python関連の一時ファイルやキャッシュファイルを無視
__pycache__/
*.py[cod]
*$py.class

# Virtual environments
venv/
.env/
.virtualenv/
**/venv/

# IDE/エディタ関連の設定ファイル
.vscode/

# SQLiteデータベース(開発用)
db.sqlite3

# 秘密情報(環境変数や設定ファイル)
*.env
.env.local

# Migrations(必要であれば無視する場合)
*/migrations/__pycache__/
*/migrations/*.pyc
*/migrations/*.pyo

# Djangoの特定のファイル
*.log
*.pot
*.pyc
*.pyo
*.~*

# 静的ファイル(収集後のファイルを無視、ただし手動管理の静的ファイルは追跡)
staticfiles/
*.bak
*.swp

# テストやデバッグに関連するファイル
coverage.xml
*.cover
*.log

# テストの結果を無視
*.test-result

# DockerやCI/CDで利用する場合
docker-compose.override.yml
.dockerignore
  • Python関連のキャッシュファイル
    pycache/ や *.py[cod] はPythonのコンパイル済みファイルであり、追跡する必要がないため無視します。

  • 仮想環境
    仮想環境フォルダ(venv/ など)は追跡せず、環境は各開発者が独自に作成することを想定しています。

  • IDE/エディタの設定ファイル
    VS Code の設定フォルダ(.vscode/)を無視します。ただし、チーム全体で共有する必要がある設定ファイル(例: launch.json)を追跡する場合は、 .gitignore に追記して除外する必要があります。

  • SQLiteデータベース
    db.sqlite3 は開発用の簡易データベースであり、運用環境では使用しないのが一般的なので無視します。

  • 秘密情報
    APIキーやデータベース接続情報などを含む .env ファイルは無視します。このファイルは共有するべきではなく、各開発者が環境に応じてローカルに用意します。

  • 静的ファイル
    Djangoが生成する静的ファイル(staticfiles/)は、開発中に自動生成されるため無視します。ただし、手動で追加する静的ファイルは追跡します。

  • テストやデバッグに関連するファイル
    テスト結果やデバッグログファイルなどは追跡する必要がないため無視します。

  • Docker関連(使用する場合)
    Docker Composeのオーバーライドファイルや.dockerignoreなど、ローカル開発環境でのみ利用される設定ファイルを無視します。

Djangoのstaticファイルやtemplateの位置など

プロジェクト名がmysiteで、
pollsという名前のアプリのとき

*****************
プロジェクトルート
 mysite
  __init__.py
  asgi.py
  settings.py
  urls.py
  wsgi.py
 polls
  static
   polls
    images
     background.png
    style.css
  templates
   polls
    detail.html
    index.html
    results.html
 templates
 db.sqlite3
 manage.py
venv
 Include
 Lib
 Script
  activate
  ・
  ・

★★★★★★★★★
補足(その1)
★★★★★★★★★
プロジェクトルートをVSコードを開くので、
VSコードのターミナル上では、
ls ../venv/Scripts/activate
であることを確認し、
source ../venv/Scripts/activate
でvenv環境の有効かを行います。
★★★★★★★★★


★★★★★★★★★
補足(その2)
★★★★★★★★★
settings.py は、プロジェクトフォルダのmysiteの中にしかありません。
settings.py の中のBASE_DIRは、プロジェクトルートを指し示しています。
各アプリ(pollsなど)のtemplatesフォルダはDjangoが勝手に見つけます。
( これは、特に、settings.pyの指定がなくても行われる )

プロジェクトルート/templates
については、
settings.pyの TEMPLATES = [ の中のDIRSにて、
'DIRS': [BASE_DIR / 'templates'],
のように記述して認識させる必要があります。

上記の設定をしたうえで、
プロジェクトルート/templates
の下に、アプリごとにフォルダをきって、*.htmlを作成する方法もできるが
Djangoのチュートリアルでは、
polls/templates/polls/*
のように各アプリごとに、自動認識してくれるtemplatesフォルダに
さらにpollsなどのアプリ名のフォルダをきって、*.htmlを作るやり方を
推奨している。
その理由は、リユーザブルなアプリになるからだそうです。

ただ、どちらの方法でしているかは、現場によって異なりますので、
どちらの方式でも理解できる状況にしておけば、よろしいだろう。
★★★★★★★★★
*****************

のような位置関係にする
polls/static/polls/*
polls/templates/polls/*
のように、staticや、templatesの下に
アプリ名のpollsのフォルダを作るような構成にする
同一プロジェクト内に、他のアプリがあったときに、
同じファイル名のものについて、djangoがどちらを使うかわからなくなるのを
防ぐため、このようなフォルダ階層とするのが「お作法」であるとのこと。

上の例での、

style.cssを使うときは、

{% load static %}

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">

のように、最初に
{% load static %}
を書いておいて ( これを最初にやっとかないと、実行時にエラーになります )
{% static 'polls/style.css' %}
のような形で書くと、
{% static 'polls/style.css' %}
の部分は、
/static/polls/style.css
に変換される
これが、hrefに指定されて使うことができる。

style.css
は、下記のようにstyle.css自身からの相対パスで
images/background.pngにて、background.pngにアクセスできる

li a {
    color: green;
}

body {
    background: white url("images/background.png") no-repeat;
}

Djangoの「フィールドルックアップ(Field Lookup)」

項目名がpub_dateだとしたときに、
pub_date__lte のように、二重アンダースコア(__)を使ってクエリを記述する方法
を「フィールドルックアップ(Field Lookup)」という
以下に列挙する

  • 比較演算系

exact
値が完全一致している

Question.objects.filter(pub_date__exact='2024-01-01')

SQLで考えると
pub_date = '2024-01-01'

iexact
値が大文字小文字を区別せずに一致している

Question.objects.filter(name__iexact='django')

SQLで考えると
name ILIKE 'django'

contains
部分一致を調べる

Question.objects.filter(name__contains='django')

SQLで考えると
name LIKE '%django%'

icontains
部分一致 (大文字小文字を区別しない)

Question.objects.filter(name__icontains='django')

SQLで考えると
name ILIKE '%django%'

gt
より大きい (Greater Than)

Question.objects.filter(pub_date__gt='2024-01-01')

SQLで考えると
pub_date > '2024-01-01'

gte
以上 (Greater Than or Equal)

Question.objects.filter(pub_date__gte='2024-01-01')

SQLで考えると
pub_date >= '2024-01-01'

lt
より小さい (Less Than)

Question.objects.filter(pub_date__lt='2024-01-01')

SQLで考えると
pub_date < '2024-01-01'

lte
以下 (Less Than or Equal)

Question.objects.filter(pub_date__lte='2024-01-01')

SQLで考えると
pub_date <= '2024-01-01'
  • 範囲系

in
値が指定したリスト内にある

Question.objects.filter(id__in=[1, 2, 3])

SQLで考えると
id IN (1, 2, 3)

range
値が指定した範囲内にある

Question.objects.filter(pub_date__range=('2024-01-01', '2024-12-31'))

SQLで考えると
pub_date BETWEEN '2024-01-01' AND '2024-12-31'
  • Null判定

isnull
値がNULLであるかどうかを判定

Question.objects.filter(name__isnull=True)

SQLで考えると
name IS NULL
  • 正規表現系

regex
値が正規表現にマッチするか

Question.objects.filter(name__regex=r'^Django.*')

SQLで考えると
name ~ '^Django.*'

iregex
値が正規表現にマッチ (大文字小文字を区別しない)

Question.objects.filter(name__iregex=r'^django.*')

SQLで考えると
name ~* '^django.*'
  • 日付系

date
日付部分のみを比較

Question.objects.filter(pub_date__date='2024-01-01')

SQLで考えると
DATE(pub_date) = '2024-01-01'

year, month, day, week_day
年・月・日・曜日を抽出して比較

Question.objects.filter(pub_date__year=2024)
Question.objects.filter(pub_date__month=1)

SQLで考えると
EXTRACT(YEAR FROM pub_date) = 2024
EXTRACT(MONTH FROM pub_date) = 1
  • 文字列系

startswith
値が指定の文字列で始まる

Question.objects.filter(name__startswith='Django')

SQLで考えると
name LIKE 'Django%'

istartswith
値が指定の文字列で始まる (大文字小文字を区別しない)

Question.objects.filter(name__istartswith='django')

SQLで考えると
name ILIKE 'django%'

endswith
値が指定の文字列で終わる

Question.objects.filter(name__endswith='framework')

SQLで考えると
name LIKE '%framework'

iendswith
値が指定の文字列で終わる (大文字小文字を区別しない)

Question.objects.filter(name__iendswith='Framework')

SQLで考えると
name ILIKE '%Framework'

https://docs.djangoproject.com/ja/5.1/ref/models/querysets/

Djangoの「 reverse('polls:results', args=(question_id,)) 」

return HttpResponseRedirect(reverse('polls:results', args=(question_id,)))

reverse('polls:results', args=(question_id,))
の部分は、
テンプレートでの
{% url 'polls:results' question_id %}
と同じような意味で、urls.pyを通じて、urlの文字列取得をしている

なお、この例での
argsがタプル型の値を要求してます
(question_id,) は、末尾に、「,」をつけてるのは、重要です。
こうしないとpythonのタプルとして認識してくれない
※ 末尾に、「,」がないと通常の変数を単に()で囲っただけだとpythonは認識する。

Djangoの「 vote{{ choice.votes|pluralize }} 」の話

vote{{ choice.votes|pluralize }}
は、choice.votesが2以上ならば、複数形にするため「s」を補い
votesにする、そうでない場合は、なにもせず、voteとなる
それだけの話。

Djangoの「 self.assertQuerySetEqual() の途中のSは大文字だ!! 」

ハマるVSコードの入力補完にバグがあり、小文字のsのもので補完されるのでハマります

AttributeError: 'QuestionIndexViewTests' object has no attribute 'assertQuerysetEqual'. Did you mean: 'assertQuerySetEqual'?
このエラーメッセージを見たら、★★ 思い出せ!! ★★

古いバージョンのDjangoでは、小文字のsだった様子

★ この事を、わかったうえで次の読むこと ★

Djangoのテストコードの作成と実行

polls\tests.py

import datetime
from django.test import TestCase
from django.utils import timezone
from .models import Question
from django.urls import reverse


def create_question(question_text, days):
    time = timezone.now() + datetime.timedelta(days=days)
    return Question.objects.create(question_text=question_text, pub_date=time)

class QuestionIndexViewTests(TestCase):
    def test_no_questions(self):
        response = self.client.get(reverse('polls:index'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'No polls are available.')
        self.assertQuerySetEqual(response.context['latest_question_list'], [])

    def test_past_question(self):
        question = create_question(question_text='Past question.', days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerySetEqual(
            response.context['latest_question_list'],
            [question],
        )
        
    def test_future_question(self):
        create_question(question_text='Future question.', days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertContains(response, 'No polls are available.')
        self.assertQuerySetEqual(response.context['latest_question_list'], [])
    
    def test_future_question_and_past_question(self):
        question = create_question(question_text='Past question.', days=-30)
        create_question(question_text='Future question.', days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerySetEqual(
            response.context['latest_question_list'],
            [question],
        )

    def test_two_past_question(self):
        question1 = create_question(question_text='Past question.', days=-30)
        question2 = create_question(question_text='Past question.', days=-5)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerySetEqual(
            response.context['latest_question_list'],
            [question2, question1,],
        )

class QuestionDetailViewTests(TestCase):
    def test_future_question(self):
        future_question = create_question(question_text='Future question.', days=5)
        url = reverse('polls:detail', args=(future_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)
        
    def test_past_question(self):
        past_question = create_question(question_text='Past question.', days=-5)
        url = reverse('polls:detail', args=(past_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context['question'], past_question)
        self.assertContains(response, past_question.question_text)

class QuestionModelTest(TestCase):
    
    def test_was_published_recently_with_future_question(self):
        time = timezone.now() + datetime.timedelta(days=30)
        future_question = Question(pub_date=time)
        self.assertIs(future_question.was_published_recently(), False)

    def test_was_published_recently_with_old_question(self):
        time = timezone.now() - datetime.timedelta(days=1, seconds=1)
        old_question = Question(pub_date=time)
        self.assertIs(old_question.was_published_recently(), False)

    def test_was_published_recently_with_recent_question(self):
        time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
        recent_question = Question(pub_date=time)
        self.assertIs(recent_question.was_published_recently(), True)

の状況にて、
python manage.py test polls

$ python manage.py test polls
Found 10 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..........
----------------------------------------------------------------------
Ran 10 tests in 0.062s

OK
Destroying test database for alias 'default'...

Djangoの簡単なサンプル

https://github.com/takux/django-tutorial/tree/main/sec03

https://github.com/takux/django-tutorial/tree/main/sec04

https://github.com/takux/django-tutorial/tree/main/sec05

https://github.com/takux/django-tutorial/tree/main/sec06

https://github.com/takux/django-tutorial/tree/main/sec07

https://github.com/takux/django-tutorial/tree/main/sec08

https://github.com/takux/django-tutorial/tree/main/sec09

django rest frameworkの簡単なサンプル

https://github.com/ryotaohashi-codor/django-api

他にDjango用にVSコードに入れたプラグイン

https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-close-tag

https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag

https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring

https://marketplace.visualstudio.com/items?itemName=anseki.vscode-color

https://marketplace.visualstudio.com/items?itemName=batisteo.vscode-django

https://marketplace.visualstudio.com/items?itemName=ecmel.vscode-html-css

https://marketplace.visualstudio.com/items?itemName=HTMLHint.vscode-htmlhint


下記は、python用か・・・。


https://marketplace.visualstudio.com/items?itemName=donjayamanne.python-extension-pack

https://marketplace.visualstudio.com/items?itemName=KevinRose.vsc-python-indent

https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring


★追記★


https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph

https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistory

gitをVSコードで操作するに関する説明動画

https://www.youtube.com/watch?v=idxwEU930b0

https://www.youtube.com/watch?v=tkDRmfpqhR8

https://www.youtube.com/watch?v=O8FWIWTXYQw

Djangoでユーザのパスワードがわからなくなってしまったときの対処方法

djangoプロジェクトのルートディレクトリにて

python manage.py shell

でpythonのシェルを起動して
シェル内で下記を実行する

from django.contrib.auth.models import User

# 管理者ユーザー名が 'admin' の場合
user = User.objects.get(username='admin')

# 新しいパスワードを設定
user.set_password('new_password_here')

# データベースに保存
user.save()

その後、下記でシェルを終了する。

exit()

Djangoのテンプレートの*.htmlで!とTABでhtml5のテンプレートをVisualStudioCodeで入力補完するには。

F1キーを押して、上部のテキストボックスで、
Preferences: Open Workspace Settings (JSON)
を選択入力すると
settings.jsonが開くので
そこに、下記のものを指定する

{
    "emmet.includeLanguages": {
        "html": "html",
        "django-html": "html"
    },
    "emmet.triggerExpansionOnTab": true,
}

box-shadowジェネレータ

box-shadow ジェネレータ
でググるか。
https://hiroyuki-n.github.io/boxShadow_generator/
サイトにアクセスして、
つまみを調整し、
例として、

box-shadow: 0px 12px 18px 7px #777777;
border-radius: 10px;

のようなコードを、divタグに適用させるcssクラスなどに指定することで
そのdivタグで囲った領域にbox shadowのスタイルを適用することができる。

        .todoAppWrapper {
            max-width: 530px;
            margin: 0 auto;
            background-color: white;

            box-shadow: 0px 12px 18px 7px #777777;
            border-radius: 10px;
        }
    </style>
</head>
<body>
    <div class="todoAppWrapper">
        {% block content %}
        {% endblock content %}
    </div>
</body>
</html>

background グラデーションジェネレーター

background グラデーションジェネレーター
でググるか。
https://cssgradient.io/
サイトにアクセスして、
つまみを調整し、
例として、

    background: rgb(9,9,121);
    background: linear-gradient(126deg, rgba(9,9,121,1) 32%, rgba(41,78,168,0.94) 87%);

cssのclass定義の箇所に指定する

.header {
    text-align: center;
    padding: 20px 0;

    background: rgb(9,9,121);
    background: linear-gradient(126deg, rgba(9,9,121,1) 32%, rgba(41,78,168,0.94) 87%);

    border-radius: 10px 10px 0 0;
    color: white;
}

ファイルの作成日時の変更

powershellにて実行する。

ファイルの作成日時の確認

(Get-Item "foo.txt").CreationTime

2024年11月23日 22:03:15

ファイルの作成日時の変更

(Get-Item "foo.txt").CreationTime = Get-Date "2024-11-24 22:30:12"

ファイルの作成日時の(再)確認

(Get-Item "foo.txt").CreationTime

2024年11月24日 22:30:12

gitを使う前に最低限しなければならないこと

名前とメールアドレスをローカルのgitクライアントに教える

git config --global user.name "Taro Sato"
git config --global user.email taro_sato@example.com

下記のコマンドで設定ができているかを確認する

git config --global user.name

で、設定値が表示されるか

git config --global user.email

で、設定値が表示されるか
を確認する。

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
user.name について
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
リモートリポジトリについて、gitHubでコミット履歴を見た時に、
コミットしたユーザの表示名などとして、使われる。(ニックネームなどでよい)
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
user.email について
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
ここで登録したメールアドレスのgitHub上のアカウントが
開発作業を行うリモートリポジトリのgitHubのサイトから招待をうけて
アクティベーションしている状況、または、リモートリポジトリの管理者のgitHubアカウント
のいずれかである必要があります。
今使ってる開発端末が、特定のリモートリポジトリ用であれば、
「--global」で問題ないと思います。
「--global」の他の細かい指定の仕方は、詳しく知りません。
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

gitコマンドのメモ( 一番最初に、リモートリポジトリを作成するところ )

一番最初に、
ローカルにあるソースコードを、git hubに新規で登録したいときは、

  1. ローカルリポジトリのルートとしたいフォルダで、
git init

を打ち込む。
ローカルリポジトリのルートとは、直下に、
「.git」フォルダがあるフォルダ
git initの結果、mainブランチが作られ現在のブランチがmainブランチとなる

  1. リモートリポジトリをリモート名をoriginとして登録する
git remote add origin https://github.com/username/applicationName.git
https://github.com/username/applicationName.git

は、リモートリポジトリとなるurlであるが、これに対して好きな名称のリモート名をつけて、
ローカルの .git フォルダの内部情報に登録する。
好きな名称のリモート名をつけれるが、一般的には、リモート名は
originにしておくことが多い。
.git/config に

[remote "origin"]
    url = https://github.com/username/applicationName.git
    fetch = +refs/heads/*:refs/remotes/origin/*

が登録される。

その後、ステージングの追加や、コミットをしてから

3)

git push origin main

をすると、リモートにもmainブランチが作成される。

他の人は、

git clone https://github.com/username/applicationName.git

を打ち込んだら、カレントディレクトリに
applicationNameフォルダが作成される
cd applicationName
して、
ls -a
をすると、「.git 」フォルダがある
git branch
で、現在のブランチがmainであることが確認できる

git clone https://github.com/username/applicationName.git other_dir

を打ち込んだら、カレントディレクトリに
other_dirフォルダが作成される
cd other_dir
して、
ls -a
をすると、「.git 」フォルダがある
git branch
で、現在のブランチがmainであることが確認できる

gitコマンドのメモ_もろもろ

developをプルする。

git pull origin develop


現在のブランチが、feature/taro/task001 などのの状況にて、
developの内容を現在のブランチにマージする。

git merge --no-edit develop

※ --no-edit は環境によってはエディタ起動してしまうのを抑止



ステージングの状況を確認

git statu



ステージング追加だがステージングの追加、削除は、なにかのソフトでするのがよさそう
コマンドは下記のようなもの

git add .
全ての未ステージングのファイルをステージングにする

や、

git add src/foo.py
ファイルを指定して未ステージングをステージングにする

や、

git restore --staged .
全てのステージングのファイルを未ステージングにする

や、

git restore --staged src/foo.py
ファイルを指定してステージングを未ステージングにする


コミットは、

git commit -m"かくかくしかじかの対応をしました"



直前のコミットを取り消したい場合は、

git reset --soft HEAD~

git resetは他にもいろんなオプションがあるが、詳しく知らない
コミットを取り消し、直前のコミット分のファイルは、ステージングに残った状態にする
意味として「--soft」のようである。
プッシュまでしてしまったものは、このコマンドでは取り消せない。



feature/taro/task001をリモートにプッシュする。

git push origin feature/taro/task001


他、

feature/taro/task001 にローカルブランチを切り替える

git checkout feature/taro/task001


develop にローカルブランチを切り替える

git checkout develop


developブランチにいる状況で、今の、develop を基点にして、
feature/hanako/task007 のブランチを作る

git checkout -b feature/hanako/task007

上記は「 VSコードの「・・・」 -> 「ブランチ」 -> 「分岐の作成」で、ブランチ名を入力して、Enter 」か!?


トラッキングで、リモートブランチをローカルに落としてくる

git fetch --all

git checkout -t origin/feature/hanako/iikanji_no_task


プルリクでコミット履歴が複数あるときに、とあるコミット段階まで戻したい

例)
リモートのfeatureのプルリクで
コミットA
コミットB
コミットC
の順にコミット履歴がある
コミットAの状態に戻したい

コミットB
コミットC
を取り消したい

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

ステップ1)

ローカルのリポジトリで該当のブランチであるかを確認する
git branch
などで、確認。
必要があれば、git checkout featureABC などで該当のブランチにする

ステップ2)

コミットAの状態にリセット

git reset --hard abc123

( abc123は、git logで確認したコミットAのハッシュとする )

ステップ3)
リモートに強制プッシュ
(強制プッシュによりリモートの履歴が上書きされる)

git push --force

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

もしくは、

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

git revert <コミットCのハッシュ>

git revert <コミットBのハッシュ>

git push

ミットBとコミットCを打ち消す新しいコミットを追加でのやり方

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

Ubuntu(WSL2)からcmd.exeを使って*.batを「 「set」でのwindows側の環境変数を意識させる 」形で実行する方法

dosでsetを打ったときに、
PUBLIC=C:\Users\Public
の行が含まれていたときに、

C:\temp\testEnv.bat
の内容が下記であるとして

@echo off
echo %PUBLIC%

exit 10

Windows側の環境変数を意識して
C:\temp\testEnv.bat
を動く

それを、Ubuntu(WSL2)から実行している例

/temp_dir
で、
cd /mnt/c/temp && /mnt/c/Windows/System32/cmd.exe /c testEnv.bat
を打ち込んだで、その後
echo $?
をした例

$ cd /mnt/c/temp && /mnt/c/Windows/System32/cmd.exe /c testEnv.bat
C:\Users\Public
$ echo $?
10

補足)
subst D: "C:\temp\dir_d"
などのsubstで作られた仮想ドライブとしてのD:\などは、
Ubuntu(WSL2)では自動マウントしない。
( substは、windows側の仮想ドライブで、Ubuntu(WSL2)は認識しない )

Ubuntu(WSL2)は、Windows側にDドライブが無い場合でも、
/mnt/d は、空フォルダとして準備されているので
その空フォルダを使って、
一旦、手動マウントにて実験してみる。

sudo mount --bind /mnt/c/temp/dir_d /mnt/d

にてマウントする必要がある
( 物理的なDドライブなら、Ubuntu(WSL2)で自動マウントされるため、この必要はない )

cd /mnt/d && /mnt/c/Windows/System32/cmd.exe /c testEnv.bat
を打ち込んだで、その後
echo $?
をした例

$ cd /mnt/d && /mnt/c/Windows/System32/cmd.exe /c testEnv.bat
C:\Users\Public
$ echo $?
10

後から物理的なDドライブができた時にバッティングしないように
一旦、アンマウントしておく、
アンマウントするには、

sudo umount /mnt/d

を行う。
( 物理的なDドライブなら、Ubuntu(WSL2)で自動マウントされるため、この必要はない )

メモ

https://github.com/olifolkerd/tabulator/issues/4537?utm_source=chatgpt.com

Discussion