Django冲刺#3 – 用户模型的定制化
-
- Django Sprint #0 イントロダクション
-
- Django Sprint #1 環境構築
-
- Django Sprint #2 新規プロジェクトのスタート
-
- Django Sprint #3 ユーザーモデルのカスタマイズ
-
- Django Sprint #4 トップページの作成
-
- Django Sprint #5 汎用ビューとCRUD 前編
- Django Sprint #6 汎用ビューとCRUD 後編
目录
1. Git的运用
2. 各种设置
3. 模型概论
4. 用户模型与认证、管理员
Git的使用
对于第一次使用Git的人来说,可能会觉得有点难。在这种情况下,与其追求掌握技术的语言,不如按“学以致用”的方式亲自尝试使用,这样更容易理解。如果感觉困难,不用Git也可以进行开发。
在这个教程中,我们对 GitHub Flow 进行了一些小的更改。如果团队有其他运营规则,请以那些规则为优先。
-
- GitHub Flow
-
- 『GitHubの機能を活用したGitHub Flowによる開発の進め方』
- 『A successful Git branching model (日本語訳)』
此外,要在 GUI 上操作 Git(而非使用命令行界面,而是以视觉化方式),
-
- GitHub Desktop
- 『GitHub Desktopの使い方 – ブランチの作成・削除と切り替える方法』
我强烈推荐。
让我们尝试一下添加README文件(此项目的说明文件等)。
「branch」的意思是
让我们尝试使用以下命令来创建分支,分支是用来记录工作历史的分叉记录。
$ git branch
* master
现在应该只有主要分支(master branch)。
两个分支:主分支(master)和开发分支(develop)。
刚刚看到的master分支是项目的主要分支。让我们将其称为“始终可部署(公开)的内容”。换句话说,只有经过测试的“成品”才允许在这个神圣的地方存在。
另一方面,我们将平时的开发分支称为develop。这是用于开发的主要分支,不会被公开。
开一个develop分支。
让我们创建这个develop分支。切换分支需要使用以下命令。
$ git checkout -b develop
Switched to a new branch 'develop'
大致上看了一下分支的列表,果然
$ git branch
* develop
master
请将其更新到远程分支上,*代表当前所在的分支。
$ git push -u origin develop
在中文中,仅提供一种选择的释义如下:
通过选项-u,将本地代码库当前分支的上游设为origin develop。设置后将保持,不再更改。
$ git push
只需通过远程推送到develop分支。
我们可以先在GitHub页面上确认一下develop分支是否正确生成。
实际的开发流程
实际的开发工作基本上是以下的循环。
-
- 使用git pull将远程更改同步到本地
使用git checkout -b在本地创建功能分支
使用git add和git commit将更改同步到本地的功能分支
使用git push将本地功能分支推送到远程
创建pull request并进行审查和修正,确认能否将功能分支合并到develop分支
在GitHub上进行合并操作
参考:「使用Pull Request的开发过程」。
创建功能分支
如果团队成员还没有关于Git的任何设置,请
- 『2.1 Git の基本 – Git リポジトリの取得』
请参考。
首先,将远程更改同步到本地。
$ git checkout develop
$ git pull origin develop
接下来,我们要创建一个功能分支。我们将给这个分支取名为”add-readme”。最好尽量使用描述性的分支名称。
$ git checkout -b add-readme
添加并提交
首先,我们要对功能进行添加、修改和删除。在这里,我们将在code目录的根目录下创建一个名为README.md的文件。README文件是用来写项目说明等内容的地方,主要用于外部人员理解这个项目是什么样的。顺便说一下,.md是表示Markdown语法的文件扩展名。创建文件后,我们将根据以下内容进行修改。
# Django Sprint
This is a tutorial for UTokyo Project Sprint.
请随意书写,按照自己喜欢的方式来表达。
接下来,保存并应用更改。
$ git add -A
$ git commit -m "Add README"
拉取请求
完成任务后,首先进行推送。
$ git push origin add-readme
接下来,我会发送一个Pull Request(PR),进入团队成员的确认阶段。详细的内容和步骤如下:
-
- 『プルリクエストとは?』
-
- 『開発ブランチで修正』
-
- 『レビューとマージ』
- 『画面上からマージ』
请参考。
今后
我以后会省略细微的Git相关命令。由于熟悉Git很重要,所以建议您在README.md或下一个设置更改中尝试使用一次。
当发生冲突时是很麻烦的。首先请尝试在Google上搜索,如果没有类似的案例,请毫不犹豫地咨询我们。
各种配置
设置.py
让我们打开一下settings.py文件吧!
settings.py文件是用来描述该项目的”设置”的。你可以在这里修改各种设置。因为经常使用,所以最好先大致看一下。
时区变更
让我们试试改变时区。首先,请从settings.py文件中找出以下的描述!
...
TIME_ZONE = 'UTC'
...
默认情况下,时区设置为“UTC”,即伦敦的时区。如果要将其更改为东京的时区,可以尝试按照以下方式进行修改。
...
TIME_ZONE = 'Asia/Tokyo'
...
改变语言
我们来更改语言吧。默认情况下,语言设置为”en-us”,即英语(美国)。Django支持很多语言,幸运的是它也支持日语。要更改为日语,请按以下方式进行修改。
...
LANGUAGE_CODE = 'ja'
...
模型概述
MVC模型是什么?
MVC模型是什么?
-
- モデル(Model): データベースとコントローラーの間で、データ処理を行うファイル群
-
- ビュー(View): ブラウザとコントローラーの間で、リクエストの取得やHTMLなどのファイルの出力を担うファイル群
- コントローラー(Controller): 中枢でモデルとビューからデータを受け取り、処理して返すファイル群
请详细说明
- 『MVCモデルについてわかりやすく解説します!【初心者向け】』
请参考。
Django中的MVC模型
有相当复杂的写法差异。
-
- モデル(Model): Djangoではモデル(Model)と呼ばれる
-
- ビュー(View): Djangoではテンプレート(Template)と呼ばれる
- コントローラー(Controller): Djangoではビュー(View)と呼ばれる
请以后在写代码时,将“ビュー”翻译为“控制器”。
具体的模型文件
让我们看一下具体的模型文件。在这里,我们来看一下从其他项目中带来的样例。
...
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
请想象一下博客应用程序的文章模型。这个类(class)Post类似于Excel表格。而author(作者)和title(标题)等则对应于Excel表格中的各列。CharField等表示数据的类型。如果想要将模型与其他模型组合在一起,可以使用ForeignKey等。在上面的例子中,author将Post模型和User模型连接在一起。请参考以下文档,了解此类字段类型和选项。
- Django docs | モデルフィールドリファレンス
用户模型与认证、管理员。
Django默认附带了用户模型和认证功能(包括用户名、电子邮件和密码)。(密码会被哈希加密后保存)。因此,相较于其他框架,Django更加方便且安全可靠。
然而,需要注意的是如果不首先定制用户模型,之后更改将变得困难。虽然不是不可能,但会很麻烦。因此,在本教程中,我们建议首先进行一些少量的定制,以便之后进行更容易的定制。(实际上,官方文档也强烈推荐这样做。)
创建一个新应用程序
首先,我们创建一个应用程序。在Django中,应用程序是构成项目的功能单元之一。我们可以将其按功能划分,以便更容易地进行重复利用。但是,在本教程中,我们将把所有内容都暂时集中在一个名为”cms”(内容管理系统)的应用程序中。
$ docker-compose run --rm web django-admin startapp cms
请确保现在的目录结构如下所示。
code
├─ requirements.txt
├─ README.md
├─ manage.py
├─ config
│ ├─ __init__.py
│ ├─ asgi.py
│ ├─ settings.py
│ ├─ urls.py
│ └─ wsgi.py
├─ cms
│ ├─ migrations
│ ├─ __init__.py
│ ├─ admin.py
│ ├─ apps.py
│ ├─ models.py
│ ├─ tests.py
│ └─ views.py
├─ docker-compose.yml
└─ Dockerfile
刚才看到了类似于 models.py 和 views.py 的文件呢!实际上,通过修改这些文件来构建网页。(也可能需要添加新的文件。)
让Django知道我们在这里添加了应用程序,并确保不要忘记这个事实。
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'cms.apps.CmsConfig', #Added
]
...
在決定專案名稱為「config」時,是因為當把整個專案文件視為一個應用程式(與Django應用程式不同)時,「config」目錄中包含了相關的配置信息。
models.py文件的更改
我会修改模型文件,但目前来说,我认为复制粘贴是可以的。(虽然可能还有更简洁的方法,但因为已经确认其可行,所以暂时就用这个吧…)
from django.contrib.auth.base_user import (
AbstractBaseUser, BaseUserManager,
)
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
# User-related
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Create and save a user with the given email and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
**extra_fields,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
class AbstractUser(AbstractBaseUser, PermissionsMixin):
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
validators=[username_validator],
error_messages={
'unique': _("A user with that username already exists."),
},
)
first_name = models.CharField(_('first name'), max_length=150, blank=True)
last_name = models.CharField(_('last name'), max_length=150, blank=True)
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
abstract = True
def __str__(self):
return self.email
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
class User(AbstractUser):
class Meta(AbstractUser.Meta):
swappable = "AUTH_USER_MODEL"
也许可以创建一个继承自现有AbstractUser的User来解决这个问题。实际上,这个代码片段已经被隐含地写入了其中,就像对其进行了覆盖一样。具体来说,
username : ユーザー名
first_name : 名
last_name : 姓
email : メールアドレス
date_joined : 参加した日時
is_staff : 管理者権限を持つか?
is_active : 有効なアカウントか?
该列已设置。
最后,我们将其告知Django作为用户模型,在settings.py的末尾部分。
...
# Custom
AUTH_USER_MODEL = 'cms.User'
如果添加了,请您完成此事。此后您可以在任何时候进行迁移,没有问题。
管理.py
Django预先准备了一个管理员页面。有了这个页面,您可以在不实际操作控制台或编写SQL语句的情况下,进行数据添加、更新和删除操作。
暫時先將admin.py按照以下方式修改,以反映對使用者模型的更改。
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from .models import (
User,
)
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email',)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('email', 'password', 'is_active', 'is_staff',)
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'is_staff')
list_filter = ('is_staff',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('username', 'last_name', 'first_name')}),
('Permissions', {'fields': ('is_staff',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'username', 'password1', 'password2'),
}),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
# Now register the new UserAdmin...
admin.site.register(User, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
管理者可以使用以下命令进行生成。
$ docker-compose run --rm web python manage.py createsuperuser
然后,您将被要求输入用户名、电子邮件地址(可以是虚构的),以及密码,请按照指示进行设置。(请记住这些信息,因为您以后会用到。)
迁移
最后一步是将模型的更改信息传递到数据库中。这被称为迁移,在Django中通常使用。
-
- 创建迁移文件
- 应用迁移文件
只有一个选项:它包含两个步骤。
$ docker-compose run --rm web python manage.py makemigrations
$ docker-compose run --rm web python manage.py migrate
现在,初始设置已经完成了!
管理界面
当你访问 http://localhost:8000/admin/,你应该能够看到管理员登录界面。在那里,你需要输入之前设置的”用户名”和”密码”,然后应该会显示出类似这样的界面。
好吧,当点击这个“添加”用户时,就可以简单地添加用户。让我们尝试先添加一个样例用户。
请以参考为准
-
- Django Sprint #0 イントロダクション
-
- Django Sprint #1 環境構築
-
- Django Sprint #2 新規プロジェクトのスタート
-
- Django Sprint #3 ユーザーモデルのカスタマイズ
-
- Django Sprint #4 トップページの作成
-
- Django Sprint #5 汎用ビューとCRUD 前編
-
- Django Sprint #6 汎用ビューとCRUD 後編
-
- Django Sprint Appendix Docker関連
-
- Django Sprint Appendix 各種実装まとめ
-
- Django Sprint Appendix モデルとデータベース
-
- Django+PostgreSQLのアプリケーションをAWSのElastic Beanstalkにデプロイする (UTokyo Project Sprint 用)
- Django+MySQLのアプリケーションをAWSのElastic Beanstalkにデプロイする (UTokyo Project Sprint 用)