Django---博客项目实战

1.urls

from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^get_valid_code/', views.get_valid_code),
    url(r'^register/', views.register),
    url(r'^check_username/', views.check_username),
    url(r'^index/', views.index),
    url(r'^logout/', views.logout),
]

2.settings

"""
Django settings for BBS project.

Generated by 'django-admin startproject' using Django 1.11.25.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'kujfnfd#)my=#u$o(kj__^$_opl^ro=&525*fmki1wpf2z6r2v'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog.apps.BlogConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'BBS.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [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',
            ],
        },
    },
]

WSGI_APPLICATION = 'BBS.wsgi.application'

# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'bbs',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '',
    }
}

# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

AUTH_USER_MODEL = 'blog.UserInfo'

3.models

from django.db import models
from django.contrib.auth.models import AbstractUser


# Create your models here.
# UserInfo这个表,继承AbstractUser,因为要用auth组件


class UserInfo(AbstractUser):
    nid = models.AutoField(primary_key=True)
    # blank=True 只是admin中表单提交的时候,做校验,如果设置成True,就是不校验了
    phone = models.CharField(max_length=32, null=True, blank=True)
    # upload_to需要传一个路径(文件夹自动创建)
    avatar = models.FileField(upload_to='avatar/', default='/static/img/default.png/')
    blog = models.OneToOneField(to='Blog', to_field='nid', null=True)

    class Meta:
        # admin中显示表名
        verbose_name = '用户信息表'
        # 表名去掉s
        verbose_name_plural = verbose_name


class Blog(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    site_name = models.CharField(max_length=32)
    theme = models.CharField(max_length=64)

    def __str__(self):
        return self.site_name


class Category(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)

    def __str__(self):
        return self.title


class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)


class Article(models.Model):
    nid = models.AutoField(primary_key=True)
    # verbose_name='文章标题   修改admin中表单的文字显示
    title = models.CharField(max_length=64, verbose_name='文章标题')
    desc = models.CharField(max_length=255)
    # 大文本TextField()
    content = models.TextField()

    create_time = models.DateTimeField(auto_now_add=True)

    # 因为查询多,写入少,所以加这三个字段,以后不需要再连表查询了
    commit_num=models.IntegerField(default=0)
    up_num=models.IntegerField(default=0)
    down_num=models.IntegerField(default=0)

    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
    category = models.ForeignKey(to='Category', to_field='nid', null=True)
    tag = models.ManyToManyField(to='Tag', through='ArticleToTag', through_fields=('article', 'tag'))

    def __str__(self):
        return self.title


# 手动创建第三张表
class ArticleToTag(models.Model):
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(to='Article', to_field='nid')
    tag = models.ForeignKey(to='Tag', to_field='nid')


class Commit(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo', to_field='nid')
    article = models.ForeignKey(to='Article', to_field='nid')
    content = models.CharField(max_length=255)
    create_time = models.DateTimeField(auto_now_add=True)
    # 这样写是可以的
    # parent_id=models.IntegerField()
    # 自关联
    # parent_id=models.ForeignKey(to='Commit',to_field='nid')
    parent = models.ForeignKey(to='self', to_field='nid', null=True,blank=True)


class UpAndDown(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo', to_field='nid')
    article = models.ForeignKey(to='Article', to_field='nid')
    is_up = models.BooleanField()

    class Meta:
        # 联合唯一,只是为了不写脏数据
        unique_together = (('user', 'article'),)

4.blog/myforms.py

from django import forms
from django.forms import widgets
from blog import models
from django.core.exceptions import ValidationError


class RegForm(forms.Form):
    username = forms.CharField(max_length=18, min_length=2, label="用户名",
                               widget=widgets.TextInput(attrs={'class': 'form-control'}),
                               error_messages={"max_length": '字符长度超出限制', "min_length": '字符长度不够', "required": '用户名不能为空'}
                               )
    password = forms.CharField(max_length=18, min_length=2, label="密码",
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                               error_messages={"max_length": '字符长度超出限制', "min_length": '字符长度不够', "required": '密码不能为空'}
                               )
    re_password = forms.CharField(max_length=18, min_length=2, label="确认密码",
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                                  error_messages={"max_length": '字符长度超出限制', "min_length": '字符长度不够', "required": '密码不能为空'}
                                  )
    email = forms.EmailField(label="邮箱",
                             widget=widgets.TextInput(attrs={'class': 'form-control'}),
                             error_messages={"invalid": '格式不合法', "required": '邮箱为必填'}
                             )

    # 局部校验钩子函数
    def clean_username(self):
        name = self.cleaned_data.get('username')
        # 去数据库校验
        ret = models.UserInfo.objects.filter(username=name).first()
        if ret:
            raise ValidationError('用户名已存在')
        return name

    # 全局校验钩子函数
    def clean(self):
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')
        if pwd and re_pwd:
            if pwd == re_pwd:
                return self.cleaned_data
            else:
                raise ValidationError('两次密码不一致')

5.views

from django.shortcuts import render, HttpResponse,redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
# https://www.cnblogs.com/liuqingzheng/articles/10023849.html
from blog import myforms
from django.contrib import auth
from django.http import JsonResponse
from blog import models


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    # 判断前台发送的请求是不是ajax的请求
    elif request.is_ajax():
        response = {'user': None, 'msg': None}
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')
        if valid_code.upper() == request.session.get('valid_code').upper():
            user = auth.authenticate(request, username=name, password=pwd)
            if user:
                # ajax请求,不能再返回render页面,或者redirect,只能返回字符串
                # 校验通过,一定要登录
                auth.login(request, user)
                response['user'] = name
                response['msg'] = '登录成功'
            else:
                # 用户密码错误
                response['msg'] = '用户名或密码错误'
        else:
            response['msg'] = '验证码错误'
    return JsonResponse(response)


def get_random_color():
    return (
        random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
    )


def get_valid_code(request):
    # pillow是一个图形处理的模块
    # 生成一张图片,模式,大小,颜色
    # img = Image.new('RGB', (320, 35), color=get_random_color())
    # # 保存到本地
    # with open('valid_code.png', 'wb') as f:
    #     img.save(f, 'png')
    # with open('valid_code.png', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    #     在内存中生成一个空文件(把它想象成open('valid_code.png','wb')as f:
    # 一个是在硬盘上一个是在内存中
    # img = Image.new('RGB', (320, 35), color=get_random_color())
    # f=BytesIO()
    # img.save(f,'png')
    # data=f.getvalue()
    # return HttpResponse(data)

    img = Image.new('RGB', (320, 35), color=get_random_color())
    img_draw = ImageDraw.Draw(img)
    font = ImageFont.truetype('static/font/ss.ttf', size=25)
    random_code = ''
    for i in range(5):
        char_num = random.randint(0, 9)
        char_lower = chr(random.randint(97, 122))
        char_upper = chr(random.randint(65, 90))
        char_str = str(random.choice([char_num, char_lower, char_upper]))
        # 坐标,文字,颜色,字体
        img_draw.text((i * 30 + 12, 0), char_str, get_random_color(), font=font)
        random_code += char_str
    # 把验证码保存到session 中
    request.session['valid_code'] = random_code
    f = BytesIO()
    img.save(f, 'png')
    data = f.getvalue()
    return HttpResponse(data)


def register(request):
    if request.method == 'GET':
        my_form = myforms.RegForm()
        return render(request, 'register.html', {'my_form': my_form})
    elif request.is_ajax():
        response = {'status': 100, 'msg': None}
        print(request.POST)
        my_form = myforms.RegForm(request.POST)
        if my_form.is_valid():
            # 存数据,返回正确信息
            # 得用create_user()
            # 定义一个字典,把清理的数据赋给它
            dic = my_form.cleaned_data
            # 移除掉确认密码字段
            dic.pop('re_password')
            # 取出上传的文件对象
            my_file = request.FILES.get('my_file')
            if my_file:
                # 放到字典中
                dic['avatar'] = my_file
            user = models.UserInfo.objects.create_user(**dic)
            print(user.username)
            response['url'] = '/login/'
        else:
            # 返回错误信息
            response['status'] = 101
            response['msg'] = my_form.errors
        return JsonResponse(response)


def check_username(request):
    response = {'status': 100, 'msg': None}
    name = request.POST.get('name')
    user = models.UserInfo.objects.filter(username=name).first()
    if user:
        response['status'] = 101
        response['msg'] = '用户名已被占用'
    return JsonResponse(response)


def index(request):
    article_list=models.Article.objects.all().order_by('-create_time')
    return render(request,'index.html',{'article_list':article_list})


def logout(request):
    auth.logout(request)
    return redirect('/index/')

6.index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css">
    <script src="/static/jquery-3.3.1.js"></script>
    <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.js"></script>

    <title>博客园</title>
    <style>
        .article_bottom span {
            margin-right: 5px;
        }
    </style>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">博客园</a>
            </div>

            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">随笔</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心 <span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</a></li>
                            </ul>
                        </li>
                    {% else %}
                        <li><a href="/login/">登录</a></li>
                        <li><a href="/register/">注册</a></li>
                    {% endif %}

                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-default">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    请联系:8888888888888888888
                </div>
            </div>

            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">交友社区</h3>
                </div>
                <div class="panel-body">
                    <a href="http://www.baidu.com">请点击</a>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            {% for article in article_list %}
                <div>
                    <h4><a href="">{{ article.title }}</a></h4>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object" src="/static/img/default.png" alt="..." height="70" width="70">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article.desc }}
                        </div>
                    </div>
                <div style="margin-top: 10px;" class="article_bottom">
                    <span><a href="">{{ article.blog.userinfo.username }}</a></span>
                    <span>发布于 {{ article.create_time|date:"Y-m-d H:i:s" }}</span>
{#                    反向查询,一对多,按表名小写_set#}
                    <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num}})</a></span>
                    <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                </div>
                </div>
            {% endfor %}

        </div>
        <div class="col-md-3">
            <div class="panel panel-default">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    请联系:8888888888888888888
                </div>
            </div>

            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">交友社区</h3>
                </div>
                <div class="panel-body">
                    <a href="http://www.baidu.com">请点击</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

7.login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css">
    <script src="/static/jquery-3.3.1.js"></script>
    <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.js"></script>

    <title>博客园</title>
    <style>
        .article_bottom span {
            margin-right: 5px;
        }
    </style>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">博客园</a>
            </div>

            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">随笔</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心 <span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</a></li>
                            </ul>
                        </li>
                    {% else %}
                        <li><a href="/login/">登录</a></li>
                        <li><a href="/register/">注册</a></li>
                    {% endif %}

                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-default">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    请联系:8888888888888888888
                </div>
            </div>

            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">交友社区</h3>
                </div>
                <div class="panel-body">
                    <a href="http://www.baidu.com">请点击</a>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            {% for article in article_list %}
                <div>
                    <h4><a href="">{{ article.title }}</a></h4>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object" src="/static/img/default.png" alt="..." height="70" width="70">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article.desc }}
                        </div>
                    </div>
                <div style="margin-top: 10px;" class="article_bottom">
                    <span><a href="">{{ article.blog.userinfo.username }}</a></span>
                    <span>发布于 {{ article.create_time|date:"Y-m-d H:i:s" }}</span>
{#                    反向查询,一对多,按表名小写_set#}
                    <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num}})</a></span>
                    <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                </div>
                </div>
            {% endfor %}

        </div>
        <div class="col-md-3">
            <div class="panel panel-default">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    请联系:8888888888888888888
                </div>
            </div>

            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">交友社区</h3>
                </div>
                <div class="panel-body">
                    <a href="http://www.baidu.com">请点击</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

8.register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>注册</title>
    <style>
        #my_file {
        {#把上传文件的控件隐藏#} display: none;
        }
    </style>
    {#    <script>#}
    {#        //等文档加载完毕之后,再进行操作#}
    {#        window.onload=function () {#}
    {#            #}
    {#        }#}
    {#    </script>#}
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>注册</h1>
            <form action="" id="form">
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }} <span class="error pull-right" style="color: red;"></span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <label for="my_file">头像
                        <img src="/static/img/default.png" alt="" width="80" height="80" id="img_file">
                    </label>

                    <input accept="image/*" type="file" id="my_file">
                </div>

                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>

        </div>
    </div>
</div>
</body>
<script>
    //这个控件指发生变化的事件
    $("#my_file").change(function () {
        //先取出文件(图片)
        var file_obj = $("#my_file")[0].files[0];
        //通过文件阅读器,把图片放到img标签上
        //生成一个文件阅读器对象
        var filereader = new FileReader()
        //把图片对象,读到filereader对象中
        filereader.readAsDataURL(file_obj)
        //filereader.result 这是filereader对象的值
        //
        //$("#img_file").attr('src',filereader.result)
        filereader.onload = function () {
            $("#img_file").attr('src', filereader.result)
        }

    })

    $("#btn").click(function () {
        //因为要上传文件,生成formdata对象
        var formdata = new FormData()
        /*
    formdata.append('name', $("#id_name").val())
    formdata.append('pwd', $("#id_pwd").val())
    formdata.append('re_pwd', $("#id_re_pwd").val())
    formdata.append('email', $("#id_email").val())
    formdata.append('email', $("#id_email").val())
    formdata.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())


    //把文件放到formdata中
    formdata.append('my_file', $("#my_file")[0].files[0])

         */

        //$("#form").serializeArray()把form表单打包,转成对象(列表套字典)
        var arr = $("#form").serializeArray()
        //jquery的循环,传参数:第一个参数是要循环的对象,第二个参数是一个匿名函数
        $.each(arr, function (k, v) {
            console.log(k)
            console.log(v)
            formdata.append(v.name, v.value)
        })
        //把文件放到formdata中
        formdata.append('my_file', $("#my_file")[0].files[0])
        console.log(arr)
        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                //console.log(data)
                if (data.status == 100) {
                    //location.href='/login/'
                    location.href = data.url
                } else {
                    //在之前清除
                    $(".form-group").removeClass('has-error')
                    $(".error").html("")
                    $.each(data.msg, function (key, value) {
                        console.log(key, value)
                        //根据key,通过id取出控件
                        //原来取值
                        //$("#id_username").next().html()
                        //方式一
                        //$("#id_"+key).next().html(value[0])
                        //$("#id_"+key).parent().addClass('has-error')
                        //方式二
                        //处理两次密码不一致的情况
                        if (key=='__all__'){
                            $("#id_re_password").next().html(value[0])
                        }
                        $("#id_" + key).next().html(value[0]).parent().addClass('has-error')
                    })
/*
                    setTimeout(function () {
                        //清除掉父div的has-error
                        //清除掉错误信息(span里的内容)
                        $(".form-group").removeClass('has-error')
                        $(".error").html("")
                    }, 3000)

 */
                }
            }
        })
    })
    
    //name失去焦点,发ajax的请求校验用户是否存在
    //校验,但是只要值不变,只校验一次
    /*
    $("#id_username").change(function () {
        
    })
     */
    //一直会校验
    $("#id_username").blur(function () {
        $.ajax({
            url: '/check_username/',
            type: 'post',
            //name加不加引号都可以
            data: {name:$("#id_username").val(),'csrfmiddlewaretoken': '{{ csrf_token }}' },
        success:function (data) {
            if (data.status==101){
                $("#id_username").next().html(data.msg).parent().addClass('has-error')
            }
            else {
                $("#id_username").next().html(data.msg).parent().removeClass('has-error')
            }
        }
        })
    })

</script>
</html>
原文地址:https://www.cnblogs.com/sanqiansi/p/13211119.html