BBS项目day03、首页(前端文章布局、分类布局、标签布局)、个人站点(前后端实现)

一、首页

路由

from django.contrib import admin
from django.urls import path, re_path
from app01 import views
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    # 注册
    path('register/', views.register),
    # 登录
    path('login/', views.login),
    # 验证码
    path('get_code/', views.get_code),
    # 首页路由
    path('home/', views.home),
    # 退出系统
    path('logout/', views.logout),
    # 修改密码
    path('set_password/', views.set_password),
    # 放开media文件夹
    re_path('media/(?P.*)', serve, {'document_root': settings.MEDIA_ROOT}),

    # re_path('(?P\w+)/category/(\d+)', views.site),
    # re_path('(?P\w+)/tag/(\d+)', views.site),
    # re_path('(?P\w+)/archive/(\w+)', views.site),

    # 优化以上三个路由
    re_path('(?P\w+)/(?Pcategory|tag|archive)/(?P.*)', views.site),
    re_path('(?P\w+)', views.site),
]


1.首页之前端页面(文章布局)

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页title>

    {% load static %}
    <script src="{% static 'js/jquery.min.js' %}">script>
    <link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}">script>
    <script src="{% static 'layer/layer.js' %}">script>

    {% block css %}

    {% endblock %}

head>
<body>

<nav class="navbar navbar-inverse">
    <div class="container-fluid">
        
        <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 navigationspan>
                <span class="icon-bar">span>
                <span class="icon-bar">span>
                <span class="icon-bar">span>
            button>
            <a class="navbar-brand" href="#">全球最大的博客网站a>
        div>

        
        <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>
                <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="#">Actiona>li>
                        <li><a href="#">Another actiona>li>
                        <li><a href="#">Something else herea>li>
                        <li role="separator" class="divider">li>
                        <li><a href="#">Separated linka>li>
                        <li role="separator" class="divider">li>
                        <li><a href="#">One more separated linka>li>
                    ul>
                li>
            ul>
            <form class="navbar-form navbar-left">
                <div class="form-group">
                    <input type="text" class="form-control" placeholder="Search">
                div>
                <button type="submit" class="btn btn-default">搜索button>
            form>
            <ul class="nav navbar-nav navbar-right">
                {% if request.session.username %}
                    <li style="line-height: 50px;">
                        
                        {# <img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" style="width: 100px;" alt="..."> #}
                        <img src="/media/{{ cur_avatar }}" style="width: 50px; height: 36px;" class="onImg" alt="">
                    li>
                    <li><a href="#">{{ request.session.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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码a>li>
                            <li><a href="#">更改头像a>li>
                            <li><a href="/logout/">退出登录a>li>
                            <li><a href="#">后台管理a>li>
                        ul>
                    li>
                {% else %}
                    <li><a href="/login/">登录a>li>
                    <li><a href="/register/">注册a>li>
                {% endif %}
            ul>
            
            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
                <div class="modal-dialog modal-lg" role="document">
                    <div class="modal-content">
                        <div class="row">
                            <h1 class="text-center">修改密码h1>
                            <div class="col-md-8 col-md-offset-2">
                                <div class="form-group">
                                    用户名:<input type="text" readonly value="{{ request.session.username }}"
                                               class="form-control">
                                div>
                                <div class="form-group">
                                    原密码:<input type="password" id="old_password" class="form-control" msg="原密码必须输入">
                                div>
                                <div class="form-group">
                                    新密码:<input type="password" id="new_password" class="form-control" msg="原密码必须输入">
                                div>
                                <div class="form-group">
                                    确认密码:<input type="password" id="re_password" class="form-control" msg="原密码必须输入">
                                div>
                                <div class="form-group">
                                    <input type="button" value="修改密码" class="btn btn-primary btn-block">
                                div>
                            div>
                        div>
                    div>
                div>
            div>
        div>
    div>
nav>


<div class="container-fluid">
    <div class="row">
        {% block content %}
            <div class="col-md-2">
                <div class="list-group">
                    <a href="#" class="list-group-item active">
                        分类
                    a>
                    <a href="#" class="list-group-item">精华a>
                    <a href="#" class="list-group-item">候选a>
                    <a href="#" class="list-group-item">订阅a>
                    <a href="#" class="list-group-item">关注a>
                div>
            div>

            <div class="col-md-7">
                
                <ul class="media-list">
                    {% for article in article_list %}
                        <li class="media">
                            <h4 class="media-heading"><a href="">{{ article.title }}a>h4>
                            <div class="media-left">
                                <a href="#">
                                    <img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" style="width: 100px;" alt="...">
                                a>
                            div>
                            <div class="media-body">
                                {{ article.desc }}
                            div>
                            <br>
                            <div>
                                
                                
                                <span style="margin-right: 10px;"><a
                                        href="">{{ article.blog.userinfo.username }}a>span>
                                <span style="margin-right: 10px;">{{ article.create_time|date:'Y-m-d' }}span>
                                <span style="margin-right: 10px;"><span
                                        class="glyphicon glyphicon-thumbs-up">span>{{ article.up_num }}span>
                                <span style="margin-right: 10px;"> <span
                                        class="glyphicon glyphicon-thumbs-down">span>{{ article.down_num }}span>
                                <span style="margin-right: 10px;"><span
                                        class="glyphicon glyphicon-comment">span>{{ article.comment_num }}span>
                            div>
                        li>
                        <hr>
                    {% endfor %}
                ul>

            div>

            <div class="col-md-3">
                
                <div class="panel panel-info">
                    <div class="panel-heading">Panel heading without titlediv>
                    <div class="panel-body">
                        Panel content
                    div>
                div>
                <div class="panel panel-success">
                    <div class="panel-heading">Panel heading without titlediv>
                    <div class="panel-body">
                        Panel content
                    div>
                div>
                <div class="panel panel-danger">
                    <div class="panel-heading">Panel heading without titlediv>
                    <div class="panel-body">
                        Panel content
                    div>
                div>
            div>
        {% endblock %}
    div>
div>


<script>
    $(".btn").click(function () {
        // 1.获取参数
        let old_password = $("#old_password").val();
        let new_password = $("#new_password").val();
        let re_password = $("#re_password").val();

        // 2.参数验证
        let ids = ['old_password', 'new_password', 're_password'];
        $.each(ids, function (index, value) {
            if (!$('#' + value).val()) {
                layer.msg($('#' + value).attr('msg'));
                return;
            }
        });

        // 3.发起Ajax请求
        $.ajax({
            url: '/set_password/',
            type: 'post',
            data: {
                old_password: old_password,
                new_password: new_password,
                re_password: re_password,
                csrfmiddlewaretoken: '{{ csrf_token }}'
            },
            success: function (res) {
                if (res.code === 200) {
                    layer.msg(res.msg, {}, function () {
                        location.reload();
                    })
                } else {
                    layer.msg(res.msg, {});
                }
            }
        });

    });
script>

body>
html>
  

2.首页之后端

def home(request):
    '''
        media文件的开放:
            首先需要在配置文件 settings.py 中加入下面这行代码:
            MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
            以后再次注册账户的时候,图片就上传到 media/avatar 文件夹下了,
            但是,当你在前端的 img 的 src="/media/{{ article.blog.userinfo.avatar }}",
            还是不能显示图片,这也说名还没有开放 media 文件夹权限,需要在路由 urls.py 文件中
            加入:
                from django.views.static import serve
                from django.conf import settings
                re_path('media/(?P.*)', serve, {'document_root': settings.MEDIA_ROOT}),
            这个时候图片才能正常的显示在页面
    :param request:
    :return:
    '''
    # 查询文章表的所有数据
    article_list = models.Article.objects.all()

    try:
        cur_username = request.session.get('username')
        # print(cur_username)
        user_obj = models.UserInfo.objects.get(username=cur_username)
        # print(user_obj.avatar)
        cur_avatar = user_obj.avatar
    except:
        return redirect('/login/')

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

3.为表添加原信息

1.admin.py文件

from django.contrib import admin
from app01 import models

admin.site.register(models.UserInfo)
admin.site.register(models.Article)
admin.site.register(models.Article2Tag)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.Comment)
admin.site.register(models.UpAndDown)
admin.site.register(models.Tag)

2.models.py文件

from django.db import models

"""
先写普通字段
之后再写外键字段
"""
from django.contrib.auth.models import AbstractUser


# auth_user的扩展表(用户表)
class UserInfo(AbstractUser):
    '''
        null=True和blank=True的区别:
        null=True:代表的是数据库中字段可以为空
        blank=True:后台系统表单的数据可以为空
    '''
    phone = models.BigIntegerField(verbose_name='手机号', null=True, blank=True)
    # 头像
    avatar = models.FileField(upload_to='avatar/', default='avatar/default.png', verbose_name='用户头像')
    """
    给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png
    """
    create_time = models.DateField(auto_now_add=True)

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

    # 元信息
    class Meta:
        verbose_name_plural = '用户表'


# 博客表
class Blog(models.Model):
    site_name = models.CharField(verbose_name='站点名称', max_length=32)
    site_title = models.CharField(verbose_name='站点标题', max_length=32)
    # 简单模拟 带你认识样式内部原理的操作
    site_theme = models.CharField(verbose_name='站点样式', max_length=64)  # 存css/js的文件路径

    class Meta:
        verbose_name_plural = '博客表'

    def __str__(self):
        return self.site_name


# 分类表
class Category(models.Model):
    name = models.CharField(verbose_name='文章分类', max_length=32)
    blog = models.ForeignKey(to='Blog', null=True, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '分类表'

    def __str__(self):
        return self.name


# 标签表
class Tag(models.Model):
    name = models.CharField(verbose_name='文章标签', max_length=32)
    blog = models.ForeignKey(to='Blog', null=True, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '标签表'

    def __str__(self):
        return self.name


# 文章表
class Article(models.Model):
    title = models.CharField(verbose_name='文章标题', max_length=64)
    desc = models.CharField(verbose_name='文章简介', max_length=255)
    # 文章内容有很多 一般情况下都是使用TextField
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateField(auto_now_add=True)

    # 数据库字段设计优化
    up_num = models.IntegerField(verbose_name='点赞数', default=0)
    down_num = models.IntegerField(verbose_name='点踩数', default=0)
    comment_num = models.IntegerField(verbose_name='评论数', default=0)

    # 外键字段
    blog = models.ForeignKey(to='Blog', null=True, on_delete=models.CASCADE)
    category = models.ForeignKey(to='Category', null=True, on_delete=models.CASCADE)
    tags = models.ManyToManyField(to='Tag',
                                  through='Article2Tag',
                                  through_fields=('article', 'tag')
                                  )

    class Meta:
        verbose_name_plural = '文章表'

    def __str__(self):
        return self.title


# 半自动创建第三章表(文章、标签关联表)
class Article2Tag(models.Model):
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    tag = models.ForeignKey(to='Tag', on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '文章、标签关联表'


# 点赞点彩
class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    is_up = models.BooleanField()  # 传布尔值 存0/1

    class Meta:
        verbose_name_plural = '点赞点彩表'


# 评论表
class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo', null=True, on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', null=True, on_delete=models.CASCADE)
    content = models.CharField(verbose_name='评论内容', max_length=255)
    comment_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True)
    # 自关联
    parent = models.ForeignKey(to='self', null=True, on_delete=models.CASCADE)  # 有些评论就是根评论

    class Meta:
        verbose_name_plural = '评论表'

BBS项目day03、首页(前端文章布局、分类布局、标签布局)、个人站点(前后端实现)_第1张图片

str(self)方法

BBS项目day03、首页(前端文章布局、分类布局、标签布局)、个人站点(前后端实现)_第2张图片

二、个人站点

1.前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-right: 10px;
            color: #999;
        }

        .content {
            font-size: 18px;
            color: #444;
        }
    style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <div class="panel panel-info">
            <div class="panel-heading">文章分类div>
            <div class="panel-body">
                {% for category in category_list %}
                    
                     <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0}} ({{ category.1 }})a>p>
                    
{#                    <p><a href="/{{ username }}/category/{{ category.pk }}">{{ category.name }} ({{ category.count_article_num }})a>p>#}
                {% endfor %}
            div>
        div>
        <div class="panel panel-success">
            <div class="panel-heading">文章标签div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <p><a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }} ({{ tag.1 }})a>p>
                {% endfor %}
            div>
        div>
        <div class="panel panel-danger">
            <div class="panel-heading">日期归档div>
            <div class="panel-body">
                {% for date in date_list %}
                    {#  <p><a href="">{{ date.0 }} ({{ date.1 }})a>p> #}
                    <p><a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }} ({{ date.count_article_nums }})a>p>
                {% endfor %}
            div>
        div>
    div>
    <div class="col-md-9">
        {% for article in article_list %}
            <div>
                <h3><a href="">{{ article.title }}a>h3>
                <div class="content">
                    {{ article.desc }}
                div>
                <div style="margin-top: 10px;" class="pull-right">
                    <span class="s1">postedspan>
                    <span class="s1">@span>
                    <span class="s1">{{ article.create_time|date:'Y-m-d' }}span>
                    <span class="s1">{{ article.blog.userinfo.username }}span>
                    <span class="s1">
                        <span class="glyphicon glyphicon-thumbs-up">span>
                        ({{ article.up_num }})
                    span>
                    <span class="s1">
                        <span class="glyphicon glyphicon-thumbs-down">span>
                        ({{ article.down_num }})
                    span>
                    <span class="s1">
                        <span class="glyphicon glyphicon-comment">span>
                        ({{ article.comment_num }})
                    span>
                div>
                <hr style="margin-top: 35px;"> 
            div>
        {% endfor %}
    div>

{% endblock %}

2.后端

from django.db.models import Count
from django.db.models.functions import TruncYear, TruncMonth, TruncDay, TruncDate


# 个人站点
def site(request, username, **kwargs):
    # def site(request, username, condition, param):
    # def site(request, username):
    # re_path('(?P\w+)/(?Pcategory|tag|archive)/(?P.*)>', views.site),
    # 由于路由中做了有名分组,所以需要参数传递给 site(username, condition, param)

    # 拿到了站点名称
    # 做判断,拿着站点名称去查询用户信息,如果查到了,就可继续,否则返回404页面
    user_obj = models.UserInfo.objects.filter(username=username).first()

    if not user_obj:
        '''
            图片防盗链:通过 Referer参数判断,
            通过这个参数就可以知道你当前的地址是从哪个网页调过来的,然后做验证   
        '''
        return render(request, '404.html')

    # 查询用户自己的所有文章(过滤当前站点的文章)
    blog = user_obj.blog
    article_list = models.Article.objects.filter(blog=blog).all()
    # print(article_list)  # , ]>

    # print('kwargs', kwargs)  # kwargs {'condition': 'category', 'param': '1'}
    if kwargs:
        condition = kwargs.get('condition')
        param = kwargs.get('param')

        if condition == 'category':
            # 按照分类搜索  文章查分类,正向查询,按字段查
            article_list = article_list.filter(category__pk=param)
        elif condition == 'tag':
            # 按照标签来搜索  文章查标签
            article_list = article_list.filter(tags__pk=param)
        elif condition == 'archive':
            # 按照日期来搜索
            # kwargs: {'condition': 'archive', 'param': '2023-08'}
            year, month = param.split('-')
            article_list = article_list.filter(create_time__year=year, create_time__month=month)
        else:
            print('对不起,没有获取到相关数据')

    # 查询当前站点下的所有文章的分类
    # category_list = models.Category.objects.filter(blog=blog).all()
    # 分类查文章数-->外键字段在文章,则反向查询,按表名小写
    category_list = models.Category.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')
    print(category_list)  # 
    # category_list = models.Category.objects.filter(blog=blog).annotate(
    #     count_article_num=Count('article__pk')).values('name', 'count_article_num', 'pk')
    # print(category_list)  # 
    '''
        values和values_list的区别:
        values的输出结果是列表套元组 
            如, 
            前端取值用 obj.0, obj.1, obj.2, ...
        values_list的输出结果是列表套字典 
            如,,
            前端取值用 obj.name, obj.count_article_num
    '''

    # 查询当前站点下的所有标签列表
    # 标签查文章
    tag_list = models.Tag.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')
    print(tag_list)

    # 日期归档
    '''
        id title desc   crate_time    month
        1                2023-08-15    2023-08
        2                2023-08-15    2023-08
        3                2023-08-06    2023-07
        4                2023-08-15    2023-08
        5                2023-08-11    2023-02
        
        原生SQL:select date_format(create_time, 'Y-m') from article group by date_format(create_time, 'Y-m')
        
        -官方提供
        from django.db.models.functions import TruncMonth
        Article.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
    '''
    # date_list = models.Article.objects.annotate(
    #     month=TruncMonth('create_time')).values('month').filter(blog=blog).annotate(
    #     count_article_nums=Count('pk')).values_list('month', 'count_article_nums')
    date_list = models.Article.objects.annotate(
        month=TruncMonth('create_time')).values('month').filter(blog=blog).annotate(
        count_article_nums=Count('pk')).values('month', 'count_article_nums')
    # print(date_list)  # 

    '''
        侧边栏搜索功能
        1.按照分类搜索
            https://www.cnblogs.com/wupeiqi/category/850028.html
        2.按照标签搜索
            https://www.cnblogs.com/wupeiqi/tag/id/
        3.按照日期搜索
            https://www.cnblogs.com/wupeiqi/archive/2018-08.html
    '''

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

你可能感兴趣的:(python01,django,前端,django,python)