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分支是否正确生成。

实际的开发流程

实际的开发工作基本上是以下的循环。

    1. 使用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中通常使用。

    1. 创建迁移文件

 

    应用迁移文件

只有一个选项:它包含两个步骤。

$ docker-compose run --rm web python manage.py makemigrations 
$ docker-compose run --rm web python manage.py migrate

现在,初始设置已经完成了!

管理界面

当你访问 http://localhost:8000/admin/,你应该能够看到管理员登录界面。在那里,你需要输入之前设置的”用户名”和”密码”,然后应该会显示出类似这样的界面。

スクリーンショット 2020-05-02 10.17.14.png

好吧,当点击这个“添加”用户时,就可以简单地添加用户。让我们尝试先添加一个样例用户。

请以参考为准

    • 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 用)