Django
实现简单的注册登录功能流水帐结构的介绍,适合Django
的新手作为Hello World级别的第一个练习项目。
项目使用Docker
容器提供环境,Python
版本3.8.10。
创建一个目录account_app,作为本次项目的主目录。
创建一个本地的虚拟环境,不为别的,,就是为了在vscode
里可以使用自动补全。
py -m venv env
创建一个虚拟环境。python -m venv env
创建一个虚拟环境。Django
项目切换到account_app目录下,使用命令django-admin startporject saccount_app .
创建一个Django
项目。使用django-admin startapp accounts
创建一个小应用。
Django
简单的先设置一下:
MySQL
)。django-crispy-forms
工具包(至于这个包的信息去pypi查。TIME_ZONE = 'Asia/Shanghai'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'accounts',
'HOST': 'mysqldb',
'port': 3306,
'USERNAME': 'root',
'PASSWORD': 'password',
}
INSTALLED_APPS = [
.....
# forms crispy
'crispy_forms',
# custom app
'accounts',
]
AUTH_USER_MODEL = 'accounts.Account'
TEMPLATES = [
{
'DIRS': ['templates'],
]
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Crispy Forms options
CRISPY_TEMPLATE_PACK = 'bootstrap4'
}
先简单的创建应用的模型,继承自Django
自带的AbstractUser。
class Account(AbstractUser):
token = models.CharField(max_length=256, blank=False)
Docerk
相关的文件先创建一个runserver.sh, 为了方便启动项目
#!/bin/sh
# Make migrate
python manage.py makemigrations
python manage.py migrate
# Start Server
python manage.py runserver 0.0.0.0:8000
创建requirements.txt
记录需要安装的Python
包。目前只需要这几个。因为mysqlclient
的特殊,它需要一些以来所以,放在了容器创建的时候单独安装。
django>=3.0
django-mysql>=3.8
django-crispy-forms
创建Dockerfile
FROM python:3.8.10-alpine
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt requirements.txt
COPY . /app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update \
&& apk add --virtual python3-dev musl-dev build-base \
&& apk add --no-cache mariadb-dev \
&& pip config set global.index-url https://mirrors.aliyun.com/pypi/simple \
&& pip install --upgrade pip \
&& pip install mysqlclient \
&& apk del build-base \
&& pip install -r requirements.txt
创建docker-compose.yml
这里可能会出现很多小问题,自己谷歌吧。如果MySQL
无法自己创建初始的数据库,就在本地的db文件下自己添加sql
文件创建用的数据库。这里在后面启动的时候,创建数据库的操作只会发生在第一次构建容器的时候(说的不具体,具体的写在了官方文档里),如果因为某些错误没有建立初始的数据库,可以在启动构造容器的时候强制重构。
version: '3'
services:
app:
build: .
ports:
- 8000:8000
volumes:
- .:/app
command: sh runserver.sh
depends_on:
- mysqldb
mysqldb:
image: mysql
container_name: account_mysql
volumes:
- ./db:/docker-entrypoint-initdb.d/:ro
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=accounts
创建static和templates文件夹
存放静态文件和模板文件,具体看Django
的文档。
urlpatterns = [
path('', home_view, name='home'),
path('regist/', regist_view, name='regist'),
path('login/', login_view, name='login'),
path('logout/', logout_view, name='logout'),
path('forget_password/', forget_password_view, name='forgetpasswd'),
path('new_password/', new_password_view, name='new_password'),
path('activate//' , activate_view, name='activate'),
]
urlpatterns = [
path('account/', include('accounts.urls')),
]
html
模板文件,在templates文件夹下添加account文件夹与accounts应用相关的模板文件放到其中。至于css
、js
和图片文件放在static文件夹下。 def home_view(request):
return render(request, 'account/index.html')
def regist_view(request):
return render(request, 'account/form.html')
def login_view(request):
return render(request, 'account/form.html')
使用命令 docker-compose up --build -d
或者可以不用-d
。命令的具体细节看官方文档。然后在浏览器查看localhost:8000/account/login
等前面文件中定义的链接。
至此算是简单的把项目创建出来了。剩下的就是完善具体的accounts应用下的功能了。
前面的步骤是在创建项目,现在开始进一步完善功能。下面以注册功能为主介绍。
使用Django
内置forms
。实现表单、表单字段的校验和使用django-crispy-forms
与bootstrap4
简单的美化表单。所有的具体细节多看官方文档都会知晓。
注册表单文件accounts/forms.py
class RegistForm(forms.Form):
username = forms.CharField(
label='username',
required=True,
min_length=4,
max_length=80,
)
email = forms.EmailField(
label='email',
required=True,
)
first_name = forms.CharField(
label='first_name',
required=True,
min_length=1,
max_length=80,
)
last_name = forms.CharField(
label='last_name',
required=True,
min_length=1,
max_length=80,
)
password = forms.CharField(
label='passowrd',
widget=forms.PasswordInput,
required=True,
min_length=8,
max_length=80,
)
passwd_confirm = forms.CharField(
label='passwd_confirm',
widget=PasswordInput,
required=True,
min_length=8,
max_length=80,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = "id-registForm"
self.helper.form_method = 'post'
self.helper.form_action = 'regist'
self.helper.form_show_labels = False
self.helper.layout = Layout(
Div(
HTML(''),
css_class="form-icon"
),
Field('username', css_class="form-control item",
placeholder='usernmae'),
Field('email', css_class="form-control item",
placeholder='Email'),
Row(
Column(
Field('first_name', css_class="form-control item",
placeholder='First Name'),
css_class="form-group col-md-6 mb-0"),
Column(
Field('last_name', css_class="form-control item",
placeholder='Last Name'),
css_class="form-group col-md-6 mb-0"),
css_class="form-row"
),
Field('password', css_class="form-control item",
placeholder='Password'),
Field('passwd_confirm', css_class="form-control item",
placeholder='Password Confirm'),
ButtonHolder(
Submit('submit', 'Regist', css_class="btn btn-block create-account")
)
)
def clean_username(self):
value = self.cleaned_data['username']
if Account.objects.filter(username=value):
raise forms.ValidationError('Username has been registed!', code='invalide')
else:
return value
def clean_email(self):
value = self.cleaned_data['email']
if Account.objects.filter(email=value):
raise forms.ValidationError('Email has been used!', code='invalid')
else:
return value
def clean_password(self):
value = self.cleaned_data['password']
if value.isdigit():
raise forms.ValidationError('Password must hava characters', code='invalid')
else:
return value
def clean_passwd_confirm(self):
value = self.cleaned_data['passwd_confirm']
try:
target = self.cleaned_data['password']
except Exception as e:
raise forms.ValidationError('Password is valid!', code='invalid')
else:
if value != target or target is None:
raise forms.ValidationError('Passwords are not same!', code='invalid')
else:
return value
有了表单之后,接着需要处理来自表单的数据和完善注册视图函数的功能。
token
本项目使用了pyJWT
这个包来产生token
。但是从之前的Dockerfile中可以知道,本次项目中使用的基础环境是一个alpine linux
。所以安装pyJWT
的时候得稍微费点力,如果想使用更多的生成token
的算法,还需要名为cryptography
的包。所以又得安装一些依赖来编译这个包。实际上本次项目也没有用到其中的算法。
完善后的Dockerfile
FROM python:3.8.10-alpine
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt requirements.txt
COPY . /app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update \
&& apk add --virtual python3-dev musl-dev build-base \
&& apk add --no-cache mariadb-dev libffi-dev \
&& pip config set global.index-url https://mirrors.aliyun.com/pypi/simple \
&& pip install --upgrade pip \
&& pip install mysqlclient cryptography \
&& apk del build-base \
&& pip install -r requirements.txt
可以使用命令单独编译这个app容器docker-compose up --detach --build app
。记得在requirements.txt中添加pyJWT
。
如果在Django
里直接发送邮件可能会有点慢,所以本项目使用celery
来异步发送,选择了redis
作为celery
的消息和返回结果存储的工具。
celery:
build: .
command: celery -A account_app worker -l info
volumes:
- .:/app
depends_on:
- redis
redis:
image: redis
Django
配置使用celery
和邮件服务import os
from celery import Celery
# Set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'account_app.settings')
app = Celery('account_app')
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a 'CELERY_' prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
# Load task modules from all registered Django apps.
app.autodiscover_tasks()
celery
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
__all__ = ('celery_app',)
@shared_task(name="send_email")
def send_email(mail_subject, message, to):
email = EmailMessage(
mail_subject,
message,
to=to
)
email.send()
完善后的注册视图函数
def regist_view(request):
if request.method == 'POST':
regist_form = RegistForm(request.POST)
if regist_form.is_valid():
username = regist_form.cleaned_data['username']
email = regist_form.cleaned_data['email']
first_name = regist_form.cleaned_data['first_name']
last_name = regist_form.cleaned_data['last_name']
password = regist_form.cleaned_data['password']
new_account = Account.objects.create(
username=username,
email=email,
first_name = first_name,
last_name = last_name,
)
new_account.set_password(password)
# Set verify token here
new_account.token = VerifyToken.token_generator(
{
'id': new_account.pk,
'username': username,
}
)
# set user not active
new_account.is_active = False
new_account.save()
# send activate emial here.
current_site = get_current_site(request)
mail_subject = 'Activate your account'
message = render_to_string(
'email/activate_email.html',
{
'user': new_account,
'domain': current_site,
'token': new_account.token,
}
)
send_email.delay(mail_subject, message, [email,])
return render(request, 'account/info.html', {
'info': 'Success! please activate your account from E-mail.'})
else:
return render(request, 'account/form.html', {'form': regist_form, 'title': 'Register'})
form = RegistForm()
return render(request, 'account/form.html', {'form': form, 'title': 'Register'})
至此简单的注册功能算是实现了。其他的功能类似注册功能一样实现。
本项目实现的功能都是最最简单的,所以作为Django
入门的第一个小例子非常合适。自我感觉没人会看到最后,所以本文的源代码链接就放在最后了。欢迎分享更多的知识。