码迷,mamicode.com
首页 > 其他好文 > 详细

三【用django2.0来开发】会员注册登录

时间:2018-10-25 19:41:16      阅读:258      评论:0      收藏:0      [点我收藏+]

标签:try   blog   是否一致   date   maxlength   group   ack   btn   follow   

github地址:https://gitee.com/ccnv07/django_example

本章主要讲如何实现会员的前台注册登录, 会涉及到以下模块

  1. 简单的路由设置
  2. 简单的模板操作
  3. 视图以及session的操作
  4. 比较复杂的表单
  5. ajax请求以及json返回资源

使用的还是上一节的model即可

实现注册功能

# account/forms.py
class AccountForm(forms.ModelForm):
    # ... 忽略代码
    def get_first_error(self):
        # 获取所有错误转换成的json格式
        errors = self.errors.get_json_data()
        if errors:
            for key, message_dicts in errors.items():
                # 存在错误则返回第一个错误信息, 返回string
                return message_dicts[0].get(‘message‘)

        return None

创建注册表单

RegisterForm 会继承AccountForm

# account/forms.py
class RegisterForm(AccountForm):
    # 设置场景是新增用户
    # 这个不是django默认的, 是我自己加的, 作用是用来不同场景下的实现不同的自定义规则
    scene = ‘insert‘

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 因为前端框架使用的是bootstrap, 所以需要给所有的表单元素添加class名称
        # 同样的, 添加别的属性也可以使用
        self.字段名.widget.attrs.update({‘html属性名‘, ‘html属性值‘})实现
        for _field in self.fields:
            self.fields[_field].widget.attrs.update({‘class‘: ‘form-control‘})

        # 因为在基类中设置的password字段是非必填项, 而在注册的时候是必填的, 所以设置password的required属性为True
        # 同时我们也不需要status字段, 所以设置为False
        self.fields[‘password‘].required = True
        self.fields[‘status‘].required = False

    class Meta(AccountForm.Meta):
        # 使用自定义的Form, 就必须指定fields or exclude属性, 否则报错
        # 只指定表单中药用到的字段
        fields = (‘account‘, ‘password‘, ‘email‘, ‘phone‘)

    # 新增一个rep_password字段, 让用户输入两次密码, 防止出错
    rep_password = forms.CharField(
        label=‘重复密码‘,
        required=True,
        error_messages={‘required‘: ‘请再次输入密码‘},
        widget=forms.PasswordInput())

    def clean_rep_password(self):
        # 验证两次输入的密码是否一致
        # 因为在clean_password方法中, 已经加密了cleaned_data[‘password‘], 所以这里只能取data[‘password‘]
        if self.data[‘password‘] != self.cleaned_data[‘rep_password‘]:
            raise ValidationError(‘两次输入的密码不一致‘)

        return self.cleaned_data[‘rep_password‘]

创建用户模块注册的路由

先在account目录中创建一个urls.py文件
然后在cms.urls中加载account中的urls.py文件

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path(‘admin/‘, admin.site.urls),
    # include方法参数是urls文件的加载地址, 名字可以自己指定
    path(‘account/‘, include(‘account.urls‘))
]

然后再account.urls.py中创建登录路由

# account/urls.py
from django.urls import path
from . import views

urlpatterns = [
    # 访问的url路径是http://xxx/account/register
    # 把请求转发给register方法
    # 这个路由的名称是account-register
    path(‘register/‘, views.register, name=‘account-register‘),
]

注册的具体逻辑

基本流程:

  1. 把数据放入form表单中
  2. 验证表单
  3. 将表单中的数据保存到数据库
  4. 成功/失败都返回指定资源
# account/views.py
from django.views.decorators.http import require_http_methods
from .forms import RegisterForm
from cms.utils import return_json

# 指定可以请求的方式
@require_http_methods([‘GET‘, ‘POST‘])
def register(request):
    if request.method == ‘POST‘:
        # 把所有POST的数据都放入表单中
        form = RegisterForm(request.POST)
        # 验证表单中的数据是否正确
        if form.is_valid():
            # 将数据保存到数据库
            form.save()
            # 操作成功, 将数据返回给浏览器
            return return_json(url=reverse(‘account-index‘))
        else:
        # 验证失败, 从form中获取到第一个错误信息, 返回给浏览器
            return return_json(code=1, message=form.get_first_error())
    else:
        form = RegisterForm()
        return render(request, ‘account/register.html‘, {‘form‘: form})

在这个方法中加载了一个return_json的方法, 具体代码:

# cms/utils.py
from django.http import JsonResponse

def return_json(code = 0, message = ‘success‘, data = [], url=‘‘):
    return JsonResponse({
        ‘code‘: code,
        ‘url‘: url,
        ‘message‘: message,
    })

这个代码的意思是返回一个Json的资源给浏览器解析

插播一个关于模板的配置

django默认的模板目录在模块/templates中
比如account模块的模板目录就是在account/templates
而在一般的开发过程中会把所有模板放在一起, 所以需要修改配置文件, 指定模板目录的路径到根目录下。

# cms/settings.py
TEMPLATES = [
    {
        ‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘,
        ‘DIRS‘: [
            # 将templates目录放在根目录, 也就是cms/templates中
            os.path.join(BASE_DIR, ‘templates‘),
        ],
        ‘APP_DIRS‘: True,
        ‘OPTIONS‘: {
            ‘context_processors‘: [
                ‘django.template.context_processors.debug‘,
                ‘django.template.context_processors.request‘,
                ‘django.contrib.auth.context_processors.auth‘,
                ‘django.contrib.messages.context_processors.messages‘,
            ],
        },
    },
]

而且静态资源(css,js,fonts,img)文件一般也放在根目录下
还是在cms/settings.py中增加配置

STATIC_URL = ‘/static/‘ # 指定静态文件的访问url前缀
STATICFILES_DIRS = (‘static‘, ) # 指定静态文件的目录地址

这样模板和静态资源的新路径就成了cms/templates, cms/static
样式文件使用的是bootstrap. 大家可以提前下载, 并且提前放入static文件中
然后目录就变为以下这样

cms/
    templates/
    static/
        css/
        fonts/
        js/

创建注册表单模板

在templates中创建layout.html 框架文件
layout.html中包含的是所有模板共用的地方, 并且通过block标签占位符来占位, 别的模板可以继承layout.html, 然后修改block标签的占位符中的内容, 可以大幅度减少代码量

# templates/layout.html
<!-- 加载static模板, 作用是为了使用 {% static ‘filepath‘%} 来加载静态资源-->
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <!-- block名称为title的占位符-->
    <title>{% block title %} {% endblock %}</title>
    <!-- 完全的文件地址是static/css/bootstrap.min.css-->
    <link rel="stylesheet" href="{% static ‘css/bootstrap.min.css‘%}">
</head>

<body>
    {% block body %} {% endblock %}
</body>
<script src="{% static ‘js/jquery.min.js‘ %}"></script>
<script src="{% static ‘js/bootstrap.min.js‘ %}"></script>
<script src="{% static ‘js/layer/layer.js‘ %}"></script>
<script src="{% static ‘js/utils.js‘ %}"></script>
</html>

注册模板文件,templates/account/register.html

<!-- 继承layout.html-->
{% extends ‘layout.html‘ %}
<!-- 将占位符名称为title中的内容改为【注册】-->
{% block title %} 注册 {% endblock %}
{% block body %}
<div class="container">
    <div class="row" style="width:500px">
        <!-- url ‘路由的名称, 就是urls中的name‘-->
        <form action="{% url ‘account-register‘%}" method="post" onsubmit="return post(this)">
            {% csrf_token %}
            <div class="form-group">
                <label for="{{ form.account.id_for_label}}">{{ form.account.label}}</label> {{ form.account}}
            </div>
            <div class="form-group">
                <label for="{{ form.password.id_for_label}}">{{ form.password.label}}</label> {{ form.password}}
            </div>
            <div class="form-group">
                <label for="{{ form.rep_password.id_for_label}}">{{ form.rep_password.label}}</label> {{ form.rep_password}}
            </div>
            <div class="form-group">
                <label for="{{ form.email.id_for_label}}">{{ form.email.label}}</label> {{ form.email}}
            </div>
            <div class="form-group">
                <label for="{{ form.phone.id_for_label}}">{{ form.phone.label}}</label> {{ form.phone}}
            </div>
            <input type="submit" value="提交" class="btn btn-success">
        </form>
    </div>
</div>
{% endblock %}

关于form的重点说明
form.字段名是访问到字段
form.字段名.id_for_label, 是返回字段的id
form.字段名.label, 是label名称
form.字段名 会直接返回表单元素的html
比如form.account就会变成&lt;input type="password" name="password" maxlength="12" minlength="6" class="form-control" required id="id_password" /&gt;

代码中return post(this)的js function代码如下

# static/js/utils.js
function post(form) {
    $.ajax({
        url: $(form).attr(‘action‘),
        method: ‘POST‘,
        dataType: ‘json‘,
        data: $(form).serialize(),
        success: function(data) {
            if (data.code == 0) {
                layer.msg(‘操作成功‘);
                window.location.href = data.url;
            } else {
                layer.msg(data.message);
            }
        }
    })
    return false;
}

启动开发服务器并测试

python manager.py runserver

技术分享图片

实现登录方法

实现登录的方法和注册的流程是一样的, 可以自己尝试实现以下, 如果有难度可以对照我的代码

# account/forms.py
class LoginForm(AccountForm):
    scene = ‘login‘

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for _field in self.fields:
            self.fields[_field].widget.attrs.update({‘class‘: ‘form-control‘})

        # 设置密码为必须输入项
        self.fields[‘password‘].required = True
        self.fields[‘email‘].required = False
        self.fields[‘phone‘].required = False
        self.fields[‘status‘].required = False

    class Meta(AccountForm.Meta):
        # 使用自定义的Form, 就必须指定fields or exclude属性, 否则报错
        fields = (‘account‘, ‘password‘)
# account/views.py
from django.shortcuts import render
from django.urls import reverse
from django.views.decorators.http import require_http_methods
from django.shortcuts import redirect
from .forms import RegisterForm, LoginForm
from .utils import authenticate, login_required
from cms.utils import return_json
from functools import wraps

@login_required(login_url=‘/account/login/‘)
def index(request):
    return render(request, template_name=‘account/index.html‘)

@require_http_methods([‘GET‘, ‘POST‘])
def login(request):
    if request.method == ‘POST‘:
        form = LoginForm(request.POST)

        if form.is_valid():
            user = authenticate(
                request,
                account=form.cleaned_data[‘account‘],
                password=form.cleaned_data[‘password‘])

            if user is not None:
                return return_json(url=reverse(‘account-index‘))
            else:
                return return_json(code=1, message=‘账号或密码不正确‘)
        else:
            return return_json(code=1, message=form.get_first_error())
    else:
        form = LoginForm()
        return render(request, ‘account/login.html‘, {‘form‘: form})
# account/utils.py
from .models import Account
from django.contrib.auth.hashers import check_password
from django.shortcuts import redirect
from functools import wraps

def authenticate(request, account, password):
    try:
        user = Account.objects.get(account=account)
    except Account.DoesNotExist:
        return None

    if not check_password(password, user.password):
        return None

    request.session[‘user_id‘] = user.id
    request.session[‘account‘] = user.password

    return user

def login_required(func=None, login_url=‘‘):
    def wrapper(func):
        @wraps(func)
        def _func(request, *arg, **kwargs):
            if request.session.get(‘user_id‘):
                return func(request, *arg, **kwargs)
            else:
                return redirect(login_url)

        return _func
    return wrapper

request.session就是一个session对象, 可以用来保存用户登录后的信息, 使用方法和dict完全一样。

技术分享图片

三【用django2.0来开发】会员注册登录

标签:try   blog   是否一致   date   maxlength   group   ack   btn   follow   

原文地址:http://blog.51cto.com/a3147972/2308991

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!