【Django】DBを使用せずに、csvで認証・アカウント管理を行う

Djangoを使用して認証機能を組み込む際は、基本的にデータベースにアカウント情報を保存し、認証機能を実現していると思います。

本記事では、データベースを使用せずに、csvでユーザー名・パスワードを一元管理し、認証機能を実装する方法をご紹介いたします。

Djangoに関する基本的な知識やアーキテクチャに関しては割愛しておりますこと、ご了承願います。

目次

完成イメージ

このように、csvでアカウントを管理していきます。

今回は「accounts.csv」という名前でローカルに保存します。

本記事では開発環境での解説ですが、本番運用時には、S3やGCSなどに保存して、読み取る形になるのかなと思います。

実際のアプリケーション上はこんな感じです。

トップページには「ログインページ」「ダッシュボード」へのアクセスリンクがあります。

ログインしていない状態でダッシュボードにアクセスしようとすると、強制的にログインページにリダイレクトされます。

記事が冗長化してしまうため、各ページの内容は簡略化し、cssも一切設定しておりません。

準備

プロジェクト、アプリの作成

まずは、Djangoプロジェクトの作成を行います。

下記の記事を参考に、

  • 3. 仮想環境の構築
  • 4. Djangoプロジェクトの作成
  • 5. Djangoアプリケーションを作成する

までを行います。

プロジェクト名、アプリ名はなんでも構いません。

今回は下記のようなプロジェクト、アプリケーションを作成したという前提で進めていきます。

  • プロジェクト名:test_project
  • アプリケーション名:csv_auth_app

ルーティング

まずはプロジェクトルーティングを設定します。

from django.contrib import admin
from django.urls import path, include

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

続いてアプリケーション側でのルーティングです。

トップページ、ダッシュボード、ログインページへのルーティングを作成します。

from django.urls import path
from . import views

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'), 
    path('dashboad/', views.DashboadView.as_view(), name='dashboad'), 
    path('login/', views.CSVLoginView.as_view(), name='login'), 
]

テンプレートの作成

下記の3種類のテンプレートを用意します。

  • トップページ
  • ダッシュボード
  • ログイン画面

誰でもトップページにアクセスできますが、ログインしてない状態でダッシュボードに移動しようとすると、強制的にログイン画面にリダイレクトするよう実装していきます。

本題とは関係ないので、サクサクいきます。

「csv_auth_app/templates/」配下に、下記の4つのテンプレートを作成します。

ベース
{% load static %}

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{% block title %}{% endblock title %}</title>
        <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">

        {% block head %}{% endblock head %}
    </head>
    <body>
        {% block contents %}{% endblock contents %}
    </body>
</html>
トップページ
{% extends 'base.html' %}

{% load static %}

{% block title %}TOP{% endblock title %}

{% block contents %}

<div>
    <a href="{% url 'login' %}">login</a>
    <a href="{% url 'dashboad' %}">dashboad</a>
</div>

{% endblock contents %}
ダッシュボード
{% extends 'base.html' %}

{% load static %}

{% block title %}DASHBOAD{% endblock title %}

{% block contents %}

<div>
    <h1>Dashboad</h1>
</div>

{% endblock contents %}
ログイン画面
{% extends 'base.html' %}

{% load static %}

{% block title %}LOGIN{% endblock title %}

{% block contents %}

<div>
    <h1>Login</h1>
</div>

{% endblock contents %}

準備は以上です。

csv認証を実装

ここから実際に、csvのアカウントデータによる認証機能を実装していきます。

forms.py

まずはフォームの設定です。

from django import forms

class CSVAuthForm(forms.Form):
    username = forms.CharField(max_length=254)
    password = forms.CharField(widget=forms.PasswordInput)

フォーム項目、すなわちログインに必要な項目はユーザー名とパスワードです。

さらに、ログインテンプレートにフォームを追加します。

{% extends 'base.html' %}

{% load static %}

{% block title %}LOGIN{% endblock title %}

{% block contents %}

<div>
    <h1>Login</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Login</button>
    </form>
    {% if form.errors %}
        <p class="error">ユーザー名またはパスワードが正しくありません。</p>
    {% endif %}
</div>

{% endblock contents %}

models.py

続いて、カスタムモデルの設定です。

import csv


class Account:
    def __init__(self, username, password, level):
        self.username = username
        self.password = password
        self.level = level

class CSVAccountManager:
    def __init__(self, csv_file):
        self.csv_file = csv_file

    def get_account(self, username):
        """
        csvを読み込み、その中に "username" に該当するレコードがあれば Account オブジェクトを返す。
        なければ None を返す。
        """
        with open(self.csv_file, 'r') as file:
            reader = csv.DictReader(file)
            for row in reader:
                if row['username'] == username:
                    return Account(
                        username=row['username'],
                        password=row['password'],
                        level=row['level']
                    )
        return None

    def validate_account(self, username, password):
        """
        1. 入力したユーザー名に対応したアカウント情報がcsvに存在すれば、Accountオブジェクトが返る。
        2. 入力したパスワードと、csvのパスワードが一致すれば、Accountオブジェクトをそのまま返す。なければ None を返す。
        """
        account = self.get_account(username)
        if account and account.password == password:
            return account
        return None

views.py

さらにビューの設定です。

from django.views import generic, View
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect
from django.conf import settings

from .forms import CSVAuthForm
from .models import CSVAccountManager

class IndexView(generic.TemplateView):
    """ トップページのビュー """

    template_name = 'index.html'

class CSVLoginView(View):
    """ ログインページのビュー """

    template_name = 'login.html'
    form_class = CSVAuthForm

    def get(self, request):
        # ログイン情報初期化 → ログインページの表示
        self.request.session['is_authenticated'] = False
        form = self.form_class()
        return render(request, self.template_name, {'form': form})

    def post(self, request):
        form = self.form_class(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']

            # ローカルのプロジェクト直下に「accounts.csv」を配置している前提
            account_manager = CSVAccountManager(f'{settings.BASE_DIR}/accounts.csv')
            account = account_manager.validate_account(username, password)

            # accountがあれば、ログイン情報付与 → トップページにリダイレクト
            if account:
                self.request.session['is_authenticated'] = True
                return HttpResponseRedirect('/')
            else:
                form.add_error(None, 'Invalid username or password.')
        return render(request, self.template_name, {'form': form})

class DashboadView(generic.TemplateView):
    """ ダッシュボードのビュー """

    template_name = 'dashboad.html'

    def get(self, request):
        # 認証情報が通ってれば、ダッシュボード表示
        # 通っていない場合は、ログインページにリダイレクト
        if 'is_authenticated' in self.request.session:
            is_authenticated = self.request.session['is_authenticated']

            if is_authenticated == False:
                return HttpResponseRedirect('/login/')
            else:
                return render(request, self.template_name)
        else:
            return redirect('login')

今回はセッションデータを使用して、ログインできているか否かを判断しています。

また、ログインしていないとダッシュボードにはアクセスできない設定なので、ダッシュボードにアクセスした際にはセッションデータをチェックし、認証情報が通っていなければログインページにリダイレクトさせています。

ログイン時には、accounts.csv内に、入力した「ユーザー名」「パスワード」の組み合わせのレコードがあれば、認証情報をセッションデータに付与します。

settings.py

最後にsettings.pyを編集します。

セッションデータ

セッションデータを有効にするために、

  • INSTALED_APPS
  • MIDDLEWARE

の中に下記のような設定があることを確認します。

INSTALLED_APPS = [
    # ... 略 ...
    'django.contrib.sessions',
    # ... 略 ...
]

MIDDLEWARE = [
    # ... 略 ...
    'django.contrib.sessions.middleware.SessionMiddleware',
    # ... 略 ...
]

さらに、セッションデータの保存場所を設定します。今回はキャッシュに保存します。

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'

セッションをどこに保存できるかなどは下記の外部サイトに詳しく紹介されておりますので、引用させていただきます。

データベースの設定

今回はデータベースを使用しないので、下記のようにDATABASESの値を設定します。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.dummy',
    }
}

以上です!

まとめ

csvデータを使用して、認証機能を実装する方法をご紹介させていただきました。

クラウドを使用したデータベースの維持費が高いので、デフォルトの方法以外で認証機能を実装できないかと思案したのが事の発端で、備忘録も兼ねています。何か改善点や他の良い方法などございましたら、ぜひご教示いただけると幸いでございます、、、。

ご覧いただきありがとうございました。

案件、ありますか?

「メインの仕事があるけれど、週1、2日だけできる仕事ないかな、、、」

「ある程度スキルが身に付いてはきたけど、そのスキルを活用できる場が欲しい」

なんて悩みが以前はありました。

自分で仕事を探しに行くのも大事ですが、蛇の道は蛇。その道の人に頼むことで、自分だけでは見つからないような案件に携わることができます。

IT PRO パートナーズでは、簡単に無料でアカウントを登録でき、さらにはエージェントさんに希望の働き方・案件の種類を提示することでお仕事を紹介してくれます!

登録自体も非常に簡単で、「エージェントさんとの面談を希望する」という欄にチェックをするだけで、エージェントさんから直接連絡をいただくことができます。

驚くほど簡単で正直拍子抜けしてしまいました笑

もしお仕事探しに困っておりましたら、一度登録し案件を眺めてみることをおすすめします!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

勤めていた設計会社を退社し、フリーランスとして活動しています
また、趣味で主にpyhonを用いて機械学習を行なっています!
現在競艇の予測モデルの開発中です。

コメント

コメントする

目次