第六十八天 BBS项目之四 分组连表查询 路由匹配进阶使用(BBS4文件)

一、内容回顾

# 1 登录页面搭建
-bootsrtap的栅格,form-group, input:form-control
-验证码


# 2 验证码图片的生成
-1 pillow生成一张图片
-2 图片上写文字
-3 设置文字大小,设置文字颜色,设置文字字体格式(ttf)
-4 5位大小写字母,数字
-5 点,线,弧形
-6 放到bytesio,取出来
	img.save(f,'png')
	f.getvalue()
-7 返回给前端  HttpResponse
JsonResponse({code:100,data:asdfasdfasdfasd})
"""
HttpResponse主要用于直接返回字符串类型的数据
JsonResponse只能返回字典或者列表,而且你想返回列表的话,需要加参数 safe=True
JsonResponse也可以将文件转换以后的数据以二进制的方式存储在kv键值对中
JsonResponse({code:100,data:asdfasdfasdfasd})
"""


# 3 登录后台
-取出用户名,密码,验证码
-从session中取出验证码,判断传入的是否一致
-使用它:authenticate 验证用户名密码是否正确  ---》密码是加密的
-django内置user表的密码加密:虽然密码一致,但是生成的加密后的串是不一样的
"""
我们也想自己的密码被加密,我们就可以点开UserInfo.objects.create_user(**register_data)的源代码
发现主要是user.password = make_password(password)在起作用
from django.contrib.auth.hashers import make_password
秘文=make_password('明文')
"""

from django.contrib.auth import authenticate, login as auth_login, logout
auth_login(request,user)
"""
登录成功之后使用auth_login(request,user)记录登录成功以后的状态
"""

二、首页渲染(文章的摘要方面)

<a href="#">
<img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" alt="..." height="50px" width="50px">
</a>
"""
settings的media地址MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
以后再上传的文件是从media文件夹下面开始的,原来就有的默认在static文件夹下!!!!!!!!
model中avatar = models.FileField(upload_to='avatar', default='avatar/default.png')自动存储到media/avatar
urls中开启media文件访问权限path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
"""


{{ article.upanddown_set.all.count}}
"""
这里是在前端显示点赞和点踩数,注意这样写每加载一次页面就会访问一次数据库,数据库压力过大
解决方法为直接在article表中加入字段,下面只写新加入的字段
"""
class Article(models.Model):
    up_num = models.IntegerField(default=0)
    down_num = models.IntegerField(default=0)
    comment_num = models.IntegerField(default=0)
"""
直接在article中写字段能有效降低访问量
"""


# auth_user的迁移migrate必须在扩写之后完成!!!!!!!!!!!!!!!!!!!!!!
不然想改的话需要先去删库,再去删除迁移记录(admin的,auth的)
先去view里面找源码,之后按左上角的瞄准镜进入文件所在位置,删除migrations文件夹下的迁移记录(文件夹本身以及__init__不能删 )
之后在同一目录的auth中的migrations文件夹文件夹下执行统一操作

{{ article.create_time|date:'Y-m-d H:s'}}data过滤器

三、个人站点页面搭建

文字描述为上面一个边框,左边一个标签导航条,右边都是文章的摘要

首先要开一个路由,该路由的目的是接收作者名字信息,之后跳转到作者的页面所以路由为
path('<str:name>', views.site)
————————————————————————————————————————————————
def site(request, name):
    user = models.UserInfo.objects.filter(username=name).first()
    article_list = models.Article.objects.all().filter(blog=user.blog)
    article_list = user.blog.article_set.all()
    if user:
        return render(request, 'site.html', {'article_list':article_list})
    return render(request, '404.html')
"""
1.注意这个路由必须写在最下面,不然会使得其他路由失效
2.我们注意这里需要展示的是这个作者下面的文章
3.article_list = models.Article.objects.all().filter(blog=user.blog)
4.上面的filter使用方式挺重要的,其他地方也可以使用!!!!!!!!!!!!!!!!!!!!!
"""

四、个人站点左侧渲染(重要!!!!!!!!!!!!!!!!!!!!!!!!!!!)

我们的目标要统计这个人的每个分类下的所有文章

1.首先我们用sql
select blog_category.id,blog_category.name,count(blog_article.id), blog_category.blog_id from blog_category left join blog_article on blog_category.id = blog_article.category_id where blog_category.blog_id = 1 group by blog_category.id
"""
1.where的筛选必须在group by分组前面执行
2.首先进行并表操作
select * from blog_category left join blog_article on blog_category.id = blog_article.category_id
3.之后进行分组,确定分组依据,利用count计数
select blog_category.id,blog_category.name,count(blog_article.id), blog_category.blog_id from blog_category left join blog_article on blog_category.id = blog_article.category_id group by blog_category.id
4.最后用where加上查询条件注意where的位置!!!!!!!!!!!!!!!!!!!!!!!!!
"""

2.之后是django的查询方法(正反向查询)
category_res = models.Category.objects.filter(blog=user.blog).values('id').annotate(c=Count('article__id')).values_list('id', 'name', 'c')
category_res1 = models.Article.objects.filter(blog=user.blog).values('category_id').annotate(c=Count('id')).values_list('category_id', 'category__name', 'c')
"""
1.首先进行并表操作annotate可以理解为指定合并的表格,Count用来计算分组数据
models.Category.objects.annotate(c=Count('article__id'))
2.value在前作用是指定分组的依据依照Category的id进行分组
models.Article.objects.values('category_id').annotate(c=Count('id'))
3.之后自己指定需要哪些字段
models.Category.objects.values('id').annotate(c=Count('article__id')).values_list('id', 'name', 'c')
4.最后再自己去加筛选条件和上面的where同理虽然是最后写的,位置却不能错
models.Article.objects.filter(blog=user.blog).values('category_id').annotate(c=Count('id')).values_list('category_id', 'category__name', 'c')
5.因为都是合并以后去查,所以是可以有两个写法的
"""

tag_res = models.Tag.objects.filter(blog=user.blog).values('id').annotate(c=Count('article__id')).values_list('id', 'name', 'c')
tag_res1 = models.Article.objects.filter(blog=user.blog).values('tag__id').annotate(c=Count('id')).values_list('tag__id', 'tag__name', 'c')
# 这里的标签也是同上


# 原生sql转成orm
filter 在annotate前,表示where 条件
valeus 在annotate前,表示分组依据(group by 谁里面就写谁)
filter 在annotate后,表示having 条件
valeus 在annotate后,表示取字段  (id ,name ,c)

where与having的功能其实是一样的 都是用来筛选数据
只不过where用于分组之前的筛选 而having用于分组之后的筛选
为了人为的区分 所以叫where是筛选 having是过滤
select post,avg(salary) from emp where age>30 group by post having avg(salary) > 10000;


 # 随笔档案的查询数据结构
	-分组,单表  文章表---发布时间(年月日十分秒)
    -文章表的数据,在单表中增加一个year_month字段,以它作为分组依据
    id  title     create_time                        year_month
    1    go       2022年9月18日 10时23分14秒          2022年9月
    3    java     2022年9月14日 10时23分14秒          2022年9月
    2    python   2022年7月10日 10时23分14秒          2022年7月
    4    js       2022年8月12日 10时23分14秒          2022年8月

'''
Sales.objects.all().
.annotate(year_month=TruncMonth('create_time'))  # Truncate to month and add to select list
.values('year_month')  # Group By year_month
.annotate(c=Count('id'))  # Select the count of the grouping
.values('year_month', 'c')  # (might be redundant, haven't tested) select month and count
'''

五、个人站点左侧响应点击事件

当前的地址为 http://127.0.0.1:8000/jason
<a href="{{ user.username }}/tag/{{ tag.0 }}.html">
<a href="/{{ user.username }}/tag/{{ tag.0 }}.html">
"""
注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
第一种发送的地址为http://127.0.0.1:8000/jason/jason。。。。。。。。。
第一种发送的地址为http://127.0.0.1:8000/jason。。。。。。。
有斜杠的表示从根路径开始添加,没有用斜杠的则是从当前路径开始添加
"""
<a href="/{{ user.username }}/achieve/{{ data.0|date:'Y/m' }}">
path('<str:name>/archive/<int:year>/<int:month>.html', views.site)
# 因为上面的内容当中有Y/m,下面就得多个接收器




urlpatterns = [
    path('<str:name>', views.site),
    path('<str:name>/tag/<int:tag>.html',views.site),
    path('<str:name>/category/<int:category>.html',views.site),
    path('<str:name>/achieve/<int:year>/<int:month>.html', views.site),
]
——————————————————————————————————————————————————
<a href="/{{ user.username }}/tag/{{ tag.0 }}.html">
<a href="/{{ user.username }}/category/{{ category.0 }}.html">
<a href="/{{ user.username }}/achieve/{{ data.0|date:'Y/m' }}.html">
——————————————————————————————————————————————————
def site(request,name, **kwargs):
    user = models.UserInfo.objects.filter(username=name).first()
    if user:
        article_list = models.Article.objects.all().filter(blog=user.blog)
        tag = kwargs.get('tag')
        category = kwargs.get('category')
        year = kwargs.get('year')
        month = kwargs.get('month')
        if tag:
            article_list = article_list.filter(tag__id=tag)
        elif category:
            article_list = article_list.filter(category__id=category)
        elif year and month:
            article_list = article_list.filter(create_time__year=year,create_time__month=month)
        tag_res = models.Tag.objects.filter(blog=user.blog).values('id').annotate(c=Count('article__id')).values_list('id', 'name', 'c')
        category_res = models.Category.objects.filter(blog=user.blog).values('id').annotate(c=Count('article__id')).values_list('id', 'name', 'c')
        data_res = models.Article.objects.filter(blog=user.blog).annotate(year_month=TruncMonth('create_time')).values('year_month').annotate(c=Count('id')).values_list('year_month', 'c')
        print(data_res)
        return render(request, 'site.html', locals())
    else:
        return render(request, '404.html')
"""
这里做到了使用一个函数去接收传过来的参数巧妙的使用了**kwargs接收多余的参数
article_list = article_list.filter(create_time__year=year,create_time__month=month)这里是获取年月的方式以前好像没见过!!!!!!!!!!!!!
"""

五、路由最简化版本

re_path('^(?P<name>\w+)/(?P<type_name>tag|category|archive)/(?P<condition>\d+).html', views.site),
————————————————————————————————————————————————
def site(request, name, **kwargs):
    # 根据人名查到数据库中,才返回个人站点,如果没有,返回404页面
    user = UserInfo.objects.filter(username=name).first()
    if user:
        # 返回当前这个人所有文章
        article_list = Article.objects.all().filter(blog=user.blog)
        # 要么根据tag,category或时间过滤
        type_name = kwargs.get('type_name')
        condition = kwargs.get('condition')
        if type_name == 'tag':
            article_list = article_list.filter(tag__id=condition)  # 跨表到tag表,过滤tag的id为传入的id号
        elif type_name == 'category':
            article_list = article_list.filter(category_id=condition)  # 跨表到tag表,过滤tag的id为传入的id号
        elif type_name == 'archive':
            year = str(condition)[:4]
            month = str(condition)[4:]
            article_list = article_list.filter(create_time__year=year, create_time__month=month)

        category_res = Category.objects.all().filter(blog=user.blog).values('id').annotate(
            c=Count('article__id')).values_list('id', 'name', 'c')
        tag_res = Tag.objects.all().filter(blog=user.blog).values('id').annotate(c=Count('article__id')).values_list(
            'id', 'name', 'c')
        date_res = Article.objects.all().filter(blog=user.blog).annotate(year_month=TruncMonth('create_time')).values(
            'year_month').annotate(c=Count('id')).values_list('year_month', 'c')
        print(tag_res)
        print(category_res)
        print(date_res)
        return render(request, 'site.html', locals())
    else:
        return render(request, '404.html')

"""
1.这里使用了正则表达式同时能够接受三种地址
2.这里最骚的是把日期转化为20220212这种数字的格式就不用新开路由了
"""
posted @ 2024-03-06 16:53  暧昧的花蝴蝶  阅读(13)  评论(0)    收藏  举报