一.主页的搭建及后台视图函数
路由url.py:
url(r'^index/$', views.index), #主页面路由 url(r'^logout/$', views.logout), # path是要访问图片的路径,document_root:是要开口的路径 #注销路由 url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}), #头像访问路由
1. 对于头像访问,除了static文件夹下内容可由外界页面直接访问外,所有其他文件夹需要开一个访问接口,可在settings.py中配置路径,并配好路由即可访问,不建议使用
# 用户上传文件根路径,如果models中指定上传到avatar/,它就会上传到media/avatar MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
2.主页面视图函数views.py(拿到所有的文章,并提交前端进行渲染):
def index(request): # 取出所有文章(作业,改成分页) article_list=models.Article.objects.all() return render(request,'index.html',{'article_list':article_list})
3.主页前端渲染index.html(借助bootstrap搭建页面):
<!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> </head> <body> <div> <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> #利用auth模块前端模板语法对用户登录状态进行校验,登录显示if中代码渲染语句,并可点击注销的a标签退出, #若未登录则显示else中代码渲染语句 <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-danger"> <div class="panel-heading">同城交友</div> <div class="panel-body"> 详情联系:133394455 </div> </div> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">榴社区</h3> </div> <div class="panel-body"> <a href="">点我</a> </div> </div> <div class="panel panel-warning"> <div class="panel-heading">重金求子</div> <div class="panel-body"> 真心交友 </div> </div> </div> <div class="col-md-8"> {% for article in article_list %} <h5><a href="">{{ article.title }}</a></h5> <ul class="media-list"> <li class="media"> <div class="media-left"> <a href="/{{ article.blog.userinfo.username }}"> {# 对外界页面开放一个访问窗口 url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),#} <img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" width="60" height="60"> </a> </div> <div class="media-body"> {{ article.desc }} </div> </li> </ul> <div> <span><a href="/{{ article.blog.userinfo.username }}">{{ article.blog.userinfo.username }}</a></span> <span>发布于 {{ article.create_time|date:'Y-m-d' }}</span> {# <span>评论({{ article.comment_set.count }})</span>#} {# <span>点赞({{ article.upanddown_set.count}})</span>#} <span class="glyphicon glyphicon-comment">评论({{ article.comment_num}})</span> <span class="glyphicon glyphicon-thumbs-up">点赞({{ article.up_num}})</span> </div> <hr> {% endfor %} </div> <div class="col-md-2"> <div class="panel panel-danger"> <div class="panel-heading">同城交友</div> <div class="panel-body"> 详情联系:133394455 </div> </div> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">榴社区</h3> </div> <div class="panel-body"> <a href="">点我</a> </div> </div> <div class="panel panel-warning"> <div class="panel-heading">重金求子</div> <div class="panel-body"> 真心交友 </div> </div> </div> </div> </div> </div> </body> </html>
几点说明:
a.利用auth模块前端模板语法对用户登录状态进行校验,登录显示if中代码渲染语句,并可点击注销的a标签退出,若未登录则显示else中代码渲染语句,
注销后台借助auth登出:
def logout(request): auth.logout(request) return redirect('/index/')
b.页面按2-8-2栅格系统分布,主要内容占8,首先有一个套着a标签的文章title,然后是一个media-list类里包着作者名及头像,可把头像从media/avatar/中获取,右侧是摘要,
最下面一层是作者,发布时间,评论及点赞,用span标签布局
c.通过<a href="/{{ article.blog.userinfo.username }}">(头像及作者姓名)跳转到个人站点
4.个人站点路由url.py
# url(r'^(?P<username>\w+)/tag/(?P<id>\d+)$', views.site_page), # url(r'^(?P<username>\w+)/category/(?P<id>\d+)$', views.site_page), # url(r'^(?P<username>\w+)/archive/(?P<id>\d+)$', views.site_page), url(r'^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)$', views.site_page), # 一定要注意放到最后 url(r'^(?P<username>\w+)$', views.site_page),
注:通过有名分组将路由作者姓名传到后台,可通过视图函数进行校验
5.个人站点视图函数views.py
from django.db.models import Count from django.db.models.functions import TruncMonth def site_page(request,username,*args,**kwargs): print(args) print(kwargs) user = models.UserInfo.objects.filter(username=username).first() if not user: return render(request, 'error.html') # 用户存在 # 查出用户的所有文章 # 根据用户得到个人站点 blog = user.blog # 取到当前站点下所有文章 article_list = blog.article_set.all() # 过滤 codition = kwargs.get('condition') param = kwargs.get('param') if codition == 'tag': article_list = article_list.filter(tag=param) elif codition == 'category': article_list = article_list.filter(category_id=param) elif codition == 'archive': # 2019-07 year_t = param.split('-') article_list = article_list.filter(create_time__year=year_t[0], create_time__month=year_t[1]) # 查询当前站点下所有标签对应的文章数 # 查询当前站点下所有分类对应的文章数 # 查询所有分类对应的文章数 # 分组查询固定规则: # filter 在annotate前表示where条件 # values 在annotate前表示group by # filter 在annotate后表示having条件 # values 在annotate后表示取值 # category_ret=models.Category.objects.all().values('pk').annotate(cou=Count('article__nid')).values('title','cou') # 查询当前站点下所有分类对应的文章数 category_ret = models.Category.objects.all().filter(blog=blog).annotate(cou=Count('article__nid')).values_list( 'title', 'cou', 'nid') print(category_ret) # 查询当前站点下所有标签对应的文章数 tag_ret = models.Tag.objects.all().filter(blog=blog).annotate(cou=Count('article__nid')).values_list('title', 'cou', 'nid') print(tag_ret) # 查询某年某月下对应的文章数 ''' from django.db.models.functions import TruncMonth Sales.objects .annotate(month=TruncMonth('timestamp')) # Truncate to month and add to select list .values('month') # Group By month .annotate(c=Count('id')) # Select the count of the grouping .values('month', 'c') # (might be redundant, haven't tested) select month and count ''' year_ret = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate( c=Count('nid')).values_list('month', 'c') print(year_ret) # locals 表示把当前作用域下的所有变量传入模板 return render(request, 'site_page.html', locals())
几点说明:
a.首先校验作者是否存在,不存在渲染到error.html页面(参照博客园404页面):
<html> <head> <meta charset='utf-8' /> <link rel="icon" href="//common.cnblogs.com/favicon.ico" type="image/x-icon" /> <title>404_页面不存在 - 博客园</title> <style type='text/css'> body { margin: 8% auto 0; max-width: 550px; min-height: 200px; padding: 10px; font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; font-size: 14px; } p { color: #555; margin: 10px 10px; } img { border: 0px; } .d { color: #404040; } </style> </head> <body> <a href='https://www.cnblogs.com/'><img src='//common.cnblogs.com/images/logo_small.gif' alt='cnblogs' /></a> <p><b>404.</b> 抱歉,您访问的资源不存在。</p> <p class='d'>请确认您输入的网址是否正确,如果问题持续存在,请发邮件至 contact@cnblogs.com 与我们联系。</p> <p><a href='https://www.cnblogs.com/'>返回网站首页</a></p> </body> </html>
b.orm分组过滤规则:
filter 在annotate前表示where条件 values 在annotate前表示group by filter 在annotate后表示having条件 values 在annotate后表示取值
c. 个人站点后台需要提交到前台渲染的数据有: 文章列表:
article_list = blog.article_set.all()
当前站点下所有分类对应的文章数:
category_ret = models.Category.objects.all().filter(blog=blog).annotate(cou=Count('article__nid')).values_list('title', 'cou', 'nid')
当前站点下所有标签对应的文章数:
tag_ret = models.Tag.objects.all().filter(blog=blog).annotate(cou=Count('article__nid')).values_list('title', 'cou', 'nid')
查询某年某月下对应的文章数:
year_ret = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate( c=Count('nid')).values_list('month', 'c')
d.当用户点击分类,标签,随笔档案时,其他页面内容不动,把点击那一栏的文章删选出来即可:
codition = kwargs.get('condition') param = kwargs.get('param') if codition == 'tag': article_list = article_list.filter(tag=param) elif codition == 'category': article_list = article_list.filter(category_id=param) elif codition == 'archive': # 2019-07 year_t = param.split('-') article_list = article_list.filter(create_time__year=year_t[0], create_time__month=year_t[1])
6.个人站点前台渲染site_page.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{ blog.title }}的个人站点</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"> <link rel="stylesheet" href="/static/css/{{ blog.theme }}"> <script src="/static/js/jquery-3.4.1.js"></script> </head> <body> <div class="head"> {{ blog.site_name }} </div> <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"> {% for tag in tag_ret %} <p><a href="/{{ user.username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a></p> {% endfor %} </div> </div> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">随笔分类</h3> </div> <div class="panel-body"> {% for category in category_ret %} <p><a href="/{{ user.username }}/category/{{ category.2 }}">{{ category.0 }}({{ category.1 }})</a></p> {% endfor %} </div> </div> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">随笔档案</h3> </div> <div class="panel-body"> {% for year in year_ret %} <p><a href="/{{ user.username }}/archive/{{ year.0|date:"Y-m" }}">{{ year.0|date:"Y年m月" }}({{ year.1 }})</a></p> {% endfor %} </div> </div> </div> <div class="col-md-9"> {% for article in article_list %} <div style="margin-bottom: 20px"> <h4><a href="">{{ article.title }}</a></h4> <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" height="60" width="60"> </a> </div> <div class="media-body"> {{ article.desc }} </div> </div> <div class="bottom_article pull-right"> <span>posted @</span> <span>发布于 {{ article.create_time|date:"Y-m-d H:i" }}</span> <span>{{ article.blog.userinfo.username }}</span> <span class="glyphicon glyphicon-comment">评论 ({{ article.commit_num }})</span> <span class="glyphicon glyphicon-thumbs-up">点赞 ({{ article.up_num }})</span> </div> </div> {% endfor %} </div> </div> </div> </body> </html>
注:tag_ret, category_ret , year_ret 都是列表套元组的方式,用for循环形成 用户名/tag/tag_id的a链接,内容是title(文章数),按月份截断的显示是年月(文章数),
点击之后通过路由有名分组后将数据又传到个人站点的视图函数进行文章列表的再次过滤,然后提交到前台渲染.
{% for tag in tag_ret %} <p><a href="/{{ user.username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a></p> {% endfor %}