Django实现简单的注册登录

目录结构

  • `Django`实现简单的注册登录功能
    • 第一步:创建项目
      • 1. 创建目录
      • 2. 创建`Django`项目
        • 2.1 简单的设置一下`Django`
        • 2.2 创建应用*accounts*模型
        • 2.3 创建和`Docerk`相关的文件
        • 2.3 完善*accounts*应用
        • 2.4 如果不出错的话可以启动应用了
    • 第二步:完善项目
      • 1. 创建注册表单
      • 2. 完善注册视图函数
        • 2.1 简单的添加注册激活`token`
        • 2.2 添加邮件服务发送激活链接的邮件
        • 2.3 完善视图函数
        • 2.4 注册结果展示
    • 第三步: 继续完善(TODO)
    • 最后

Django实现简单的注册登录功能

流水帐结构的介绍,适合Django的新手作为Hello World级别的第一个练习项目。

项目使用Docker容器提供环境,Python版本3.8.10

第一步:创建项目

1. 创建目录

创建一个目录account_app,作为本次项目的主目录。

创建一个本地的虚拟环境,不为别的,,就是为了在vscode里可以使用自动补全。

  • Windows:
    使用命令py -m venv env创建一个虚拟环境。
  • Linux
    使用命令python -m venv env创建一个虚拟环境。

2. 创建Django项目

切换到account_app目录下,使用命令django-admin startporject saccount_app .创建一个Django项目。使用django-admin startapp accounts创建一个小应用。

2.1 简单的设置一下Django

简单的先设置一下:

  1. 时区。
  2. 数据库(这里使用MySQL)。
  3. 注册一下刚刚创建的应用和用到的django-crispy-forms工具包(至于这个包的信息去pypi查。
  4. 还有静态文件和模板文件等一些配置。
  5. 没必要和下面的配置完全一样,具体的多看官方的文档
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'

}
2.2 创建应用accounts模型

先简单的创建应用的模型,继承自Django自带的AbstractUser

class Account(AbstractUser):
    token = models.CharField(max_length=256, blank=False)
2.3 创建和Docerk相关的文件
  1. 先创建一个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
    
  2. 创建requirements.txt
    记录需要安装的Python包。目前只需要这几个。因为mysqlclient的特殊,它需要一些以来所以,放在了容器创建的时候单独安装。

    django>=3.0
    django-mysql>=3.8
    django-crispy-forms
    
  3. 创建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
    
  4. 创建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
    
  5. 创建statictemplates文件夹
    存放静态文件和模板文件,具体看Django的文档。

2.3 完善accounts应用
  1. accounts应用文件夹下添加urls.py
    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'),
    ]
    
  2. 在项目account_app下的urls.py注册应用的url文件
    urlpatterns = [
        path('account/', include('accounts.urls')),
    ]
    
  3. 简单的创建几个视图函数
    如下,简单的创建视图函数和相应的html模板文件,在templates文件夹下添加account文件夹与accounts应用相关的模板文件放到其中。至于cssjs和图片文件放在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')
    
2.4 如果不出错的话可以启动应用了

使用命令 docker-compose up --build -d或者可以不用-d。命令的具体细节看官方文档。然后在浏览器查看localhost:8000/account/login等前面文件中定义的链接。

至此算是简单的把项目创建出来了。剩下的就是完善具体的accounts应用下的功能了。

第二步:完善项目

前面的步骤是在创建项目,现在开始进一步完善功能。下面以注册功能为主介绍。

1. 创建注册表单

使用Django内置forms。实现表单、表单字段的校验和使用django-crispy-formsbootstrap4简单的美化表单。所有的具体细节多看官方文档都会知晓。

注册表单文件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

2. 完善注册视图函数

有了表单之后,接着需要处理来自表单的数据和完善注册视图函数的功能。

2.1 简单的添加注册激活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

2.2 添加邮件服务发送激活链接的邮件

如果在Django里直接发送邮件可能会有点慢,所以本项目使用celery来异步发送,选择了redis作为celery的消息和返回结果存储的工具。

  1. 所以需要在docker-compose.yml 加入新的服务。
  celery:
    build: .
    command: celery -A account_app worker -l info
    volumes:
      - .:/app
    depends_on:
      - redis

  redis:
    image: redis
  1. Django配置使用celery和邮件服务
  • account_app项目下创建celery.py
    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()
    
  • 在*account_app/__init__*下配置全局使用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',)
    
  • 在应用下创建accounts/tasks.py
    @shared_task(name="send_email")
    def send_email(mail_subject, message, to):
        email = EmailMessage(
            mail_subject,
            message,
            to=to
        )
    
        email.send()
    
2.3 完善视图函数

完善后的注册视图函数

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'})

至此简单的注册功能算是实现了。其他的功能类似注册功能一样实现。

2.4 注册结果展示

Django实现简单的注册登录_第1张图片

第三步: 继续完善(TODO)

  1. 还没有改变默认的404或其他特殊的界面。
  2. 表单校验可能还有漏洞
  3. 还应该添加好看的邮件模板

最后

本项目实现的功能都是最最简单的,所以作为Django入门的第一个小例子非常合适。自我感觉没人会看到最后,所以本文的源代码链接就放在最后了。欢迎分享更多的知识。

你可能感兴趣的:(django,python,后端,docker,mysql)