個人開発記録　2024/05/06(djangoでログイン機能を実装)

## 目的

* とにかくやりきることが一番の目的
* 毎日発信することで、発信する習慣作り
* 個人開発をやりきることで、実績作り


## 開発するもの

X(旧Twitter)のパクリ

## 構成

* バックエンド
    * python
    * django REST framework
* DB
    * postgreSQL
* フロントエンド
    * React

## 本編

今回はログイン画面を作成していきたいと思います。

### バックエンド

以下を参考にログインのバックエンド処理を組んでいきます。

https://qiita.com/ryo-keima/items/f9766572cd4843747e24#%E3%82%A2%E3%83%97%E3%83%AA%E4%BD%9C%E6%88%90

#### モデルの修正

まずはmodelを修正。
変更点としては`create_auth_token`メソッドを追加。
`@receiver(post_save, sender=settings.AUTH_USER_MODEL)`をつけることで、User保存後にTokenを生成するようにした。

```python:likeX/user/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser, PermissionsMixin
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

from likeX import settings


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    """
    ユーザーが保存された後にトークンを作成する
    """
    if created and instance is not None:
        Token.objects.create(user=instance)


class User(AbstractUser, PermissionsMixin):
    bio = models.CharField(max_length=280, blank=True)
    location = models.CharField(max_length=100, blank=True)
    birth_date = models.DateField(null=True, blank=True)

```

#### ログインURLを追加

`views.obtain_auth_token`と紐づけたいURLを追加する。
`views.obtain_auth_token`は`Django REST Framework`が用意しているログイン用のview。
これを使うことによって、Tokenの登録など良しなにやってくれる。

```python:likeX/user/urls.py
from django.urls import re_path, path
from rest_framework.authtoken import views

from .views import UserRegister

urlpatterns = [
    re_path(r'^register/$', UserRegister.as_view()),
    path('login/', views.obtain_auth_token),
]

```

### フロントエンド

#### LoginFormを作成

`/src/auth/components/LoginForm.js`を作成。
マテリアルUIからテンプレートをパクってくる。

https://github.com/mui/material-ui/blob/v5.15.15/docs/data/material/getting-started/templates/sign-in/SignIn.js

#### routesを作成

```js:src/auth/routes/Login.jsx
import { LoginForm } from "../components/LoginForm";

export const Login = () => {

    return (
        <>
            <LoginForm />
        </>
    );
};
```

#### AuthRoutesにLoginを追加

```js:src/auth/routes/index.jsx
import { Route, Routes } from 'react-router-dom';
import { Register } from './Register';
import { Login } from './Login';

export const AuthRoutes = () => {
  return (
    <Routes>
      <Route path="register" element={<Register />} />
      <Route path="login" element={<Login />} />
    </Routes>
  );
};
```

#### ログイン画面を表示

表示は完了。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2263720/b52895ea-4ba7-a0c1-a500-12a428b6e4b4.png)

#### APIを作成

```js:src/auth/api/login.js
import axios from 'axios';
import { API_URL } from '../../../config';

export const login = (data) => {
    const jsonData = JSON.stringify(data);
    const config = {
        headers: {
            'Content-Type': 'application/json'
        }
    };
    const url = `${API_URL}/api/user/login/`;
    return axios.post(url, jsonData, config)
};
```

LoginFormの`handleSubmit`を以下の通り修正。
`username`を`email`からとっているのは、おいおい修正。。

```js:/src/auth/components/LoginForm.js
  const handleSubmit = (event) => {
    event.preventDefault();
    const data = new FormData(event.currentTarget);

    const loginData = {
      username: data.get('email'),
      password: data.get('password'),
    };

    // API 関数を使用してデータを送信
    login(loginData)
      .then((response) => {
        console.log('Registration successful:', response);
        // ここに成功時の処理を書く、例えばログインページへリダイレクト等
      })
      .catch((error) => {
        console.error('Registration failed:', error);
        // ここにエラー処理を書く、例えばエラーメッセージの表示等
      });
  };
```

### 動作確認

#### 会員登録

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2263720/24105997-2b61-a17a-b181-11edd19a9426.png)

こんな感じでuserとtokenが作成される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2263720/bbf5dea9-dcf5-6314-1eb4-e15f05b9ef0c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2263720/ede17514-5ca7-5a72-14a2-80e9f5cab4bc.png)

### ログイン

先ほど作成された会員でログインする。
ログインする前に、トークンを削除しておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2263720/4c6a074e-b0e3-5bc4-6cc9-a670fa31a382.png)

先ほどとは違う、Tokenが作成されていることを確認。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2263720/69b91745-4635-ba81-453d-2e38e015dbed.png)

## 感想

簡単にログイン機能が作成できるのはさすがdjangoって思った。
だが、spring bootとかもそうだけど、カスタマイズがどれだけ簡単にできるかが、フレームワークとしての質が問われると思う。
実際の業務では、フレームワーク作成者の想定より複雑だったりするので。。
明日は、ログイン機能のカスタマイズ練習として、会員登録、ログインの必要項目をメールアドレスにしたい。