BBS - 表、登陆、文件上传、注册

1、博客系统得表关系

 

models.pyjavascript

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


class UserInfo(AbstractUser):
    """
    用户信息
    """
    nid = models.AutoField(primary_key=True)
    telephone = models.CharField(max_length=11, null=True, unique=True)
    avatar = models.FileField(upload_to='avatars/', default='/avatars/default.png')
    create_time = models.DateTimeField(verbose_name='建立时间', auto_now_add=True)

    blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE)

    def __str__(self):
        return self.username


class Blog(models.Model):
    """
    博客信息
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='我的博客标题', max_length=64)
    site = models.CharField(verbose_name='我的博客后缀', max_length=32, unique=True)
    theme = models.CharField(verbose_name='博客主题', max_length=32)

    def __str__(self):
        return self.title


class Category(models.Model):
    """
    博主我的文章分类表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='分类标题', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE)

    def __str__(self):
        return self.title


class Tag(models.Model):
    """
    博主我的文章标签表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='标签名称', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE)

    def __str__(self):
        return self.title


class Article(models.Model):
    """
    文章信息
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=50, verbose_name='文章标题')
    desc = models.CharField(max_length=255, verbose_name='文章描述')
    create_time = models.DateTimeField(verbose_name='建立时间')
    category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE)
    user = models.ForeignKey(verbose_name='做者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)

    tags = models.ManyToManyField(
        to='Tag',
        through='Article2Tag',
        through_fields=('article','tag'),
    )

    def __str__(self):
        return self.title


class ArticleDetail(models.Model):
    """
    文章详细表
    """
    nid = models.AutoField(primary_key=True)
    content = models.TextField()
    article = models.OneToOneField(to='Article', to_field='nid', on_delete=models.CASCADE)


class Article2Tag(models.Model):
    """
    多对多,文章、标签得第三张表
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid', on_delete=models.CASCADE)
    tag = models.ForeignKey(verbose_name='标签', to='Tag', to_field='nid', on_delete=models.CASCADE)

    class Meta:
        unique_together = [
            ('article', 'tag'),
        ]

    def __str__(self):
        v = self.article.title + '--' + self.tag.title
        return v


class ArticleUpDown(models.Model):
    """
    点赞,踩灭表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey("UserInfo", null=True, on_delete=models.CASCADE)
    article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE)
    is_up = models.BooleanField(default=True)

    class Meta:
        unique_together = [
            ('article','user'),
        ]


class Comment(models.Model):
    """
    评论表
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE)
    user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)
    content = models.CharField(verbose_name='评论内容', max_length=255)
    create_time = models.DateTimeField(verbose_name='建立时间', auto_now_add=True)

    parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE)

    def __str__(self):
        return self.content

 

操做配置:css

AUTH_USER_MODEL = "blog.UserInfo"    

create database cnblog;

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

import pymysql
pymysql.install_as_MySQLdb()

Tools/Run manage.py Task
makemigrations
migrate

。。。

 

结果:html

 

2、基于Ajax和用户登陆验证

 

验证码得获取:前端

方式一:java

 <img width="260" height="35" src="/get_valid_img/" alt="">
def get_valid_img(request):
    # 方式一
    with open('girl.jpg','rb') as f:
        data = f.read()

    return HttpResponse(data)

 

方式二:mysql

def get_valid_img(request):
    # 方式二
    """
    pip install pillow
    """
    import PIL
    from PIL import Image

    import random

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

    image = Image.new('RGB',(260,35),get_random_color())

    f = open('valid_code.png','wb')  # 存在磁盘上
    image.save(f,'png')

    f = open('valid_code.png','rb')
    data = f.read()
    f.close()

    return HttpResponse(data)

   

 

方式三:jquery

def get_valid_img(request):
    # 方式三
    import PIL
    from PIL import Image

    import random

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

    image = Image.new('RGB',(260,35),get_random_color())

    from io import BytesIO
    f = BytesIO()   # 在内存中建立一张图片 直接返回
    image.save(f,'png')

    data = f.getvalue()

    return HttpResponse(data)

  

 

方式四:ajax

kumo.ttfsql

def get_valid_img(request):
    # 方式四
    import PIL
    from PIL import Image,ImageDraw,ImageFont

    import random

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

    image = Image.new('RGB',(260,35),get_random_color())

    draw = ImageDraw.Draw(image)

    font = ImageFont.truetype('app01/static/font/kumo.ttf',size=32)

    temp = []
    # 生成5个随机字符
    for i in range(5):
        random_num = str(random.randint(0,9))
        random_low_alpha = chr(random.randint(97,122))
        random_upper_alpha = chr(random.randint(65,90))

        random_char = random.choice([random_num,random_low_alpha,random_upper_alpha])
        draw.text((24+i*36,0),random_char,get_random_color(),font=font)

        # 保存随机数
        temp.append(random_char)

    # 造点 造线 造圆
    width = 210
    height = 35
    for i in range(2):
        x1 = random.randint(0,width)
        x2 = random.randint(0,width)
        y1 = random.randint(0,height)
        y2 = random.randint(0,height)
        draw.line((x1,y1,x2,y2),fill=get_random_color())
    for i in range(2):
        draw.point([random.randint(0,width),random.randint(0,height)],fill=get_random_color())
        x = random.randint(0,width)
        y = random.randint(0,height)
        draw.arc((x,y,x+10,y+5),0,360,fill=get_random_color())

    # 在内存中生成图片
    from io import BytesIO
    f = BytesIO()
    image.save(f,'png')
    data = f.getvalue()
    f.close()

    valid_str = ''.join(temp)
    print('valid_str:',valid_str)

    request.session['valid_str'] = valid_str

    return HttpResponse(data)

 

登陆验证:django

    

<body>
<h3>hello: {{ request.user.username }} , {{ request.user.email }}</h3>
</body>
<input type="button" class="btn btn-default login-btn" value="提交">
<span class="error" style="color: red; margin-left: 10px;"></span>
<script src="/static/js/jquery-3.2.1.min.js"></script>
    <script type="text/javascript">
        $('.login-btn').click(function () {
            $.ajax({
                url:'',
                type:'post',
                data:{
                    csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
                    user:$('#user').val(),
                    pwd:$('#pwd').val(),
                    valid_code:$('#valid_code').val()
                },
                success:function (data) {
                    if(data.state){
                        location.href = '/index/'
                    }else{
                        $('.error').text(data.msg)

                    }
                }
                
            })
        })
from django.shortcuts import render,HttpResponse,redirect

from django.http import JsonResponse
from django.contrib import auth


def login(request):
    if request.is_ajax():
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        valid_str = request.session.get('valid_str')

        res = {"state": False, "msg": None}
        if valid_code.upper() == valid_str.upper():
            user = auth.authenticate(username = user,password = pwd)
            if user:
                res["state"] = True
                auth.login(request, user)
            else:
                res["msg"] = "username or pwd error"
        else:
            res["msg"] = "验证码错误"

        return JsonResponse(res)

    return render(request,'login.html')


def index(request):
    if not request.user.username:
        return redirect('/login/')

    return render(request,'index.html')

 

验证码点击刷新:

 <img id="valid_img" width="260" height="35" src="/get_valid_img/" alt="">
//验证码点击刷新
$('#valid_img').click(function () {
    $(this)[0].src += '?'
})    

 

示例:

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


class UserInfo(AbstractUser):
    """
    用户信息
    """
    nid = models.AutoField(primary_key=True)
    telephone = models.CharField(max_length=11, null=True, unique=True)
    avatar = models.FileField(upload_to='avatars/', default='/avatars/default.png')
    create_time = models.DateTimeField(verbose_name='建立时间', auto_now_add=True)

    blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE)

    def __str__(self):
        return self.username


class Blog(models.Model):
    """
    博客信息
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='我的博客标题', max_length=64)
    site = models.CharField(verbose_name='我的博客后缀', max_length=32, unique=True)
    theme = models.CharField(verbose_name='博客主题', max_length=32)

    def __str__(self):
        return self.title


class Category(models.Model):
    """
    博主我的文章分类表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='分类标题', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE)

    def __str__(self):
        return self.title


class Tag(models.Model):
    """
    博主我的文章标签表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='标签名称', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE)

    def __str__(self):
        return self.title


class Article(models.Model):
    """
    文章信息
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=50, verbose_name='文章标题')
    desc = models.CharField(max_length=255, verbose_name='文章描述')
    create_time = models.DateTimeField(verbose_name='建立时间')
    category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE)
    user = models.ForeignKey(verbose_name='做者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)

    tags = models.ManyToManyField(
        to='Tag',
        through='Article2Tag',
        through_fields=('article','tag'),
    )

    def __str__(self):
        return self.title


class ArticleDetail(models.Model):
    """
    文章详细表
    """
    nid = models.AutoField(primary_key=True)
    content = models.TextField()
    article = models.OneToOneField(to='Article', to_field='nid', on_delete=models.CASCADE)


class Article2Tag(models.Model):
    """
    多对多,文章、标签得第三张表
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid', on_delete=models.CASCADE)
    tag = models.ForeignKey(verbose_name='标签', to='Tag', to_field='nid', on_delete=models.CASCADE)

    class Meta:
        unique_together = [
            ('article', 'tag'),
        ]

    def __str__(self):
        v = self.article.title + '--' + self.tag.title
        return v


class ArticleUpDown(models.Model):
    """
    点赞,踩灭表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey("UserInfo", null=True, on_delete=models.CASCADE)
    article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE)
    is_up = models.BooleanField(default=True)

    class Meta:
        unique_together = [
            ('article','user'),
        ]


class Comment(models.Model):
    """
    评论表
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE)
    user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)
    content = models.CharField(verbose_name='评论内容', max_length=255)
    create_time = models.DateTimeField(verbose_name='建立时间', auto_now_add=True)

    parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE)

    def __str__(self):
        return self.content
models.py

 

from django.shortcuts import render,HttpResponse,redirect

from django.http import JsonResponse
from django.contrib import auth


def login(request):
    if request.is_ajax():
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        valid_str = request.session.get('valid_str')

        res = {"state": False, "msg": None}
        if valid_code.upper() == valid_str.upper():
            user = auth.authenticate(username = user,password = pwd)
            if user:
                res["state"] = True
                auth.login(request, user)
            else:
                res["msg"] = "username or pwd error"
        else:
            res["msg"] = "验证码错误"

        return JsonResponse(res)

    return render(request,'login.html')


def index(request):
    if not request.user.username:
        return redirect('/login/')

    return render(request,'index.html')


def get_valid_img(request):
    # 方式四
    import PIL
    from PIL import Image,ImageDraw,ImageFont

    import random

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

    image = Image.new('RGB',(260,35),get_random_color())

    draw = ImageDraw.Draw(image)

    font = ImageFont.truetype('app01/static/font/kumo.ttf',size=32)

    temp = []
    # 生成5个随机字符
    for i in range(5):
        random_num = str(random.randint(0,9))
        random_low_alpha = chr(random.randint(97,122))
        random_upper_alpha = chr(random.randint(65,90))

        random_char = random.choice([random_num,random_low_alpha,random_upper_alpha])
        draw.text((24+i*36,0),random_char,get_random_color(),font=font)

        # 保存随机数
        temp.append(random_char)

    # 造点 造线 造圆
    width = 210
    height = 35
    for i in range(2):
        x1 = random.randint(0,width)
        x2 = random.randint(0,width)
        y1 = random.randint(0,height)
        y2 = random.randint(0,height)
        draw.line((x1,y1,x2,y2),fill=get_random_color())
    for i in range(2):
        draw.point([random.randint(0,width),random.randint(0,height)],fill=get_random_color())
        x = random.randint(0,width)
        y = random.randint(0,height)
        draw.arc((x,y,x+10,y+5),0,360,fill=get_random_color())

    # 在内存中生成图片
    from io import BytesIO
    f = BytesIO()
    image.save(f,'png')
    data = f.getvalue()
    f.close()

    valid_str = ''.join(temp)
    print('valid_str:',valid_str)

    request.session['valid_str'] = valid_str

    return HttpResponse(data)


    # 方式三
    # import PIL
    # from PIL import Image
    #
    # import random
    #
    # def get_random_color():
    #     return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
    #
    # image = Image.new('RGB',(260,35),get_random_color())
    #
    # from io import BytesIO
    # f = BytesIO()   # 在内存中建立一张图片 直接返回
    # image.save(f,'png')
    #
    # data = f.getvalue()
    #
    # return HttpResponse(data)


    # 方式二
    # """
    # pip install pillow
    # """
    # import PIL
    # from PIL import Image
    #
    # import random
    #
    # def get_random_color():
    #     return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
    #
    # image = Image.new('RGB',(260,35),get_random_color())
    #
    # f = open('valid_code.png','wb')  # 存在磁盘上
    # image.save(f,'png')
    #
    # f = open('valid_code.png','rb')
    # data = f.read()
    # f.close()
    #
    # return HttpResponse(data)


    # 方式一
    # with open('girl.jpg','rb') as f:
    #     data = f.read()
    #
    # return HttpResponse(data)
views.py

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h3>hello: {{ request.user.username }} , {{ request.user.email }}</h3>
</body>
</html>
index.html

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css">
    <style type="text/css">
        .container{ margin-top: 100px;}
    </style>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form>
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="user">用户名</label>
                        <input type="text" class="form-control" id="user" placeholder="Username">
                    </div>
                    
                    <div class="form-group">
                        <label for="pwd">密码</label>
                        <input type="password" class="form-control" id="pwd" placeholder="Password">
                    </div>

                    <div class="form-group">
                        <label for="pwd">验证码</label>
                        <div class="row">

                            <div class="col-md-6">
                                <input type="text" class="form-control" id="valid_code">
                            </div>

                            <div class="col-md-6">
                                <img id="valid_img" width="260" height="35" src="/get_valid_img/" alt="">
                            </div>
                        </div>
                    </div>

                    <input type="button" class="btn btn-default login-btn" value="提交">
                    <span class="error" style="color: red; margin-left: 10px;"></span>
                </form>
            </div>
        </div>
    </div>


    <script src="/static/js/jquery-3.2.1.min.js"></script>
    <script type="text/javascript">
        //提交
        $('.login-btn').click(function () {
            $.ajax({
                url:'',
                type:'post',
                data:{
                    csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
                    user:$('#user').val(),
                    pwd:$('#pwd').val(),
                    valid_code:$('#valid_code').val()
                },
                success:function (data) {
                    if(data.state){
                        location.href = '/index/'
                    }else{
                        $('.error').text(data.msg)

                    }
                }
                
            })
        });

        //验证码点击刷新
        $('#valid_img').click(function () {
            $(this)[0].src += '?'
        })
        
    </script>
</body>
</html>
login.html

注:

 css js 静态文件
css js 存在静态文件中 修改以后,client(浏览器)若是没有修改;
需设置一些 浏览器得setting Disable cache

3、form表单、ajax文件上传

django-ajax   http://www.cnblogs.com/yuanchenqi/articles/7638956.html

  


基于form表单提交数据:
默认值:Content—Type = urlencoded
<form action="" method="post" enctype="application/x-www-form-urlencoded">
print(request.POST)
<QueryDict: {'csrfmiddlewaretoken': ['azZWQlmRhdPPXxtRf9e04B31VZLyVJN8Dc1eAuWEMIxnWShqNP5IyxbozXVRjx8A'],
'user': ['alex'],
'avatar': ['girl.jpg']}> # 其实文件并无传上来,只是个名称!
print(request.FILES)
<MultiValueDict: {}>

涉及到上传文件时:Content—Type = form-data
<form action="" method="post" enctype="multipart/form-data">
print(request.POST)
<QueryDict: {'csrfmiddlewaretoken': ['gsAIE42u5Io47YttQO9ExBEFuZiVZ965J5C0odChAd6C6jh2ou0m1xM28XsenXrx'],
'user': ['alex']}>
print(request.FILES)
<MultiValueDict: {'avatar': [<InMemoryUploadedFile: girl.jpg (image/jpeg)>]}>

基于Ajax提交数据:
$('#btn').click(function () {
$.ajax({
url:'',
type:'get',
data:{name:'alex',pwd:'123'},
success:function (data) {
console.log(data)
}

})
})

涉及到上传文件时:
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$('#btn').click(function () {
formdate = new FormData(); // 加var 局部, 不加var就是 全局.
formdate.append("user",$('#user').val());
formdate.append("avatar",$('#avatar')[0].files[0]);
formdate.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val());

$.ajax({
url:'',
type:'post',
data:formdate,
contentType:false,
processData:false,
success:function (data) {
console.log(data)
}

})
})

</script>

<QueryDict: {'user': ['alice'], 'csrfmiddlewaretoken': ['9ipnZT2GROgz1BFiCZzsYTE32MCBPXKWCVrFJ2CtmjY70WtRaFqasPMqGKMUdL5o']}>
<MultiValueDict: {'avatar': [<InMemoryUploadedFile: girl.jpg (image/jpeg)>]}>


def upload(request):
    if request.method == 'POST':
        print(request.POST)
        print(request.FILES)
        obj = request.FILES.get('avatar')

        with open(obj.name,'wb') as f:
            for line in obj:
                f.write(line)
        return HttpResponse('OK')

    return render(request,'upload.html')

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h3>基于form表单提交数据</h3>
{#<form action="" method="post" enctype="application/x-www-form-urlencoded">#}
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <p>用户名 <input type="text" name="user"></p>
    <p>头像 <input type="file" name="avatar"></p>
    <input type="submit">

</form>

<hr>
<h3>基于Ajax提交文件数据</h3>
<form>
    {% csrf_token %}
    <p>用户名 <input type="text" id="user"></p>
    <p>头像 <input type="file" id="avatar"></p>
    <input type="button" value="submit" id="btn">
</form>

<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
    $('#btn').click(function () {
        formdate = new FormData();  // 加var 局部, 不加var就是 全局.
        formdate.append("user",$('#user').val());
        formdate.append("avatar",$('#avatar')[0].files[0]);
        formdate.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val());

        $.ajax({
            url:'',
            type:'post',
            data:formdate,
            contentType:false,
            processData:false,
            success:function (data) {
                console.log(data)
            }

        })
    })

</script>
</body>
</html>

4、form表单得注册页面

          

 

头像默认图片

#avatar{ display: none}
.avatar{ width: 60px; height: 60px;margin-left: 15px;cursor: pointer;}
<div class="form-group">
    <label for="avatar">头像
        <img class="avatar" src="/static/img/default.png" alt="">
    </label>

    <input type="file" id="avatar">
</div>

图像预览

//图像预览
$('#avatar').change(function () {
    var choose_file = $(this)[0].files[0];  //文件对象

    var reader = new FileReader();  // 阅读器对象
    reader.readAsDataURL(choose_file);

    reader.onload = function (ev) {
         $('.avatar').attr('src',reader.result)
    };

    {#$('.avatar').attr('src',reader.result)  // 还没读完,就执行了这句了 出问题了!!#}
    //解决办法   reader.onload

});

 注意:

为何   src="/static/img/default.png"    可访问到 ? 

由于:  settings   配置了   STATIC_URL = '/static/'   

STATIC_URL = '/static/' 
/static/... 对外得文件能够直接访问

 

Ajax 注册:

from django import forms
from django.forms import widgets,ValidationError

from app01.models import UserInfo

class RegForm(forms.Form):
    user = forms.CharField(max_length=8,label='用户名',
                        widget=widgets.TextInput(attrs={'class':'form-control'}))
    pwd = forms.CharField(min_length=4,label='密码',
                          widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    repeat_pwd = forms.CharField(min_length=4,label='确认密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱',
                         widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 局部钩子
    def clean_user(self):
        val = self.cleaned_data.get('user')

        ret = UserInfo.objects.filter(username = val)
        if not ret:
            return val
        else:
            raise ValidationError('该用户已存在')

    # 校验局部钩子得时候 没有办法 拿到 全部得干净数据,
    # 如何 校验 两个 字段呢! 全局钩子

    # 全局钩子 能获得 任何一个干净得 数据   def clean(self):return self.cleaned_data
    def clean(self):
        if self.cleaned_data.get('pwd') == self.cleaned_data.get('repeat_pwd'):
            return self.cleaned_data
        else:
            raise ValidationError('两次密码不一致')


from django.http import JsonResponse

def reg(request):
    if request.method == 'POST':
        res = {'user':None,'error_dict':None}
        form = RegForm(request.POST)

        if form.is_valid():
            print("clean:",form.cleaned_data)  # {"user":'yuan','pwd':"123"}
            print(request.FILES)

            user = form.cleaned_data.get('user')
            pwd = form.cleaned_data.get('pwd')
            email = form.cleaned_data.get('email')
            avatar = request.FILES.get('avatar')
            if avatar:
                # 接受文件对象  将avatar文件对象下载到avatar字段对应的upload_to指定路径 没指定在 根目录下
                user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar)
            else:
                user = UserInfo.objects.create_user(username=user, password=pwd, email=email)

            res['user'] = user.username

        else:
            print('error:',form.errors)  #{"repear_pwd":['',''],"email":['',''],}
            res['error_dict'] = form.errors

        return JsonResponse(res)

    form = RegForm()
    return render(request,'reg.html',locals())
// 注册事件
$('.reg_btn').click(function () {
    formdata = new FormData();
    formdata.append("user",$('#id_user').val());
    formdata.append("pwd",$('#id_pwd').val());
    formdata.append("repeat_pwd",$('#id_repeat_pwd').val());
    formdata.append("email",$('#id_email').val());
    formdata.append("avatar",$('#avatar')[0].files[0]);
    formdata.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val());

    $.ajax({
        url:'',
        type:'post',
        contentType:false,
        processData:false,
        data:formdata,
        success:function (data) {
            if(data.user){
                //注册成功
                location.href = "/login/"
            }else{
                //注册失败
                //清空错误信息
                $('form span').html("");
                $('form .form-group').removeClass('has-error');
                // 加载错误信息
                $.each(data.error_dict,function (field,error_list) {
                    // 全局
                    if(field == "__all__"){
                        $('#id_repeat_pwd').next().text(error_list[0]).css('color','red');
                        $('#id_repeat_pwd').parent().addClass('has-error')
                    }
                    $('#id_'+field).next().text(error_list[0]).css('color','red');
                    $('#id_'+field).parent().addClass('has-error')
                })
            }
        }

    })

})

 

注意点:

1. 局部钩子 与 全局钩子:

def clean_user(self): pass   局部钩子 只能校验某一个字段
def clean(self): pass        能够校验两个不一样得字段,全局钩子,能获得任何一个干净得数据!!
局部钩子得源码:
try
: if isinstance(field, FileField): initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) else: value = field.clean(value) self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e)
全局钩子得源码:
try
: cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data

 

2. 上传文件:

 formdata = new FormData();
formdata.append("avatar",$('#avatar')[0].files[0]); # 文件对象 formdata.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val());
 contentType:false,
 processData:false,
 data:formdata,
。。。

 

3. ajax得局部刷新:

注册失败:

 
 
1. 清空错误信息 
  $('form span').html("");
  $('form .form-group').removeClass('has-error');

2. 加载错误信息 each
  $.each(data.error_dict,function (field,error_list) {
       // 全局  全局得错误信息 field == '__all__'
       if(field == "__all__"){ 
         $('#id_repeat_pwd').next().text(error_list[0]).css('color','red');
           $('#id_repeat_pwd').parent().addClass('has-error')
        }
        $('#id_'+field).next().text(error_list[0]).css('color','red');
        $('#id_'+field).parent().addClass('has-error')
    })

 

4.上传文件 得存放地址:media 配置

 avatar = models.FileField(upload_to='avatars/', default='avatars/default.png')
 FileField  ImgField  传什么数据呢?  文件对象

前端:
formdata.append("avatar",$('#avatar')[0].files[0]); # 文件对象

后台:
avatar = request.FILES.get('avatar') # 文件对象
if avatar:
# 接收文件对象 将avatar文件对象下载到avatar字段对应的upload_to(upload_to='avatars/')指定路径下
user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar)
else:
user = UserInfo.objects.create_user(username=user, password=pwd, email=email)

真实文件:
upload_to='' 存在项目得根目录下 girl.jpg
upload_to='avatars/' 存在项目得根目录下 /avatars/girl.jpg 注意: 相对路径 不是绝对路径,若是upload_to='/avatars/'会报错!!

userinfo:
库里存得是 相对路径!! avatars/girl.jpg

BUT:
用户我的得文件 不该该存在项目得根目录下: ??

知识点:
静态文件 /static/css..js..img.. 用户可直接 url 访问获得
由于:settings 配置 STATIC_URL = '/static/'

用户文件:存用户得我的信息 头像 简历。。。 存在哪里呢???
media 配置 存用户信息 针对用户上传得文件
avatar = models.FileField(upload_to='avatars/', default='avatars/default.png')
        settings

1.MEDIA_ROOT = os.path.join(BASE_DIR,'app01','media') # 针对 upload_to 得相对位置得!!

2.MEDIA_URL = '/media/'                 # 针对 用户经过 url 访问得!!

3.urls.py
from django.views.static import serve
from bbs import settings
re_path(r'media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT}) # 针对 用户经过 url 访问得!!

media 配置以后
用户可直接访问相似于访问/static/img/...


upload_to='avatars/' 用户上传得文件就会存在 /media/avatars/girl.jpg 库里存得是,相对路径! avatars/girl.jpg

  

 
注意:
# avatar = models.FileField(upload_to='avatars/', default='avatars/default.png')
# 若是用户没有上传图片,使用默认得!!avatar 不须要传!!

avatar = request.FILES.get('avatar')
if avatar:
user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar)
else:
user = UserInfo.objects.create_user(username=user, password=pwd, email=email)

 示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>reg</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css">
    <style type="text/css">
        .container{ margin-top: 100px;}
        #avatar{ display: none}
        .avatar{ width: 60px; height: 60px;margin-left: 15px;cursor: pointer;}
    </style>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form action="">
                    {% for field in form %}
                        <div class="form-group">
                            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            {{ field }} <span class="error pull-right"></span>
                        </div>
                    {% endfor %}
                    
                    <div class="form-group">
                        <label for="avatar">头像
                            <img class="avatar" src="/static/img/default.png" alt="">
                        </label>

                        <input type="file" id="avatar">
                    </div>
                

                    <input type="button" class="btn btn-default login-btn reg_btn pull-right" value="提交">
                </form>
            </div>
        </div>
    </div>
    {% csrf_token %}


    <script src="/static/js/jquery-3.2.1.min.js"></script>
    <script type="text/javascript">
        //图像预览
        $('#avatar').change(function () {
            var choose_file = $(this)[0].files[0];  //文件对象

            var reader = new FileReader();  // 阅读器对象
            reader.readAsDataURL(choose_file);

            reader.onload = function (ev) {
                 $('.avatar').attr('src',reader.result)
            };

            {#$('.avatar').attr('src',reader.result)  // 还没读完,就执行了这句了 出问题了!!#}
            //解决办法   reader.onload

        });

        // 注册事件
        $('.reg_btn').click(function () {
            formdata = new FormData();
            formdata.append("user",$('#id_user').val());
            formdata.append("pwd",$('#id_pwd').val());
            formdata.append("repeat_pwd",$('#id_repeat_pwd').val());
            formdata.append("email",$('#id_email').val());
            formdata.append("avatar",$('#avatar')[0].files[0]);
            formdata.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val());

            $.ajax({
                url:'',
                type:'post',
                contentType:false,
                processData:false,
                data:formdata,
                success:function (data) {
                    if(data.user){
                        //注册成功
                        location.href = "/login/"
                    }else{
                        //注册失败
                        //清空错误信息
                        $('form span').html("");
                        $('form .form-group').removeClass('has-error');
                        // 加载错误信息
                        $.each(data.error_dict,function (field,error_list) {
                            // 全局
                            if(field == "__all__"){
                                $('#id_repeat_pwd').next().text(error_list[0]).css('color','red');
                                $('#id_repeat_pwd').parent().addClass('has-error')
                            }
                            $('#id_'+field).next().text(error_list[0]).css('color','red');
                            $('#id_'+field).parent().addClass('has-error')
                        })
                    }
                }

            })

        })
        

    </script>
</body>
</html>
reg.html
from django import forms
from django.forms import widgets,ValidationError

from app01.models import UserInfo

class RegForm(forms.Form):
    user = forms.CharField(max_length=8,label='用户名',
                        widget=widgets.TextInput(attrs={'class':'form-control'}))
    pwd = forms.CharField(min_length=4,label='密码',
                          widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    repeat_pwd = forms.CharField(min_length=4,label='确认密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱',
                         widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 局部钩子
    def clean_user(self):
        val = self.cleaned_data.get('user')

        ret = UserInfo.objects.filter(username = val)
        if not ret:
            return val
        else:
            raise ValidationError('该用户已存在')

    # 校验局部钩子得时候 没有办法 拿到 全部得干净数据,
    # 如何 校验 两个 字段呢! 全局钩子

    # 全局钩子 能获得 任何一个干净得 数据   def clean(self):return self.cleaned_data
    def clean(self):
        if self.cleaned_data.get('pwd') == self.cleaned_data.get('repeat_pwd'):
            return self.cleaned_data
        else:
            raise ValidationError('两次密码不一致')


from django.http import JsonResponse

def reg(request):
    if request.method == 'POST':
        res = {'user':None,'error_dict':None}
        form = RegForm(request.POST)

        if form.is_valid():
            print("clean:",form.cleaned_data)  # {"user":'yuan','pwd':"123"}
            print(request.FILES)

            user = form.cleaned_data.get('user')
            pwd = form.cleaned_data.get('pwd')
            email = form.cleaned_data.get('email')
            avatar = request.FILES.get('avatar')
            if avatar:
                # 接受文件对象  将avatar文件对象下载到avatar字段对应的upload_to指定路径 没指定在 根目录下
                user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar)
            else:
                user = UserInfo.objects.create_user(username=user, password=pwd, email=email)

            res['user'] = user.username

        else:
            print('error:',form.errors)  #{"repear_pwd":['',''],"email":['',''],}
            res['error_dict'] = form.errors

        return JsonResponse(res)

    form = RegForm()
    return render(request,'reg.html',locals())
views.py

 

知识点1:
             jquery对象[0]
jquery对象------------------>DOM
          <------------------
               $(DOM)
知识点2:
    请求形式:
        form   get
        form   post
        ajax   get
        ajax   post
        a标签  get
        地址栏 get
    注意:
        form只有 get post
        ajax 有  get post put delete ...
            查看 get  修改 post  (增 post 删 delete 更新 put)

知识点3:                                   url_encoded
    http请求协议:{"user":"alex","pwd":123}-------------->"user=alex&pwd=123"
                                               form-data
                  {"user":"alex","avatar":obj}----------->

    "GET HTTP1.1 /upload/\r\n content—type='url_encoded'\r\n\r\nuser=alex&pwd=123"

    请求头:
        Content—Type:告诉服务器此次请求数据的格式,默认值:url_encoded
        if 上传文件:Content—Type=form-data

知识点4:
    form对象渲染样式:
          #方式1:
              {{form.as_p}}
          #方式2:
              {{form.user}}
          #方式3:
               {% for field in form %}
               <div>
                   <label for="">{{ field.label }}</label>
                   <div>
                       {{ field }}
                   </div>
               </div>
               {% endfor %}

博客系统的登陆
    ---- 基于PIL模块实现的验证码图片
    ---- 基于session保存验证码
    ---- 刷新验证码

博客系统的注册
    基于ajax和form组件实现注册

    ---设计注册页面


    self.fields:{"字段":字段规则}
    form.is_valid():
         self.errors={}
         self.clean_data={}

    UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar)

    将avatar文件对象下载到avatar字段对应的upload_to指定路径


(1)    media配置:
    针对:
        avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")
        avatar = models.ImageField(upload_to='avatars/', default="/avatars/default.png")
    MEDIA_ROOT=os.path.join(BASE_DIR,"blog","media")


(2) MEDIA_URL="/media/"
    # media 配置
    url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
笔记