Django ---- blog项目学习所得

一、登录功能

  1、采用ajax 提交form表单的方式

  2、后台生成随机验证码,登录时提交验证码

  3、用PLI库生成随机验证码,置于session中,登录时与前台提交的code进行upeer()的验证  

    <div class="col-lg-6">
         <img height="35" width="250" src="/get_code/" alt="">
     </div>

二、首页

1、index.html分别采用头和container的布局

<div class="my_head">
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
                <div class="panel panel-danger">
                <div class="panel-heading">广告信息</div>
                <div class="panel-body">
                    重金求子
                </div>
            </div>
        <div class="col-md-6">
        <div class="col-md-3">        

2、head处理时,如果已经登录,则显示username

     {% if request.user.is_authenticated %}
                        <li><a href="/login/">{{ request.user.username }}</a></li>
                        <li><a href="#"></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><a href="/logout/">退出登录</a></li>
                            </ul>
                        </li>
                    {% else %}
                        <li><a href="/login/">登录</a></li>
                        <li><a href="/register/">注册</a></li>
                    {% endif %}

3、class="media-left"将div分别放于同一行左右侧

class="glyphicon glyphicon-comment"图标的话可以采用bootcss的、也可以用static里的font-awesome的拿来即用
  <div class="col-md-6">
            {% for articles in articles_list %}
                <p><h4><a href="">{{ articles.title }}</a></h4></p>
                <div class="media-left"><img height="60" width="60"
                                             src="/media/{{ articles.user.avatar }}"
                                             alt=""></div>
                <div class="media-right">
                    {{ articles.desc }}
                </div>
                <div style="margin-top: 10px">
                <span><a href="">{{ articles.user.username }}</a></span>&nbsp
                <span>发布于:{{ articles.create_time|date:'Y-m-d' }}</span>&nbsp
                <span class="glyphicon glyphicon-comment">评论数({{ articles.comment_count}})</span>&nbsp
                <span class="glyphicon glyphicon-thumbs-up">点赞数({{ articles.up_count}})</span>&nbsp

                </div>
                <hr>
            {% endfor %}
        </div>
def index(request):
    articles_list = models.Article.objects.all()
    return render(request, 'index.html', locals())

三、注册的头像设计

1、为了使得点击字体或者图片都能发生jquery的change事件,label标签的for属性和input的id属性一样即可

 <div class="form-group">

                    <label for="my_file">头像
                        <img height="60px" width="60px" src="/static/img/default.png" id="id_img">
                    </label>
                    <input type="file" id="my_file">
                </div>

2、读取图片内容到img标签里

 $("#my_file").change(function () {
        alert(123)
        //$("#my_file")[0].files[0]  取出文件
        var obj=$("#my_file")[0].files[0]
        //生成一个文件阅读器
        var read=new FileReader()
        //把文件读到我的对象里
        read.readAsDataURL(obj)
        read.onload=function(){
            $("#id_img").attr('src',read.result)
        }

        {#$("#id_img").attr('aa','bb')#}

    })

3、同时需要将input文件的字去掉

 <style>
        #my_file {
            display: none;
        }
    </style>

4、form表单有两个功能,一个是校验字段,一个是生成input标签

   {% for foo in form_obj %}
                    <div class="form-group">
                        <label for="">{{ foo.label }}</label>
                        {{ foo }} <span class="errors"></span>
                    </div>
   {% endfor %}

5、1和2的方式会在form里面直接提交form表单,此处想用ajax的方式提交,采用3的方法

{#             1、   <input type="submit">#}
{#             2、   <button></button>#}
               3、 <input type="button" id="id_button" class="btn btn-success" value="注册">

四、form组件局部钩子和全局钩子使用

1、在view.py里创建forms类,有全局和局部钩子

from django import forms
from django.forms import widgets


class RegForms(forms.Form):
    name = forms.CharField(max_length=20, min_length=2, label='用户名',
                           widget=widgets.TextInput(attrs={'class': 'form-control'}),
                           error_messages={'max_length': '太长了', 'min_length': '太短了'}
                           )
    pwd = forms.CharField(max_length=20, min_length=2, label='密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                          error_messages={'max_length': '太长了', 'min_length': '太短了'}
                          )
    re_pwd = forms.CharField(max_length=20, min_length=2, label='确认密码',
                             widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                             error_messages={'max_length': '太长了', 'min_length': '太短了'}
                             )
    email = forms.EmailField(label='邮箱',
                             widget=widgets.EmailInput(attrs={'class': 'form-control'}),
                             )

    def clean_name(self):
        name = self.cleaned_data.get('name')
        user = models.UserInfo.objects.filter(username=name).first()
        if user:
            raise ValidationError('用户已经存在')
        else:
            return name

    def clean(self):
        pwd = self.cleaned_data.get('pwd')
        re_pwd = self.cleaned_data.get('re_pwd')
        if pwd == re_pwd:
            return self.cleaned_data
        else:
            raise ValidationError('两次密码不一致')

2、form校验了request.post之后存在两种数据:

 # form_obj.cleaned_data
 # form_obj.errors
def register(request):
    form_obj = RegForms()
    back_msg = {}
    if request.is_ajax():
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        re_pwd = request.POST.get('re_pwd')
        email = request.POST.get('email')
        myfile = request.FILES.get('myfile')
        print(myfile)
        print(re_pwd)
        form_obj = RegForms(request.POST)
        if form_obj.is_valid():
            # form_obj.cleaned_data
            # form_obj.errors
            if myfile:
                user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email, avatar=myfile)
            else:
                user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email)
            back_msg['user'] = name
            back_msg['msg'] = '注册成功'
        else:
            back_msg['msg'] = form_obj.errors
            print(form_obj.errors)
            print(type(form_obj.errors))
        return JsonResponse(back_msg)

    return render(request, 'register.html', {'form_obj': form_obj})

3、ajax提交文件的话,用formdata

  1、each函数,循环

  2、if(index=="__all__")判断是否为全局钩子,

  3、加class=has-error的属性,即被错误提示选中

  4、setTimeout(function ()  定时函数执行某件事

  $("#id_button").click(function () {
        var formdata=new FormData()
        var tt=$("#my_form").serializeArray()
        $.each(tt,function (index,value) {
            //console.log(value.name)
            //console.log(value.value)
            formdata.append(value.name,value.value)
        })
        formdata.append('myfile',$("#my_file")[0].files[0])
       // console.log(tt)

        $.ajax({
            url:'',
            type:'post',
            processData:false,
            contentType:false,
            data:formdata,
            success:function (data) {
                //console.log(data)
                if(data.user){
                    location.href='/login/'
                }else{
                    $.each(data.msg,function (index,value) {
                        console.log(index)
                        console.log(value)
                        if(index=="__all__"){
                            $("#id_re_pwd").next().text(value[0])
                        }
                        $("#id_"+index).next().text(value[0]).parent().addClass('has-error')
                        //$("#id_"+index).next().parent().addClass('has-error')
                    })
                    setTimeout(function () {
                        $(".form-group").removeClass('has-error')
                        $('span').text("")
                    },1000)
                }

            }

        })

    })

五、注册

1、var tt=$("#my_form").serializeArray()  自动的将my_form里的name和value打包起来

2、传文件的话,必须

       processData:false,
            contentType:false,否则会以url-encoded的方法传送了
 $("#id_button").click(function () {
        var formdata=new FormData()
        var tt=$("#my_form").serializeArray()
        $.each(tt,function (index,value) {
            //console.log(value.name)
            //console.log(value.value)
            formdata.append(value.name,value.value)
        })
        formdata.append('myfile',$("#my_file")[0].files[0])
       // console.log(tt)

        $.ajax({
            url:'',
            type:'post',
            processData:false,
            contentType:false,
            data:formdata,
            success:function (data) {
                //console.log(data)
                if(data.user){
                    location.href='/login/'
                }else{
                    $.each(data.msg,function (index,value) {
                        console.log(index)
                        console.log(value)
                        if(index=="__all__"){
                            $("#id_re_pwd").next().text(value[0])
                        }
                        $("#id_"+index).next().text(value[0]).parent().addClass('has-error')
                        //$("#id_"+index).next().parent().addClass('has-error')
                    })
                    setTimeout(function () {
                        $(".form-group").removeClass('has-error')
                        $('span').text("")
                    },1000)
                }

            }

        })

    })

4、create生成数据时,avatar=myfile,即直接传入文件对象即可

def register(request):
    form_obj = RegForms()
    back_msg = {}
    if request.is_ajax():
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        re_pwd = request.POST.get('re_pwd')
        email = request.POST.get('email')
        myfile = request.FILES.get('myfile')
        print(myfile)
        print(re_pwd)
        form_obj = RegForms(request.POST)
        if form_obj.is_valid():
            # form_obj.cleaned_data
            # form_obj.errors
            if myfile:
                user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email, avatar=myfile)
            else:
                user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email)
            back_msg['user'] = name
            back_msg['msg'] = '注册成功'
        else:
            back_msg['msg'] = form_obj.errors
            print(form_obj.errors)
            print(type(form_obj.errors))
        return JsonResponse(back_msg)

    return render(request, 'register.html', {'form_obj': form_obj})

5、models里加入此句的意思 upload_to='avatars/', default="/avatars/default.png",上传到MEDIA_ROOT + avatars路径,前面不能再加media,否则路径多了一层media的路径

settings.py

# 设置用户上传头像的根路径
MEDIA_ROOT=os.path.join(BASE_DIR,'media')

6、默认图片在media/avatars/default.png

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")
    # auto_now_add=True
    '''
    auto_now_add
    配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
    auto_now
    配置上auto_now=True,每次更新数据记录的时候会更新该字段。
    '''
    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

六、media文件夹的配置

1、static文件夹下的内容外部人都可以通过url访问到

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

# 设置用户上传头像的根路径
MEDIA_ROOT=os.path.join(BASE_DIR,'media')

2、用户上传的图片放在static不合适,需要一个专门的media的文件夹存储,直接127.0.0.1/media/avatars/default.png就可以访问到了

from django.views.static import serve

  #
def serve(request, path, document_root=None, show_indexes=False):
url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),

慎用此方法,如不小心,代码会暴露被外部访问到

3、如此,即可在首页index.html里将路径改为   src="/media/{{ articles.user.avatar }},即可访问

  {% for articles in articles_list %}
                <p><h4><a href="">{{ articles.title }}</a></h4></p>
                <div class="media-left"><img height="60" width="60"
                                             src="/media/{{ articles.user.avatar }}"
                                             alt=""></div>
                <div class="media-right">
                    {{ articles.desc }}
                </div>
                <div style="margin-top: 10px">
                <span><a href="">{{ articles.user.username }}</a></span>&nbsp
                <span>发布于:{{ articles.create_time|date:'Y-m-d' }}</span>&nbsp
                <span class="glyphicon glyphicon-comment">评论数({{ articles.comment_count}})</span>&nbsp
                <span class="glyphicon glyphicon-thumbs-up">点赞数({{ articles.up_count}})</span>&nbsp

                </div>

                <hr>
            {% endfor %}

七、分组查询

1、views.py

 url(r'^(?P<username>\w+)/(?P<condition>tag|category|time|\w+)/(?P<search>.*)', views.homesite),
 url(r'^(?P<username>\w+)/', views.homesite),

2、后台数据分析

 2.1 blog = user.blog  此为查找时的对象
 2.2 分组查询1 group by谁,用谁做基表
    #         2 filter在前,表示查询  ,filter在后,表示过滤,having
    #          3 values在前,表示group by  在后,取字段
def homesite(request, username, **kwargs):
    print(kwargs)
    username=username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'errors.html')
    blog = user.blog
    article_list = models.Article.objects.filter(user=user)
    if kwargs:
        condition=kwargs.get('condition')
        search=kwargs.get('search')
        if condition=='tag':
            article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
        elif condition=='category':
            article_list = models.Article.objects.filter(user=user).filter(category__title=search)
        elif condition=='time':
            ll=search.split('-')
            article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[0],create_time__month=ll[1])
        else:
            return render(request, 'errors.html')

    # print(article_list)
    from django.db.models import Count
    # 查询每个标签下的文章数(分组查询)
    # 分组查询1 group by谁,用谁做基表
    #         2 filter在前,表示查询  ,filter在后,表示过滤,having
    #          3 values在前,表示group by  在后,取字段
    # 查询当前站点下每个标签下的文章数(分组查询)
    tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
    # 查询当前站点下每个分类下的文章数
    category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
                                                                                                             'c')
    from django.db.models.functions import TruncMonth
    # 查询当前站点每个月份下的文章数
    # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
    # for i in time_count:
    #     print(i.title)
    #     print(i.y_m)
    time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
        c=Count('y_m')).values_list('y_m', 'c')

    print(tag_count)
    print(category_count)
    print(time_count)

    # 统计每个出版社书籍个数
    # Publish.object.all().annotate(Count('book__title'))

    return render(request, 'homesite.html', locals())

2、homesite.html

   1、用admin添加数据

  2、{{ foo.0 }}({{ foo.1 }})       -------------  xxxxxxxxxxx(xx)

<div class="container-fluid" style="margin-top: 10px">
    <div class="row">
        <div class="col-md-3">

            <div class="panel panel-danger">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                    {% for foo in tag_count %}
                        <p><a href="/{{ username }}/tag/{{ foo.0 }}">{{ foo.0 }}({{ foo.1 }})</a></p>

                    {% endfor %}

                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">随笔分类</div>
                <div class="panel-body">
                    {% for foo in category_count %}
                        <p><a href="/{{ username }}/category/{{ foo.0 }}">{{ foo.0 }}({{ foo.1 }})</a></p>

                    {% endfor %}
                </div>
            </div>
            <div class="panel panel-danger">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in time_count %}
                        <p><a href="/{{ username }}/time/{{ foo.0|date:'Y-m' }}">{{ foo.0|date:'Y-m' }}({{ foo.1 }})</a></p>

                    {% endfor %}

                </div>
            </div>


        </div>

八、个人站点路由设计

将此条至于最下方的目的,前面都匹配不了的时候,到此可以匹配成功进入视图函数,有则看,无资源则显示404

 url(r'^(?P<username>\w+)/', views.homesite),

九、个人站点过滤

1、总的设计

def homesite(request, username, **kwargs):
    print(kwargs)
    username=username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'errors.html')
    blog = user.blog
    article_list = models.Article.objects.filter(user=user)
    if kwargs:
        condition=kwargs.get('condition')
        search=kwargs.get('search')
        if condition=='tag':
            article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
        elif condition=='category':
            article_list = models.Article.objects.filter(user=user).filter(category__title=search)
        elif condition=='time':
            ll=search.split('-')
            article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[0],create_time__month=ll[1])
        else:
            return render(request, 'errors.html')

    # print(article_list)
    from django.db.models import Count
    # 查询每个标签下的文章数(分组查询)
    # 分组查询1 group by谁,用谁做基表
    #         2 filter在前,表示查询  ,filter在后,表示过滤,having
    #          3 values在前,表示group by  在后,取字段
    # 查询当前站点下每个标签下的文章数(分组查询)
    tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
    # 查询当前站点下每个分类下的文章数
    category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
                                                                                                             'c')
    from django.db.models.functions import TruncMonth
    # 查询当前站点每个月份下的文章数
    # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
    # for i in time_count:
    #     print(i.title)
    #     print(i.y_m)
    time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
        c=Count('y_m')).values_list('y_m', 'c')

    print(tag_count)
    print(category_count)
    print(time_count)

    # 统计每个出版社书籍个数
    # Publish.object.all().annotate(Count('book__title'))

    return render(request, 'homesite.html', locals())

2、按时间分类,用TruncMonth截断年月,会在原表的基础上在加一条时间为年月的数据,再以此分类(group by谁,用谁做基表)

  from django.db.models.functions import TruncMonth
    # 查询当前站点每个月份下的文章数
    # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
    # for i in time_count:
    #     print(i.title)
    #     print(i.y_m)
    time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
        c=Count('y_m')).values_list('y_m', 'c')

3、分类取得数据,页面展示article_list数据,只会显示符合的文章show

 if kwargs:
        condition=kwargs.get('condition')
        search=kwargs.get('search')
        if condition=='tag':
            article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
        elif condition=='category':
            article_list = models.Article.objects.filter(user=user).filter(category__title=search)
        elif condition=='time':
            ll=search.split('-')
            article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[0],create_time__month=ll[1])
        else:
            return render(request, 'errors.html')

4、相应的前台html是如下,加入a连接和url的处理,可以看得相应的数据

   <div class="panel panel-danger">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                    {% for foo in tag_count %}
                        <p><a href="/{{ username }}/tag/{{ foo.0 }}">{{ foo.0 }}({{ foo.1 }})</a></p>

                    {% endfor %}

                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">随笔分类</div>
                <div class="panel-body">
                    {% for foo in category_count %}
                        <p><a href="/{{ username }}/category/{{ foo.0 }}">{{ foo.0 }}({{ foo.1 }})</a></p>

                    {% endfor %}
                </div>
            </div>
            <div class="panel panel-danger">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in time_count %}
                        <p><a href="/{{ username }}/time/{{ foo.0|date:'Y-m' }}">{{ foo.0|date:'Y-m' }}({{ foo.1 }})</a></p>

                    {% endfor %}

                </div>
            </div>

十、后台管理页面

猜你喜欢

转载自www.cnblogs.com/di2wu/p/10111881.html
今日推荐