Django View的应用·登录功能·AbstructBaseUser(自用备忘录)
Django的自用备忘录
本次将总结关于视图的应用
软件开发环境
操作系统:Mac
编辑器:VSCode
Python版本:3.10.9
Django版本:4.1.0
将模型注册到管理界面
from django.contrib import admin
from .models import Items
admin.site.register(Items) #Itemsはmodel名
重定向处理
from django.shortcuts import redirect
def one_item(request):
return redirect('store:item_detail',id=1)
#リダイレクト時にidを渡すことも可
def item_detail(request,id):
item = Items.objects.filter(pk=id).first()
#detail/15のようにidがないURLを指定されたらリストに飛ばすなどの処理もこのように可能
if item is None:
return redirect('store:item_list')
return render(request,'store/item_detail.html',context={
'item':item
})
错误处理 (Incorrect handling/error handling)
・如果不将setting.py中的DEBUG设置为False,则无法进行错误处理。
・在ALLOWED_HOSTS中添加自己的主机。
ALLOWED_HOSTS=[‘127.0.0.1’]
・在项目的urls.py中添加想要设置的状态码处理程序。
handler404=views.函数名(在404错误情况下要执行的函数(有两个参数))
handler500=views.函数名(在500错误情况下要执行的函数(有一个参数))
故意引发404错误的方法。
from django.http import Http404
#if id ==0: この条件の時に404エラーを発生させるなど
raise Http404
当输入了无意的URL时,会显示404错误页面。
#views.py
def page_not_found(request,exception):
#statusに404を設定しないとターミナルのアクセスログに404ではなく200(通常アクセス)と認識される
return render(request,'store/404.html',status=404)
# return redirect('store:item_list') またはホームに飛ばすなんかもあり
#プロジェクトのurls.py
from store import views
handler404 = views.page_not_found
#テンプレートに404.htmlを作成で完了
在服务器处理出现问题时发生的错误
在编写Django时无法检查到错误,只能在连接服务器时发现错误
如果在出现此错误的页面上预先填写例如电子邮件地址等信息,用户就能更容易地了解出现了什么问题以及应该如何解决。
from django.shortcuts import get_object_or_404
get_object_or_404
#指定したモデルを呼び出し、getを行う。値を取得できなかった場合raise Http404を送出する
#例)item = get_object_or_404(Items,pk=id)
get_list_or_404
#指定したモデルを呼び出し、filterを行う。同上
#リストで取り出すためリストの中身がない状態で取り出すと404を返す
#例)items = get_list_or_404(Items,id__gt=2) idが2より大きいものを取り出す
登录功能的实现篇
在保存用户信息时,一定要将密码进行哈希处理后再保存。在指定进行哈希处理的函数(哈希算法)时,可以添加到setting.py文件的PASSWORD_HASHERS变量中。
有很多官方提供的哈希处理函数可以选择,而Argon2和BCrypt似乎是强大的选项。
要设置验证选项,请在settings.py中设置AUTH_PASSWORD_VALIDATORS的选项。
点击此处查看详细信息。
#追加
PASSWORD_HASHERS=[#上から順に実行される
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
]
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
#追加
'OPTIONS':{
'min_length':8,
}
},
#(略)
]
在使用argon2之前,需要在虚拟环境中安装pip。
$pip install django\[argon2\]
通过这样做可以成功安装。
当在zsh中执行命令时,如果包含方括号,会引发错误,所以需要进行转义才能顺利完成。
使用Django默认的User类来实现登录相关功能(实际场景中很少使用,仅供备忘)。
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
#デフォルトのUserモデルとOneToOneで結びつけて追加したい項目を追加
user = models.OneToOneField(User,on_delete=models.CASCADE)
website = models.URLField(blank=True)
picture =models.FileField(upload_to='user/',blank=True)
def __str__(self):
return self.user.username #管理画面でみた時にわかりやすく
将admin.py文件中的内容描述,以便在管理界面中可查看。
from django.contrib import admin
from .models import Profile
admin.site.register(Profile)
在forms.py中设置表格以注册到相应的模型
继承ModelForm并与模型关联
from django import forms
from django.contrib.auth.models import User
from user.models import Profile
class UserForm(forms.ModelForm):
username = forms.CharField(label='名前')
email = forms.EmailField(label='メールアドレス')
password = forms.CharField(label='パスワード',widget=forms.PasswordInput())
class Meta:
model = User #紐付け
fields = ('username','email','password')
class ProfileForm(forms.ModelForm):
website = forms.URLField(label='ホームページ')
picture = forms.FileField(label='写真')
class Meta:
model = Profile #紐付け
fields = ('website','picture')
#ログイン用のフォーム
class LoginForm(forms.Form):
username=forms.CharField(label='名前',max_length=150)
password=forms.CharField(label='パスワード',widget=forms.PasswordInput())
confirm_password=forms.CharField(label='パスワード再入力',widget=forms.PasswordInput())
def clean(self): #バリデーションを設定
cleaned_data=super().clean()
password = cleaned_data['password']
confirm_password = cleaned_data['confirm_password']
if password != confirm_password:
raise forms.ValidationError('パスワードが一致しません')
下面是views.py。
当用户提交用户表单后,
通过user = user_form.save()函数将用户信息保存至数据库。
然后,使用user.set_password(user.password)对用户密码进行加密,并将加密后的密码设置给用户。
接着,使用user.save()函数保存用户信息至数据库。
而与用户模型通过OneToOne关联的profile模型需要进行以下操作:
首先,通过profile_form.save(commit=False)将临时保存的profile表单信息暂存起来。
然后,将profile.user=user赋值,将保存的用户信息与profile关联。
最后,通过profile.save()函数将用户和profile的信息一并保存至数据库中。
在登录函数中,通过authenticate函数来验证用户是否存在。
通过添加@login_required装饰器,在登录状态时才能执行指定的函数。
{% 如果用户已经认证 %}:只在用户已经认证的情况下执行或显示
from django.shortcuts import render
from user.forms import UserForm,ProfileForm,LoginForm
from django.contrib.auth import authenticate,login,logout
from django.http import HttpResponse
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required
def user_list(request):
return render(request,'user/user_list.html')
def index(request):
return render(request,'user/index.html')
#登録用
def register(request):
user_form=UserForm(request.POST or None)
profile_form=ProfileForm(request.POST or None,request.FILES or None)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user.password) #パスワードを暗号化してセット
user.save() #保存
profile = profile_form.save(commit=False) #仮置きで保存
profile.user=user
profile.save() #ここで保存
return render(request,'user/registration.html',context={
'user_form':user_form,
'profile_form':profile_form
})
#ログイン用ビュー
def user_login(request):
login_form=LoginForm(request.POST or None)
if login_form.is_valid():
username=login_form.cleaned_data.get('username')
password = login_form.cleaned_data.get('password')
user = authenticate(username=username,password=password) #そんなユーザがいるかどうか確かめる
if user: #userが存在する場合
if user.is_active: #そしてuserがactiveの場合
login(request,user) #login関数は第一引数にrequest,第二引数にそのユーザにするとログインができる
return redirect('user:index')
else:
return HttpResponse('アカウントがアクティブではないです')
else: #userが存在しない時
return HttpResponse('ユーザが存在しません')
return render(request,'user/login.html',context={
'login_form':login_form
})
#ログアウト
@login_required #loginしていないとこの関数のURLにいっても起動しない
def user_logout(request):
logout(request)
return redirect('user:index')
@login_required
def info(request):
return HttpResponse('ログインしています')
验证用户和密码的有效性 hé de
要判断密码是否有效
from django.contrib.auth.password_validation import validate_password,
from django.contrib.auth.password_validation import password_change
使用
检查用户的密码是否合适(不太短,不太常见,不容易从用户名推测出来)。第一个参数接收密码,第二个参数接收用户。
在注册阶段对密码进行验证。
编辑views.py文件
from django.core.exceptions import ValidationError
from django.contrib.auth.password_validation import validate_password
#登録用
def register(request):
user_form=UserForm(request.POST or None)
profile_form=ProfileForm(request.POST or None,request.FILES or None)
if user_form.is_valid() and profile_form.is_valid():
#下記から追加した部分。(commit=False)によってまだ保存しないで持っておく
user = user_form.save(commit=False)
#パスワードのバリデーションを行う
try:
validate_password(user_form.cleaned_data.get('password'),user)
#forms.pyでvalidate_passwordをしている場合はuser_form.save()でいい
except ValidationError as e:
user_form.add_error('password',e) #add_errorでフォームにエラーを追加できる
return render(request,'user/registration.html',context={
'user_form':user_form, #再表示させる
'profile_form':profile_form
})
#以下同じ。ValidationError発生後は下記は行われない
user.set_password(user.password) #パスワードを暗号化してセット
user.save() #保存
profile = profile_form.save(commit=False) #仮置きで保存
profile.user=user
profile.save() #ここで保存
return render(request,'user/registration.html',context={
'user_form':user_form,
'profile_form':profile_form
})
自己编写数据验证器
只要在settings.py中自定义并将其添加到AUTH_PASSWORD_VALIDATORS中作为验证器,就可以了。
在项目的根目录下创建utils/validators.py文件。在这里编写密码验证处理。
from django.core.exceptions import ValidationError
import re #正規表現ライブラリ
class CustomPasswordValidator():
def __init__(self):
pass
def validate(self,password,user=None):
#if allは全てがTrueの場合Trueを返して中の処理を実行
if all((re.search('[0-9]',password),re.search('[a-z]',password),re.search('[A-Z]',password))):
return
raise ValidationError('パスワードには0-9,a-z,A-Zを一文字ずつ含む必要があります')
#ヘルプテキストも追加しておく
def get_help_text(self):
return 'パスワードには0-9,a-z,A-Zを一文字ずつ含む必要があります'
在settings.py文件中进行配置
AUTH_PASSWORD_VALIDATORS = [
{
#自作のパスワードバリデータを追加
'NAME':'utils.validators.CustomPasswordValidator'
},
]
使用 AbstractBaseUser 实现登录功能
如果想要利用现有的字段并删除username字段,可以使用AbstractUser。
当想要从头开始重建User类时,可以使用AbstractBaseUser来方便操作。
1. 创建自定义管理器和自定义用户类
2. 修改settings.py文件,使用户指向自定义类
3. 进行迁移
4. 创建表单和管理员界面
使用PermissionsMixin可以将一般的Django权限,如superuser等,引入到自定义用户中。
要查看一般用户所拥有的字段和属性,请查阅官方资料。
定制化的內容在這裡。
请实际尝试写一下。
from django.db import models
from django.contrib.auth.models import (
BaseUserManager,AbstractBaseUser,PermissionsMixin
)
#マネージャーの設定(ユーザを作成する際のメゾットを定義する場所)
class UserManager(BaseUserManager):
def create_user(self,username,email,password=None):
if not email:
raise ValueError('Enter Email!')
user = self.model(
username=username,
email = email
#それ以外のフィールドはデフォルトをいれる
)
user.set_password(password) #passwordをセットして
user.save(using=self._db) #DBに保存
return user
def create_superuser(self,username,email,password=None):
user = self.model(
username=username,
email = email,
)
user.set_password(password)
user.is_staff=True
user.is_active=True
user.is_superuser=True
user.save(using=self._db)
return user
class User(AbstractBaseUser,PermissionsMixin):
username=models.CharField(max_length=150)
email = models.EmailField(max_length=255,unique=True)
is_active = models.BooleanField(default=False) #このユーザが有効かどうか
is_staff = models.BooleanField(default=False) #このユーザが管理画面にアクセスできるか
website = models.URLField(null=True)
picture = models.FileField(null=True,upload_to='picture/') #upload_toでファイルの保存フォルダを指定
#passwordフィールドはAbstractBaseUserに定義されてるのでここで定義する必要はなし
#is_superuserフィールドもPermissionsMixinに定義されているので定義しないでOK
USERNAME_FIELD='email' #USERNAME_FIELDはユーザをユニークと識別するフィールドを指定する
REQUIRED_FIELDS = ['username'] #superuser作成時に入力する項目。USERNAME_FIELDとパスワードは必須になるのでそれ以外にあれば設定
objects=UserManager() #UserManagerとの紐付け。objectsに対して作ったマネージャーを設定
def __str__(self): #管理画面でみやすくなる
return self.email
在 settings.py 文件中添加
#作成したUserクラスを指定
AUTH_USER_MODEL='accounts.User' #accountsはアプリ名
通过使用AbstructBaseUser进行用户注册的复习
要归结成中文,在forms.py中定义的clean和save方法的程度决定了在views.py中所需编写的内容。定义验证和保存方法在forms.py中可能更容易。以下是演习中的forms.py和views.py。
from django import forms
from django.contrib.auth import get_user_model
from .models import Users
from django.core.exceptions import ValidationError
from django.contrib.auth.password_validation import validate_password
User=get_user_model()
class RegistForm(forms.ModelForm):
username = forms.CharField(label='名前')
age =forms.IntegerField(label='年齢',min_value=0)
email = forms.EmailField(label='メールアドレス')
password=forms.CharField(label='パスワード',widget=forms.PasswordInput)
confirm_password = forms.CharField(label='パスワード再入力',widget=forms.PasswordInput)
# picture = forms.FileField(label='写真')
class Meta:
model=Users
fields=('username','age','email','password')
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
confirm_password = cleaned_data.get('confirm_password')
if password != confirm_password:
raise ValidationError('パスワードが一致しません')
def save(self,commit=False):
user = super().save(commit=False)
validate_password(self.cleaned_data.get('password'),user)
user.set_password(self.cleaned_data.get('password'))
user.save()
return user
下一个是views.py
from django.shortcuts import render,redirect
from .forms import RegistForm
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
def home(request):
return render(request,'accounts/home.html')
def regist(request):
form = RegistForm(request.POST or None)
if form.is_valid():
# user = form.save(commit=False) 不要
#パスワードのバリデーションを行う
try:
# validate_password(form.cleaned_data.get('password'),user)
# forms.pyでvalidate_passwordをすでにしているので今回はform.save()をtry文で実行する
form.save()
return redirect('accounts:home')
except ValidationError as e:
form.add_error('password',e) #add_errorでフォームにエラーを追加できる
# user.set_password(user.password) 不要
# user.save() 不要
return render(request,'accounts/regist.html',context={
'form':form
})
要实现可以从管理界面创建和编辑。
我理解了,这就是这种东西的认识水平。
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
User = get_user_model() #今回作成した利用するUserクラスを指定する
#管理画面でつかうフォーム
class UserCreationForm(forms.ModelForm):
password=forms.CharField(label='password',widget=forms.PasswordInput)
confirm_password = forms.CharField(label='password再入力',widget=forms.PasswordInput)
class Meta:
model =User
fields = ('username','email','password')
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
confirm_password = cleaned_data.get('confirm_password')
if password != confirm_password:
raise ValidationError('パスワードが一致しません')
def save(self,commit=False):
user = super().save(commit=False)
#下記のようにforms.pyでvalidate_passwordした場合はviews.pyにてtryで実行するのはform.save()
validate_password(self.cleaned_data.get('password'),user)
user.set_password(self.cleaned_data.get('password'))
user.save()
return user
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField() #ハッシュ化されたパスワードを表示
website = forms.URLField(required=False)
picture = forms.FileField(required=False)
class Meta:
model = User
fields = ('username','email','password','is_staff','is_active','is_superuser','website','picture')
def clean_password(self):
#initial すでに登録されているパスワードを返す
return self.initial['password']
在admin.py中进行读取
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth import get_user_model
from .forms import UserCreationForm,UserChangeForm
User=get_user_model()
class CustomizeUserAdmin(UserAdmin):
#管理画面の表示の仕方を変えたい時にUserAdminを継承して変えていく
form = UserChangeForm #ユーザ編集画面で使うFormを定義
add_form = UserCreationForm #ユーザ作成画面でつかうFormを定義
#ユーザ一覧画面で表示する要素の定義
list_display = ('username','email','is_staff')
#ユーザ編集画面で表示する要素の定義
fieldsets=(
('ユーザ情報',{'fields':('username','email','password','website','picture')}),
('パーミッション',{'fields':('is_staff','is_active','is_superuser')}),
)
#ユーザ作成画面で表示する要素の定義
add_fieldsets=(
('ユーザ情報',{
'fields':('username','email','password','confirm_password')
}),
)
#第一引数をUser,第二引数をCustomizeUserAdminにすることによって
#Userを管理画面から見れるようにして表示方法は自作したCustomizeUserAdminにすることができる
admin.site.register(User,CustomizeUserAdmin)
以下是管理画面的图像。

管理面板的自定义- 第一部分
首先,创建基本模型并使表名和数据对象名更易于理解,还要在admin.py中进行注册。
#管理画面のカスタマイズ
class Students(models.Model):
name=models.CharField(max_length=20)
age = models.IntegerField()
score = models.IntegerField()
school = models.ForeignKey(
'Schools',on_delete=models.CASCADE
)
class Meta:
db_table='students'
verbose_name_plural='生徒' #管理画面でのテーブルの名前を変更
def __str__(self):
return self.name + ': '+str(self.age) #管理画面でのオブジェクトの名前を変更
class Schools(models.Model):
name= models.CharField(max_length=20)
class Meta:
db_table = 'schools'
verbose_name_plural='学校' #管理画面でのテーブルの名前を変更
def __str__(self):
return self.name #管理画面でのオブジェクトの名前を変更
将 admin.py进行注册
admin.site.register(Students)
admin.site.register(Schools)
管理面板的自定义 第2部分 ( de dì èr )
可以在调用admin.site.register函数时将继承于admin.ModelAdmin的类作为第二个参数传入,从而对每个模型的页面进行自定义。或者可以在继承于admin.ModelAdmin的类之前使用@admin.register(模型名)装饰器来实现。
#admin.site.register(Students)
#admin.site.register(Schools) 下記を設定するならコメントアウトしておく
#管理画面での表示の並び替えと表示項目を設定
@admin.register(Students)
class StudentAdmin(admin.ModelAdmin):
fields = ('name','score','age','school') #並び替えたい順に書く
list_display = ('id','name','age','score','school') #表示項目の指定
list_display_links = ('name') #nameをクリックすると詳細に飛ぶリンクを設定
#scoreを高い順に並べたいときはmodels.pyでMetaクラスにorderingを設定
search_fields=('name','age',) #検索窓を追加。絞り込みたい項目を設定
list_filter=('name','age','score','school',) #フィルターを追加
list_editable = ('age','score','school') #一覧から直接数値を変えることができる
#学校一覧に生徒数も表示したい
@admin.register(Schools)
class SchoolsAdmin(admin.ModelAdmin):
list_display = ('name','student_count',) #student_countは下記で定義する関数
def student_count(self,obj): #objとはSchoolクラスのインスタンスのこと
# print(type(obj))
# print(dir(obj)) #メゾットをみるとstudents_setがある
count = obj.students_set.count()
return count
student_count.short_description = '生徒数' #カラム名を変える
#高校名のカラムを変える場合はmodels.pyにverbose_nameを設定
#models.py 並びかえる時はモデルのクラスMetaに設定
class Meta:
db_table='students'
verbose_name_plural='生徒' #管理画面でのテーブルの名前を変更
ordering=('score',) #管理画面での並び順の設定.降順は-scoreとする
#表示カラム名を変更
class Schools(models.Model):
name= models.CharField(max_length=20,verbose_name='学校名')
管理界面的定制化,第三部分。

点击复制粘贴并进行修改
因为django/django/contrib/admin/templates/admin/base.html文件加载了所有的模板,所以当想要修改某些内容时,可以查看这个文件,从而了解应该编辑哪个文件(在查看这个文件后可以知道,css是通过加载admin/css/base.css来实现的,所以只需要编辑它即可)。