Viteを使ってVueを始めよう

Viteとは

Vite(発音:ヴィート)はVue Cliの次世代のビルドツールで軽量でデベロップメントのスピードを速くするために作られました。

今まではnpmやvue cliでVueアプリを作ってきましたが、Viteを使ってVueだけでなくReactやSvelteも構築できてしまいます。


Viteをつかってみよう

現段階ではNode.jsの12.2.0以上のバージョンが必要とされています。ではnpm、もしくはyarnが入っていることを確認してViteを使ってみましょう。

npmの場合

npm create vite@latest

yarnの場合

yarn create vite

インストールが終わったら下のコマンドでパッケージをインストールしてデベロップメント用のウェブサーバーを起動しましょう。

  cd vite-project
  npm install
  npm run dev

index.html と プロジェクトルート

通常、Vue Cliやnpmで作成したVueアプリケーションのindex.htmlはpublicフォルダに格納されますが、Viteで作った場合はindex.htmlがプロジェクトの直下に作成されます。このファイルがアプリケーションのエントリーポイントとなっているからです。

アプリケーションのビルド

Viteでアプリケーションが完成したら下記のコマンドでコードをコンパイルしましょう。

npm run build

 

[Vue入門] テンプレートシンタックス

Vue では、HTML ベースのテンプレート構文を使用します。テンプレート構文では、基盤とするコンポーネントのインスタンスのデータと、レンダリングされる DOM を宣言的にバインドすることが可能です。すべての Vue テンプレートは、仕様に準拠しているブラウザーや HTML パーサーでパースできる、構文的に正規の HTML です。

内部では、Vue はテンプレートをコンパイルし、高度に最適化された JavaScript のコードにします。リアクティビティ機構と組み合わせ、Vue はアプリの状態が変化したとき、再レンダリングを必要とする最小限のコンポーネントをインテリジェントに見つけ出すことができます。そして、最小限の DOM 操作を適用します。

仮想 DOM の各種概念をよく知っていて、生の JavaScript が持つパワーを活かしたいという場合には、テンプレートの代わりに render 関数を直接記述することもできます。さらに、オプションで JSX もサポートされています。ただし、これらの書き方をする場合には、コンパイル時の最適化がテンプレートと同等のレベルでは利用できないことに注意してください。

テキスト展開

データバインディングで最も基本の形式は、「マスタッシュ構文」(二重中括弧) によるテキスト展開です:

<span>Message: {{ msg }}</span>

マスタッシュのタグの中身は、対応するコンポーネントのインスタンスが持つ msg というプロパティの値に置き換えられます。msg プロパティが変更されるたびに、マスタッシュの中身も更新されます。

HTMLタグを含めたデータ

マスタッシュの中では、データが HTML ではなくプレーンテキストとして解釈されます。本来の HTML を出力したい場合は、次のように v-html ディレクティブを用いる必要があります:

<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

Using text interpolation: <span style=”color: red”>This should be red.</span>

Using v-html directive: This should be red.

ここで、新たな要素が登場しました。この例にある v-html という属性は、「ディレクティブ」と呼ばれるものです。ディレクティブは v- という接頭辞を持ち、Vue によって提供される特別な属性であることを示します。そしてご想像の通り、ディレクティブはレンダリングされる DOM に、特別なリアクティブな振る舞いを割り当てます。この例では、簡単に言うと、「現在アクティブなインスタンスが持つ rawHtml というプロパティをこの要素の inner HTML に適用して最新に保つ」ということが書かれています。

span の中身は rawHtml プロパティが持つ値に置き換えられ、プレーンな HTML として解釈されます。データバインディングは無視されます。v-html は、テンプレートの断片を組み立てるのには利用できないことに注意してください。これは、Vue が文字列ベースのテンプレートエンジンではないためです。それに代わり、UI の再利用や組み立ての基本単位として推奨されているのが「コンポーネント」です。

セキュリティーに関する警告

ウェブサイト上で任意の HTML を動的にレンダリングすることは、クロスサイトスクリプティング (XSS) 脆弱性を招きやすく、非常に危険です。v-html は信頼できるコンテンツにのみ使用し、ユーザーから渡されるコンテンツには決して使用しないでください。

属性バインディング

HTML 属性の中ではマスタッシュ構文が使えません。代わりに、以下の v-bind ディレクティブを使用します:

<div v-bind:id="dynamicId"></div>

この v-bind ディレクティブは、要素の id という属性を、コンポーネントが持つ dynamicId というプロパティと同期させるよう Vue に指示しています。バインドされた値が null または undefined の場合、その属性はレンダリングされる要素から除外されます。

省略記法

v-bind は使用頻度が非常に高いため、以下の専用の省略記法があります:

<div :id="dynamicId"></div>

: で始まる属性は、普通の HTML の記法とは少し異なるように見えますが、実際には属性名として有効な文字です。Vue をサポートするすべてのブラウザーは、これを正しく解析することができます。なお、これは最終的にレンダリングされるマークアップには現れません。この省略記法を使うかどうかは任意ですが、その使い方を後ほど詳しく知れば、良さがわかるはずです。

このガイドの残りの部分では、Vue を用いる開発者にとって最も一般的な書き方である省略記法をコード例のなかで使用します。

ブーリアン属性

ブーリアン属性は、要素に含まれるかどうかによって「真」または「偽」の値を表す属性です。例えば、disabled は最も一般的に用いられるブーリアン属性の 1 つです。

以下のケースでは、v-bind は少し特別な動作をします:

<button :disabled="isButtonDisabled">Button</button>

この disabled という属性は、isButtonDisabled が 真値 (truthy value) である場合に要素に含まれます。また、<button disabled=""> との一貫性を保つため、値が空の文字列である場合にも含まれます。それ以外の偽値 (falsy values) の場合には、属性が要素から取り除かれます。

複数の属性を動的にバインドさせる

次のような複数の属性を持つ JavaScript オブジェクトがあるとします:

data() {
  return {
    objectOfAttrs: {
      id: 'container',
      class: 'wrapper'
    }
  }
}

以下のように v-bind を引数なしで用いると、これらの複数の属性を 1 つの要素にバインドすることができます:

<div v-bind="objectOfAttrs"></div>

JavaScript の式を用いる

ここまで、テンプレート内のプロパティのキーに単純なバインドを行う方法だけを見てきました。しかし、実は Vue ではあらゆるデータバインディングにおいて、以下のように JavaScript 式のフルパワーを活用することができます:

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div :id="`list-${id}`"></div>

これらの式は、現在のコンポーネントインスタンスのデータスコープ内で、JavaScript の式として評価されます。

Vue のテンプレートでは、以下の場所で JavaScript の式を使用することができます:

  • テキスト展開の内部 (マスタッシュ構文内)
  • 任意の Vue ディレクティブ (v- で始まる特殊な属性) の属性値の中身

式に限られる

それぞれのバインディングには、単一の式しか含めることができません。そのため、以下はうまく動作しません:

<!-- これは文であり、式ではありません: -->
{{ var a = 1 }}

<!-- フロー制御も動作しません。代わりに三項演算子を使用してください。 -->
{{ if (ok) { return message } }}

関数の呼び出し

コンポーネントから公開されているメソッドであれば、以下のようにバインディングの式の内部で呼び出すことができます:

<span :title="toTitleDate(date)">
  {{ formatDate(date) }}
</span>

TIP

バインディングの式の内部で呼び出される関数は、コンポーネントが更新されるたびに呼び出されます。そのため、データの変更や非同期処理をトリガーするような副作用を持たせてはいけません

グローバルへのアクセスの制限

テンプレートで用いる式はサンドボックス内で実行され、限定的なグローバルのリストにのみアクセスできます。このリストには、Math や Date などのよく使われる組み込みグローバルが含まれています。

ユーザーが window に付与したプロパティなど、このリストに明示的に含まれていないグローバルには、テンプレート内の式からアクセスすることができません。ただし、app.config.globalProperties に追加することにより、Vue のあらゆる式で利用できるグローバルを明示的に定義することができます。

ディレクティブ

ディレクティブは、v- という接頭辞を持つ特別な属性です。Vue では、上で紹介した v-html や v-bind をはじめ、数々の組み込みディレクティブが用意されています。

ディレクティブの属性値は、JavaScript の単一の式であることが期待されます (ただし v-forv-onv-slot は例外であり、後ほどそれぞれのセクションで説明します)。ディレクティブの役割は、式が示す値が変化したとき、リアクティブに更新を DOM に適用することです。例えば、v-if を取り上げてみます:

<p v-if="seen">Now you see me</p>

この例では、v-if というディレクティブを用いて、式 seen が示す値の真偽に基づいて要素 <p> を削除したり挿入したりします。

引数

一部のディレクティブは引数を取ることができます。引数は、ディレクティブ名の後にコロンで示します。以下は、v-bind ディレクティブを使って HTML 属性の 1 つをリアクティブに更新する例です:

<a v-bind:href="url"> ... </a>

<!-- 省略記法 -->
<a :href="url"> ... </a>

この例では href が引数です。これにより、要素の href という属性を url という式の値にバインドするという指示が v-bind ディレクティブに伝えられます。省略記法では、引数の前に置かれる v-bind: の部分がすべて : という 1 文字に凝縮されます。

別の例として、DOM イベントをリッスンする v-on ディレクティブを紹介します:

<a v-on:click="doSomething"> ... </a>

<!-- 省略記法 -->
<a @click="doSomething"> ... </a>

この例では、リッスンするイベント名の click が引数です。ごく一部のディレクティブには省略記法の記号として @ を持つものがあり、v-on もその 1 つです。イベントのハンドリングについても、後ほど詳しく説明します。

動的引数

ディレクティブの引数を指す部分では、以下のように角括弧で囲んだ JavaScript の式を用いることもできます:

<!--
引数で使用できる式には、いくつか制約があります。詳細は以下の
「動的引数の値に関する制約」および「動的引数の構文上の制約」セクションで説明します。
-->
<a v-bind:[attributeName]="url"> ... </a>

<!-- 省略記法 -->
<a :[attributeName]="url"> ... </a>

この例では、attributeName が JavaScript の式として動的に評価され、そこで評価された値が最終的な引数を指す値として使用されます。例えば、コンポーネントのインスタンスが attributeName というデータプロパティを持ち、その値が "href" のとき、このバインディングは v-bind:href と同等になります。

同じように、動的引数を用いてハンドラーを動的なイベント名にバインドすることもできます:

<a v-on:[eventName]="doSomething"> ... </a>

<!-- 省略記法 -->
<a @[eventName]="doSomething">

この例では、eventName の値が "focus" のとき、v-on:[eventName] が v-on:focus と同等になります。

動的引数の値に関する制約

動的引数は、評価結果が null または文字列のいずれかになることが期待されます。null は特別な値で、バインディングを削除することを明示的に表します。それ以外の非文字列の値を指定すると、警告が発生します。

動的引数の構文上の制約

動的引数の式には、構文上の制約がいくつかあります。これは、スペースや引用符など特定の文字が HTML の属性名の中では無効となるためです。例えば、次のようなものは無効となります:

<!-- この場合、コンパイラーで警告が発生します。 -->
<a :['foo' + bar]="value"> ... </a>

複雑な動的引数を渡す必要がある場合は、後ほど取り上げる算出プロパティを使用するとよいでしょう。

また、HTML ファイルに直接記述する DOM 内テンプレートを使用する場合、ブラウザーでは属性名が小文字であることが求められるため、以下のように大文字のキー名を使用することは避ける必要があります:

<a :[someAttr]="value"> ... </a>

上のコードは、DOM 内テンプレートでは :[someattr] に変換されます。もしコンポーネントに someattr ではなく someAttr というプロパティしかなければ、このコードは動作しません。

修飾子

修飾子は、ドット (.) で示される特別な接頭辞で、ディレクティブを何らかの特別な方法でバインドすることを表します。例えば、以下に示す .prevent という修飾子は、イベントがトリガーされたときに event.preventDefault() を呼び出すことを v-on ディレクティブに伝えます:

<form @submit.prevent="onSubmit">...</form>

この後、v-on 向けや v-model 向けの修飾子の例を、その機能のページで見ることになるでしょう。

最後に、ディレクティブの構文の全容をこちらの図にまとめました:

VueアプリをEXEする方法

準備するもの

・Vueアプリ

・npm

イントロ

今回はVueアプリをデスクトップアプリとして使えるようにするためにElectronJSを使います。

以前もElectronJSを紹介したことがありますが、何かしらの理由でVueのクライアントアプリをネットに公開したくない場合や、ローカルのPCからAPIにアクセスできるアプリを作成したい時に最適です。

準備

では、Vueのアプリを準備しましょう。まっさらな状態から作成する場合はVue cliやViteを使ってVueアプリを作成してもらって構いません。

もし、実際のアプリケーションで参考にしてみたい場合はこちらを使ってください。

https://github.com/TraitOtaku/VueApp/tree/4_AuthReady
git clone <URL>

cd /path/to/repo/

git branch -a

git checkout 4_AuthReady
npm i

npm run serve

とりあえずアプリが起動できるところまで確認してください。

バックエンドのDjangoのAPIもこちらからどうぞ。

https://github.com/TraitOtaku/djangoAPI/tree/8_AuthReady

ElectronJSのインストール

今回はVue-Electron Builderを使ってVue CLIのコマンドでElectronをインストールしていきます。

公式サイトをチェックしましょう。

https://nklayman.github.io/vue-cli-plugin-electron-builder/

Vue CLIがない人はこのコマンドでVueCLIをインストールしましょう。

npm install -g @vue/cli

ではVueのアプリにElectronを追加します。

vue add electron-builder

デベロプメントサーバーの起動の仕方

Yarnの場合

yarn electron:serve

NPMの場合

npm run electron:serve

アプリをExe化する

Yarnの場合

yarn electron:build

NPMの場合

npm run electron:build

Vue Routerを使っている場合

Vue Routerを使っている場合は下記のような警告が出ます。

 It is detected that you are using Vue Router. It must function in hash mode to work in Electron. Learn more at https://goo.gl/GM1xZG

この場合は、router内のindex.jsのCreateWebHistoryをcreateWebHashHistoryに変えてあげましょう。

const router = createRouter({

  history: createWebHashHistory(process.env.BASE_URL),

  routes

})

もしくはこのようなやり方でもできます。

const router = createRouter({

  history: process.env.IS_ELECTRON

    ? createWebHashHistory()

    : createWebHistory(),

  routes,

});

そうするとURLがこんな感じになります。

http://localhost:8080/#/

これはSEO対策としては悪いのでEXE化するときだけに変更するように覚えておいてください。

これをしないとElectronアプリが白い画面しか表示されないエラーが発生します。

APIフェッチがビルドの時だけエラーになる

ではElectron:Buildのコマンドでログイン画面に入ろうとしてもAxiosがNetworkエラーでログインできないことがあります。

さらに、Djangoのアプリ側では200で返ってくるので謎の現象のように見えます。

これはElectronアプリのセキュリティをCORSのヘッダーのエラーにより発生しています。

まずは、backend.jsのwebSecurityをfalseにします。

※注意!

これはあくまでも検証用として記載しております。

実際にプロダクションにする場合はより安全なやり方が下記のブログで紹介されているので参考にどうぞ。

https://pratikpc.medium.com/bypassing-cors-with-electron-ab7eaf331605
const win = new BrowserWindow({

    webPreferences: {

        webSecurity: false

    }

});

これで問題なくElectronからもAxiosが使えるようになりました。

お疲れ様です。

VueRouterでログインしていないユーザーをログイン画面に誘導する方法

準備するもの

・Vue3

・Vue-Router

イントロ

今日はVue3でログインしていないユーザーをログイン画面に飛ばす方法を紹介します。

この方法以外にもこの目的を達成できる方法が色々ありますがVue-Routerを使ったやり肩が簡単だと思ったので参考にしてみてください。

ユーザーを判断するシステムを作る

Vueアプリにアクセスしたユーザーを判断するシステムがないことにはユーザを振り分けることができませんね。

今回は、ログインしていないユーザーをどうやって判断するかを紹介します。

前回紹介した記事でトークン認証を使ったログインの仕方を紹介しました。

では、これを使って、トークンのないユーザーをはじき出しましょう。

localStrageについて

今回は単純にブラウザのローカルストレージにデータを保管していますが、もし複雑なデータをあるかうようでしたらVuexというState Managementシステムでも同じことができます。

Vue Routerについて

Vue-RouterはVueのライブラリでURLのルーティング、もしくはページのコンポーネントを担当するメカニズムの事です。

Metaについて

このコードを見て分かるようにそれぞれのルートにmetaを設定することができます。

今回は一番ふさわしいrequireAutrhというキーで名前を付けましたが、adminOnlyでも、firstTimeでも何でも良いです。

で、このキーに対してtrueの値を入れてあげます。

後からこのrequireAuthがtureのルーティングに対して更に条件を足していきます。

const routes = [

  {

    path: "/",

    name: "home",

    component: HomeView,

    meta: { requiresAuth: true },

  },

  {

    path: "/login",

    name: "login",

    component: LoginView,

  },

];

beforeEachでルーティングに制限をかける

VueRouterの公式のドキュメンテーションにもありますが、このmetaに対して条件を足す場合には下記のようにコードを書いていくことになります。

パラメータではto とfromが使えますが今回はtoだけを使います。つまりは行き先だけを指定して、どこのページから来ているかのfromは使わないという事ですね。

to.meta.requiresAuthでさっきのrequireAuthがtrueであることと、 localStorage.getItemのtokenがnull、つまり、NoValueで値がない場合にどうするか指示します。

今回はバックエンドのアプリからログインのトークンをもらっていない場合はloginのページに誘導することにしています。

router.beforeEach((to) => {

  if (to.meta.requiresAuth && localStorage.getItem("token") == null) {

    return { name: "login" };

  }

});

では、今日はこれくらいで。

Django Rest Framework (DRF)でトークン認証の際にユーザー名を取得する方法

準備するもの

・Django RESTFrameworkのプロジェクト

・Python

イントロ

通常、Djangoのプロジェクトならログインしているユーザー情報をHTMLテンプレートに出力することは簡単ですね。

ではAPIでクライアント側にDjangoのユーザー情報を渡したい場合はどうすればよいでしょうか。

今日は、これを解決するためにDjango REST Frameworkのトークン認証のメソッドを上書きしていきます。

準備するもの

GitHubから完成したサンプルコードを参考にしてください。

詳しい公式ドキュメンテーションがあるのでこれも参考にしてください。

https://www.django-rest-framework.org/api-guide/authentication/

CustomAuthTokenクラスを作成する

これからアプリのViews.pyにCustomAuthTokenのクラスをコピペしてそのリスポンスに返したいデータを追加するだけです。

from rest_framework.authtoken.views import ObtainAuthToken

from rest_framework.authtoken.models import Token

from rest_framework.response import Response



class CustomAuthToken(ObtainAuthToken):



    def post(self, request, *args, **kwargs):

        serializer = self.serializer_class(data=request.data,

                                           context={'request': request})

        serializer.is_valid(raise_exception=True)

        user = serializer.validated_data['user']

        token, created = Token.objects.get_or_create(user=user)

        return Response({

            'token': token.key,

            'user_id': user.pk,

            'email': user.email

        })

urls.pyでURLも更新する

では、トークンを取得するURLも更新しましょう。

urlpatterns += [

    path('api-token-auth/', CustomAuthToken.as_view())

]

検証する

http POST http://127.0.0.1:8000/api-token-auth/ username="admin@gmail.com" password="admin"

ではコマンドラインからHTTPリクエストを送ってみましょう。

ちなみに通常のusernameの箇所にはユーザー名が入りますが、今回使用しているDjangoアプリはEmailでトークンが取得できるように変更しています。

OKっすね。

ちなみにEmailだけではなくでユーザー名も取得したい場合はこんな感じです。

        return Response({

            'token': token.key,

            'user_id': user.pk,

            'user_name': user.user_name

            # 'email': user.email

        })

お疲れ様です。

Djangoのユーザーモデルとアドミンパネルをカスタムしよう

準備するもの

・Python3

・Djangoの基礎知識

イントロ

DjangoでデフォルトでついてくるユーザーモデルはEmailと名前くらいのシンプルなものですね。

今日は、Djangoについてくるユーザーモデルにこれから使う、従業員管理に合わせてモデルを追加していきます。

今日紹介する内容はこのリンクからも公式ドキュメントで確認できます。

https://docs.djangoproject.com/en/4.0/topics/auth/customizing/

解決できる課題

Djangoのユーザーモデルに従業員の電話番号などの情報を追加できるようになる。

Djangoのアドミンパネルで検索機能を付ける。

Djangoのアドミンパネルに表示するデータを増やす。

下準備

今回は、従業員のチケット管理アプリのバックエンドを想定したサンプルコードを紹介します。

下から完成したサンプルコードをダウンロードしてください。

https://github.com/TraitOtaku/djangoAPI/tree/9_CustomUserModel

ではいつものようにgitのコマンドを使ってリポジトリをクローンしましょう。

Gitの使い方はこちらの動画で説明しています。

git clone https://github.com/TraitOtaku/djangoAPI.git

cd DjangoAPI

git branch -a

git checkout 9_CustomuserModel

バーチャル環境を設定しましょう。

pip install virtualenv

virtualenv env

env\Scripts\activate

pip install -r requirements.txt

Djangoを設定していきます。

python manage.py makemigrations

python manage.py migrate

python manage.py createsuperuser

python manage.py runserver

カスタムUserモデルを作ろう

まずは、デフォルトのDjangoのユーザーモデルをインポートして上書きしていく作業になります。

下記のモジュールをインポートしています。

from django.utils.translation import gettext_lazy as _

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager

ユーザーモデル

では、下記のモデルを見てみましょう。

AbstractBseUserからDjangoのUserモデルを引っ張ってきています。

class Member(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(_('email address'), unique=True)

    user_name = models.CharField(max_length=150, unique=True)

    first_name = models.CharField(max_length=150, blank=True)

    last_name = models.CharField(max_length=150, blank=True)

    phone = models.CharField(max_length=150, blank=True)

    about = models.TextField(_(

        'about'), max_length=500, blank=True)

    is_staff = models.BooleanField(default=False)

    is_active = models.BooleanField(default=False)



    objects = CustomAccountManager()



    USERNAME_FIELD = 'email'

    REQUIRED_FIELDS = ['user_name', 'first_name']



    def __str__(self):

        return self.user_name

USERNAME_FIELD = ’email’ でログインする際のユーザーネームの代わりにEmailとパスワードでログインできるようになります。

REQUIRED_FIELDSはユーザーを作成する際に必須な項目の事です。

objects = CustomAccountManager()でアドミンの権限のあるユーザーの設定を上書きします。

BaseUserManager

今度は、アドミン権限のあるユーザーの設定を上書きしていきます。

class CustomAccountManager(BaseUserManager):



    def create_superuser(self, email, user_name, first_name, password, **other_fields):



        other_fields.setdefault('is_staff', True)

        other_fields.setdefault('is_superuser', True)

        other_fields.setdefault('is_active', True)



        if other_fields.get('is_staff') is not True:

            raise ValueError(

                'Superuser must be assigned to is_staff=True.')

        if other_fields.get('is_superuser') is not True:

            raise ValueError(

                'Superuser must be assigned to is_superuser=True.')



        return self.create_user(email, user_name, first_name, password, **other_fields)



    def create_user(self, email, user_name, first_name, password, **other_fields):



        if not email:

            raise ValueError(_('You must provide an email address'))



        email = self.normalize_email(email)

        user = self.model(email=email, user_name=user_name,

                          first_name=first_name, **other_fields)

        user.set_password(password)

        user.save()

        return user

Settings.pyの設定

このファイルのどこかに下記のラインを追加しましょう。

これでDjangoがMemberモデルを使ってユーザー認証をするように指示できます。

AUTH_USER_MODEL = 'members.Member'

Admin.pyの設定

ではアドミンパネルの表示をカスタマイズしていきます。

from django.contrib import admin

from .models import Member, Office

from django.contrib.auth.admin import UserAdmin



class UserAdminConfig(UserAdmin):

    model = Member

    search_fields = ('email', 'user_name', 'first_name',)

    list_filter = ('email', 'user_name', 'first_name', 'is_active', 'is_staff')

    ordering = ('email',)

    list_display = ('email', 'user_name', 'first_name',

                    'is_active', 'is_staff')

    fieldsets = (

        (None, {'fields': ('email', 'user_name',

         'first_name', 'last_name', 'phone', 'password')}),

        ('Permissions', {'fields': ('is_staff', 'is_active')}),

    )

    # formfield_overrides = {

    #     NewUser.about: {'widget': Textarea(attrs={'rows': 10, 'cols': 40})},

    # }

    add_fieldsets = (

        (None, {

            'classes': ('wide',),

            'fields': ('email', 'user_name', 'first_name', 'phone', 'password1', 'password2', 'is_active', 'is_staff')}

         ),

    )



admin.site.register(Office)

admin.site.register(Member, UserAdminConfig)

見たままの感じですがこれをもとにするとアドミンパネルももっと使いやすくなりますね。

ではおつかれさまです。

Vueアプリでトークン認証のログインシステムを作る

準備するもの

・Django RESTとToken Authができている環境

・Vueのアプリ

アプリ紹介

今回は、フロントエンドがVueでバックエンドがDjangoでできているチケット管理システムのログインのシステムを作成します。

例として、従業員がお客様からの問い合わせに対してチケットを作成していくようなイメージです。

Vueがフロントエンド(クライアント側)でDjangoのAPIのエンドポイントからデータを発信しています。

しかし、このAPIが公共に垂れ流しの状態なのでURI(URL)にアクセスできる人誰でもこのデータにアクセスできてしまいます。

前回はDjango RESTFramewrokを使ってトークンAuthでこのトークンがある人だけデータにアクセスできる人だけAPIのリクエストにデータをリスポンスとして返すことができました。

今日はそれをVueのアプリから行っていきたいと思います。

アプリの準備

DjangoアプリはこちらのURLのGitHubからクローンしてください。

https://github.com/TraitOtaku/djangoAPI/tree/8_AuthReady

Vueのアプリはこちらからダウンロードしてください。

https://github.com/TraitOtaku/VueApp/tree/4_AuthReady

Gitのコマンドを使ったことがない人は面倒でも必須のスキルですので必ず覚えておくようにしましょう。

Gitのコマンドです。

git clone URL

cd /to/repo/

git branch -a

Git checkout <branch_name> 

Djangoの準備の仕方は他の動画でも説明しているので今回は飛ばします。

Vueもnpmかyarnでライブラリをインストールして準備完了です。

npm i

EventService.js

まずは、ログインする際にDjangoにユーザー名とパスワードをJSONファイルで送りつけます。この際POSTリクエストになっていることに注意しましょう。

で、正しいクレデンシャル(ログイン情報)が遅れた後にDjangoからTokenが返されます。

const loginClient = axios.create({

  baseURL: "http://127.0.0.1:8000/",

  withCredentials: false,

  headers: {

    Accept: "application/json",

    "Content-Type": "application/json",

  },

});
  submitLogin(logininfo) {

    return loginClient.post("api-token-auth/", logininfo);

  },

ちなみにapiClientというファンクションでDjangoのTokenAuthに必要なTokenをリクエストの際に送るヘッダーに付けてあげるようにしています。

const apiClient = axios.create({

  baseURL: "http://127.0.0.1:8000/api",

  headers: {

    Authorization: `Token ${localStorage.getItem('token')}`,

    "Content-Type": "application/json",

  },

});

では、このJavaScriptをエキスポートして、LoginView.vueのコンポーネントを作成します。

LoginView.vue

このページ(コンポーネント)でログインに関連するシステムを構築しています。

AntDesignのUIライブラリからコピペしたフォームと、そのサブミットボタンにクリックイベントを付けてます。

    const submitLogin = () => {

      EventService.submitLogin(toRaw(formState))

        .then((response) => {

          console.log(response.data);

          localStorage.setItem('token', response.data.token)

          router.push({ name: 'home' })



        })

        .catch((error) => {

          console.log("Error" + error);



        });

    }

で、ここで、Django から返ってきたトークンをブラウザのストレージにtokenというキーで保管しておきます。

これでログインができたらrouter.pushでホーム画面にジャンプするように設定しています。

本来なら、ログインしない限りhomeのページへのアクセスはできないようにするのがベストですが、今回は割愛しています。

もしするのであればv-ifとvuexでState managementを使うのがベストだと思います。

また、ログインのエラーがあった場合のロジックもここに追加できますね、。

では、ログインができたらHomeViewに行くのでそこにログアウトのボタンも作成しましょう。

HomeViews.vue

ログアウトのロジックは下記のようなコードでできます。

単純にlocalStorageに保管されているtokenアイテムを取り除き、router.pushのコマンドでログイン画面に誘導してあげます。

    const logOut = () => {

      localStorage.removeItem('token')

      router.push({ name: 'login' })

    }

まとめ

まあ、全体的に煩雑なコードですが、これでVueアプリからDjangoのToken AuthのAPIにアクセスすることができました。

バックエンドのテクノロジーとAuth(認証)のやり方によって、ヘッダーの書き方が変わることに注意しましょう。

あとはサインアップと、パスワードを忘れた際の再設定のロジックもDjango側と一緒に作成していけばプロダクションで使えそうですね。

お疲れ様です。

Django – ネスト化されたAPI

準備するもの

・Django REST Frameworkのコード

チケットシステムの例

今日はこのチケット管理システムもモデルを元に説明します。

Djangoのアプリという概念で、このDjnagoプロジェクトに下記の3つのアプリが設定されております。

  • Customers
  • Members
  • Tickets

では、それぞれもモデルを見ると、アプリに必要なデータベースのモデルがあることが分かりますね。

Foregin KeyがPKで表示される

通常、ViewSetやAPIViewで作成したForeignKeyの項目はそのキーのPK(プライマリーキー)で表示されます。これはデータベースを見ると分かりますが、DB自体がこの外部キーをPKとして保管しているからです。

これだとせっかくAPIにしてもクライアント側のアプリでは使い物になりませんね。

StringRelatedFieldの使い方

その問題を簡単に解決できるのがStringRelatedFieldです。

これはモデルで設定した__Str__の値を返してくれます。

    def __str__(self):

        return self.inquiry

ただしこの問題は、これらのStringRelatedFieldを使うとReadOnlyのAPIになり、いざユーザー側からPOST(データの投稿)をしたい場合にエラーが発生します。

DjangoRESTのエンドポイントのHTMLフォームからも項目が削除されたことでも確認できますね。

ネスト化されたAPIモデル

ほかに、depthメタを使用してforeign keyの一階層下まで参照することができます。

この画像のようにモデルをシリアル化する際にforeign Keyでリファレンス(参照)しているキーをどの階層まで出力されるか設定することができます。

これも便利ですがReadOnlyです。

    class Meta:

        model = Ticket

        fields = '__all__'

        # depth = 1

APIでForeignKeyを受け付ける方法

Django RESTフレームワークでも、外部キー(Foregin Key)の扱いは我々デベロッパーに任されており、魔法のようなソリューションはありません。と記載されております。

色々試した結果、このオープンソースのライブラリが一番簡単な解決方法だと思ったので紹介します。

このライブラリはBeda Sotwareというロシアの医療ソフト開発会社が提供しているようですね。

https://github.com/beda-software/drf-writable-nested

ではこのライブラリをインストールしましょう。

pip install drf-writable-nested

その後に単純にシリアライザーのパラメーターを下のように変えるだけです。

class TicketSerializer(WritableNestedModelSerializer):

    # category = CategorySerializer()

    # staff = MemberSerializer()

    # store = StoreSerializer()

で、それぞれのシリアライザーをインポートすればCRUDレディーのAPIが完成です。

実際に試してください。

お疲れ様です。

Vue + Axios | APIでビットコインの値段を取得しよう

準備するもの

・なにかしらのAPI

・Node JS, npm

イントロ

VueではAPIのハンドリングはJavaScriptのライブラリに全て任せており、特にビルトインのメソッド等はありません。理由としてはJavaScriptで既にAPIを扱うライブラリが多く存在しているからです。

Axiosとは

まずは公式サイトを見てみましょう。

https://axios-http.com/

あとGitHubのリポも見てみます。

https://github.com/axios/axios

  1. AxiosはAPIのエンドポイント(URL)にHTTPのリクエストを送る為のライブラリです。
  2. そのリクエストから返ってきたdataプロパティを使えるようになります。
  3. リクエストが成功した場合のステータスは200で、ステータステキストはOKです。
  4. 通常はAxiosがJSONデータに自動で変換されます。
  5. タイムアウト設定やリクエストのキャンセルの設定もできます。

Axiosのインストール

では、VueのアプリにAxiosをインストールしてきます。別にVueでなくてもReactでも要領は同じです。

コマンドライン

npm install axios

基本的なAxiosの使い方

ここではAxiosのベース的なイメージを紹介します。

スクリプト側

new Vue({

  el: '#app',

  data () {

    return {

      info: null

    }

  },

  mounted () {

    axios

      .get('https://api.coindesk.com/v1/bpi/currentprice.json')

      .then(response => (this.info = response))

  }

})

HTML側

<div id="app">

  {{ info }}

</div>

このように、infoという、空のオブジェクトをHTML側に投げておいて、AxiosがAPIで取得したデータをのっけるようなイメージです。

mounted() メソッドはVueのライフサイクルの一部で、Vueアプリが作成されて、id=”app”にマウントされたときに実行されるように指示しています。

おまけ

ライフサイクルの流れ(例はOptionsAPIですが、感覚は同じです)

Axiosの慣習的な使い方

では、コンポーネントごとにAxiosのリクエストのコードを書くと、煩雑になるので、ベースとなるJSファイルを作成し、そこから呼び出すようにしましょう。

まずは、src直下にpluginsというディレクトリを作成しましょう。(フォルダ名は何でもよいです。)その下にEventService.jsファイルを作成します。(これも名前は何でも良いです。)

EventService.js

import axios from 'axios'



const apiClient = axios.create({

  baseURL: 'https://api.coindesk.com/v1/bpi/currentprice.json',

  withCredentials: false,

  headers: {

    Accept: 'application/json',

    'Content-Type': 'application/json'

  }

})



export default {

  getCurrentPrice(){

    return apiClient.get('')

  },

}

では、このEventService.jsをコンポーネントから呼び出してみます。

src/components直下にCoinPrice.vueを作成します。

CoinPrice.vue

<template>

<h1>Coin Price</h1>

  <div>

    {{ coins }}

  </div>

</template>



<script>

import EventService from "../plugins/EventService.js";



export default {

  components: {},

  data() {

    return {

      coins: [],

    };

  },

  created() {

    EventService.getCurrentPrice()

      .then((response) => {

        this.coins = response.data;

      })

      .catch((error) => {

        console.log("There was an error:" + error.response);

      });

  },

};

</script>

では、これをApp.cueからレンダーします。

App.vue

<template>

  <CoinPrice />

</template>



<script>

import CoinPrice from "./components/CoinPrice.vue";

</script>

で、デベサーバーを起動すると。。。

問題なくAPIが使えるようになりました。

なので、今後もAPIのエンドポイントが増えた場合はこのEventService.jsにベースとなるコマンドを記載して、必要なコンポーネントから呼び出していけるようになりますね。

HTMLをキレイに

では、必要な情報だけをフィルターしていきましょう。

<template>

  <a-card size="small" title="ビットコインの値段" class="card" style="width:300px;">

    <div>{{ coins.time.updated }} 現在</div>

    <div>{{ coins.bpi.USD.rate }} USD</div>

    <a-button

      type="primary"

      href="https://api.coindesk.com/v1/bpi/currentprice.json"

      >APIデータを直で見る</a-button

    >

  </a-card>

</template>



<script>

import EventService from "../plugins/EventService.js";



export default {

  components: {},

  data() {

    return {

      coins: [],

    };

  },

  created() {

    EventService.getCurrentPrice()

      .then((response) => {

        this.coins = response.data;

        console.log(this.coins);

      })

      .catch((error) => {

        console.log("There was an error:" + error.response);

      });

  },

};

</script>

今回はUIライブラリのant Desginを使っていますがなくてもOKです。

でhじゃ、お疲れさまでした。

Vite + AntDesign でVue3を始めよう | VueのCSSライブラリ

イントロ

皆さん、CSSライブラリは使ったことがありますか?

もちろん、自身で全てのUIデザインを一から作ることができれば素晴らしいのですが、そんな時間はありません!

CSSライブラリで有名なのはBootstrapとかTailwindがありますね。

今日は、Vueのバージョン3に対応したAnt Designを使ってみます。

Vue3に対応していないCSSライブラリ

ここで2つ注目したいCSSライブラリを紹介しておきます。

これらはまだVue3に対応していないので今後のリリースが待ち遠しいですね。

Ant Designとは

Ant DesignはReactのUIデザインのライブラリを提供していることでも知られているCSSライブラリです。

特にVueに対応したライブラリは通常のBootstrapとかと違い、データの扱いが少し楽になっているので今日、それを試してみたいと思います。

公式サイト

https://www.antdv.com/docs/vue/introduce/

Ant Designのインストール

今日はWebデベロッパーから満足度の高いViteを使って環境を作ります。

ViteはVue Cliに代わるデベロップメントツールのことです。

特にBabelというコンパイラを除いたことでめちゃちゃ早くなりました。

その代わりに古いブラウザでのサポートができなくなるとかのデメリットもありますが、それはいたしかたないでしょう。

まずはVueアプリを作ります。

npm create vite@latest

では初期状態のVueアプリが起動できることを確認しましょう。

それから、Ant Designのライブラリをインストールします。

npm install ant-design-vue@next --save

では、後からエラーが出るのでこれも入れておきましょう。

エラーが見たい人は出たら入れてください。

npm i vite-plugin-components -D
http://localhost:3000/
npm run dev

ライブラリのインポート

ではmain.jsからライブラリをグローバルにインポートします。

コンポーネントからインポートしたい人はそのやり方も可能です。

import { DatePicker } from "ant-design-vue";

app.use(DatePicker);

CSSライブラリもインポートしましょう。

カレンダーをインポートしたい場合。

import DatePicker from "ant-design-vue/lib/date-picker"; // for js

import "ant-design-vue/lib/date-picker/style/css"; // for css

Viteのデベツールのコンフィグファイルを更新して使えるようになります。

import ViteComponents, { AntDesignVueResolver } from 'vite-plugin-components';



export default {

  plugins: [

    /* ... */

    ViteComponents({

      customComponentResolvers: [AntDesignVueResolver()],

    }),

  ],

};