0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Djangoカスタムユーザーモデル作成&管理者サイトログイン(編集中)

Last updated at Posted at 2022-10-10

はじめに

こちらの記事の続きになります。

Django管理サイトとは

Djangoには標準で管理サイトが用意されていて、接続しているDBを参照・編集ができたりします。
私的には、この管理サイトが標準で用意されているところがDjangoの良いところだと思ってます。

ルーティング設定ファイル

urls.pyはブラウザから送られるHTTPリクエストに対して、どのページを表示するかというルーティングを記載するファイル。
設定ディレクトリや、アプリケーションディレクトリに作成します。
※設定ディレクトリのurls.pyは自動で作成されますが、アプリケーションディレクトリのurls.pyは手動で作成してあげる必要があります。

(参考)最初にアクセスされるurls.py

settings.pyに書かれています。
以下の例の場合ですと、設定ディレクトリ(config)内のurls.pyを最初にアクセスするという記述になります。
つまり、このurls.pyに記載されているルーティングが優先されます。

settings.py
ROOT_URLCONF = 'config.urls'

ルーティング設定ファイルの確認

設定ディレクトリに作られるルーティングファイルurls.pyは以下のようになっています。(コメントアウトは省いてます)
注目すべきは、urlpatternsの部分。
http://{FQDN}:{port}/adminにアクセスがあったときに、admin.site.urls(Django管理サイト)を参照するようなルーティングが書かれています。
※初期状態だとDjango管理サイトの設定をしてないのでブラウザでURLを叩いても何も表示されません。

config/urls.py
from django.contrib import admin
from django.urls import path

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

アプリケーション作成

Djangoで利用するアプリケーションを作成する。
以下のコマンドでアプリ用のテンプレ構成が作成される。(作られたばかりだと中身スカスカ)
appは任意のアプリケーション名を設定。
アプリケーションの中にユーザーの設定を書いていく。

コンテナ
# python manage.py startapp app

settings.pyに作成したアプリケーションを追記する。
AUTH_USER_MODELの設定も追加。認証用のユーザーモデルには、これから作成するCustomUserを利用するよ、という記述をしておきます。

settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
+     'app',
+ 
+ AUTH_USER_MODEL = 'app.CustomUser'

カスタムユーザーモデルを作成

Djangoのユーザーモデルには3種類ある。
今回は、AbstractBaseUserを使って作成する。

種類 カラム操作 特徴
User 不可 Django標準モデル。Model作成後にカラム追加ができないため、標準の状態で使う。
AbstractUser 追加・変更のみ カラム追加や変更は可能だが、削除ができないので、使う予定のないカラムが入ってしまう。
AbstractBaseUser 追加・変更・削除 オリジナルのテーブルが作成可能。好きにカスタマイズできるのはこれ。

参考

  • AbstractUserモデルは、AbstractBaseUserモデルとPermissionsMixinを継承している。
  • Userモデルは、AbstractUserモデルを継承している。

どのように継承されているかは、こちらを参照。

models.py

./app/models.py

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

設定 説明
AbstractBaseUser AbstractBaseUserを継承してカスタマイズ可能なユーザーモデルを作成する。
BaseUserManager BaseUserManagerを継承してCustomUserManagerクラスを作っている。
PermissionsMixin AbstractBaseUserはパーミッション(権限)関連の機能を持っていないため、パーミッションの機能を利用したい場合、PermissionsMixinを同時に継承しておく必要がある。

継承元のコードはこちらから参照できる。

create_user,create_superuser

create_userや、create_superuserの作り方は、UserManagerクラスのcreate_usercreate_superuserメソッドを参考にしている。

from django.utils.translation import gettext_lazy as _

多言語対応(翻訳)させるときに使う。させない場合は不要。
as _としており、_('ユーザー名は必須です。')ここで使われている。
なぜアンダーバーなのかというと、Pythonの標準ジュールであるgettextの慣習的なものなのだそう。
これを使う場合は、settings.pyUSE_I18N = Trueの設定が必要。

models.py(抜粋)
if not username:
  raise ValueError(_('ユーザー名は必須です。'))

django.utils.translationには、gettextgettext_lazyがあるのだが翻訳が実行されるタイミングが違うらしい。

継承元のコードはこちら。

こちらのAbstractUserを参考にすると、Metaクラスは以下のようになります。
https://github.com/django/django/blob/main/django/contrib/auth/models.py

class Meta:
      verbose_name = 'user'
      verbose_name_plural = 'users'
      abstract = True    # これは削除する

abstract = Trueは削除してください。python manage.py make migrationsしたときにエラーになります。
以下のようなエラーがでます。
django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'account.CustomUser' that has not been installed
抽象モデル(クラス)とみなされて実体が作成されず、エラーになるようです。

(参考)こちらのサイトを参考にさせていただきました
https://daeudaeu.com/django-abstractbaseuser/

管理者ページを作成する

admin.pyに以下の記述をします。
このファイルはDjango管理サイトに表示させる内容を記述するファイルになります。
fieldsetsおよびfieldsは、リスト型もしくはタプル型で記述します。

./account/admin.py
from django.contrib.auth.admin import UserAdmin
from django.contrib import admin
from .models import CustomUser

@admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin):

    fieldsets = (
        (None, {"fields": ("username", "password")}),
        ("Personal info", {"fields": ("email",)}),
        ("Permissions", {"fields": ("is_active", "is_staff", "is_admin", "groups", "user_permissions")}),
        ("Important dates", {"fields": ("last_login", "date_joined")}),
    )

    list_display = ("username", 'email', "last_login", 'is_staff')
    search_fields = ("username", "email")
    filter_horizontal = ("groups", "user_permissions")

DBに反映させる

DBに反映させる内容を作成します。

# python manage.py makemigrations

makemigrationsの最後のsをつけ忘れやすいので注意しましょう。
No changes detectedとか、エラーが出てなさそうな表示がされればOK。

DBに反映させます。

# python manage.py migrate

エラーがでなければOK。

すでにUserモデルを作成している場合、カスタムユーザー作成しようとするとエラーになります。
最初から作り直した方が楽と言われるくらい面倒くさいようなので、DBの中身を作り込む前に、カスタムユーザーモデルだけ作成しておきましょう。

Userモデルを作り直す場合、面倒くさい手順を踏みます。

  1. DBの削除(db.sqlite3の場合はファイルごと削除)

  2. DB新規作成
    (参考)sqlite3のDB作成方法はこちらにまとめてます。
    https://qiita.com/Nats72/items/4a420d7a54a0f67aa0cd

  3. アプリケーションディレクトリにあるmigrationsディレクトリの中の__init__.py以外を削除
    ※誤ってmigrationsディレクトリをまるごと削除してしまったら、migrationsディレクトリを作成し、その中に、__init__.pyを空ファイルで作成します。

  4. 再度マイグレーションします。
    ※このとき、./config/settings.pyや、./config/urls.pyのadminに関する部分をコメントアウトしておきます。

./config/settings.py
INSTALLED_APPS = [
    # 'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'account',
]
./config/urls.py
# from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    # path('admin/', admin.site.urls),
    path('', include('account.urls')),
]
  1. コメントアウトを外して、再度python manage.py migrateします。

管理者ユーザーの作成

python manage.py createsuperuserで作成します。
ユーザーを作成しておかないと、後述するDjango管理サイトにログインできないためです。

# python manage.py createsuperuser
ユーザー名: admin
Eメールアドレス: hogehoge@mail.com
Password: 
Password (again): 
Superuser created successfully.

Django管理サイトにアクセスしてみる

http://localhost:8000/admin/
にアクセスしてみます。※localhostで8000番ポートで公開させている場合

下記のURLにリダイレクトされて、ログインページが表示されると思います。
http://localhost:8000/admin/login/?next=/admin/

先程作成した管理者ユーザーのアカウントでログインします。
ログインできたら成功です。

CSSが適用されていない場合

adminの静的ファイルを集めてきましょう。

# python manage.py collectstatic

参考ページはこちら

ライブラリを追加

後述するルーティングの記載には、pathincludere_pathといった関数を使います。
※インポートしないと使えないので、必ずインポートしておきましょう。

urls.py
from django.contrib import admin
from django.urls import path
+ from django.urls import path, include, re_path

アプリケーションディレクトリのurls.pyを参照させる

設定ディレクトリのurls.pyから、アプリケーションディレクトリのurls.pyを参照させる記述になります。
urlpatternsはリスト型なので、複数記述する場合はカンマで区切ります。

config/urls.py
urlpatterns = [
    path('', include('app.urls')),
    path('sub/', include('app.sub.urls'))
]

path('', include('app.urls'))は、appディレクトリのurls.pyに、
path('sub/', include('app.sub.urls'))は、appディレクトリの中にあるsubディレクトリのurls.pyに渡します。
※(参考)アプリケーションディレクトリの中に、子ディレクトリを作ることも可能です

views.pyを参照する

アプリケーションのurls.pyから、views.pyを参照するときに記述します。
設定ディレクトリのurls.pyにも書くことはできると思いますが、アプリケーションディレクトリのurls.pyに書くほうが、どのviews.pyを参照しているか分かりやすいので、アプリケーション側に記述するほうが一般的かなと思います。

app/urls.py
urlpatterns = [
    path('{パス名}', views.{関数名}, name='{名前}'),
    path('{パス名}', views.{クラス名}.as_view(), name='{名前}')
]

nameという引数を書いてますが、これはそのpath()に対する名前を書いています。この引数(name)は記述しなくても動きます。関数名と同じ名前を入れてやればいいんじゃないかなと思います。

(例)views.pyのindex()関数を呼び出す書き方

app/urls.py
urlpatterns = [
    path('', views.index, name='index'),
    path('blog/', views.blog, name='blog'),
]
  • path('', views.index, name='index'),は、http://{サイト名}/のパスにアクセスがあった際に、views.pyのindex()関数を呼び出します。
  • path('blog/', views.blog, name='blog'),は、http://{サイト名}/blog/のパスにアクセスがあった際に、views.pyのblog()関数を呼び出します。

URLパターン

パスコンバータを使った記述方法と、re_path()関数を使った記述方法があります。

パスコンバータを使った書き方

int,str,path,slugといったパスコンバータを使えます。
以下に使用例を記載します。

app/urls.py
urlpatterns = [
    path('', views.index, name='index'),
    path('<int>/', views.integer, name='integer'),
    path('blog/<int:page>/', views.blogpage, name='blogpage'),
]
  • path('<int>/', views.integer, name='integer')は、http://{サイト名}/整数/のパスにアクセスがあった際に、views.pyのinteger()関数を呼び出します。
  • path('blog/<int:page>/', views.blogpage, name='blogpage'),は、http://{サイト名}/blog/整数/のパスにアクセスがあった際に、views.pyのblogpage()関数を呼び出します。その際の整数は、pageという名前の引数で渡されます。

re_path()を使った書き方

正規表現を使って記述できます。

re_path('[0-9]', views.integer, name='integer'),

静的ファイルを扱う

app/urls.py
urlpatterns = [
    re_path(r'^media/(?P<path>.*)$', serve,{'document_root': settings.MEDIA_ROOT}), 
    re_path(r'^static/(?P<path>.*)$', serve,{'document_root': settings.STATIC_ROOT}), 
]

こうも書ける

app/urls.py
from django.conf.urls.static import static
from django.contrib.staticfiles.urls import staticfiles_urlpatterns

urlpatterns += staticfiles_urlpatterns()
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 

デバッグモード時のみ扱う場合の書き方

app/urls.py
if settings.DEBUG:
    from django.conf.urls.static import static
    from django.contrib.staticfiles.urls import staticfiles_urlpatterns

    urlpatterns += staticfiles_urlpatterns()
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?