一、首页
路由
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),
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
re_path('(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.site),
re_path('(?P<username>\w+)', views.site),
]
在admin中模拟数据先在admin.py中注册表
from django.contrib import admin
from app01 import models
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.Article)
admin.site.register(models.Article2Tag)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)
admin.site.register(models.Tag)
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 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>
<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="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></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 title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-success">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
{% endblock %}
</div>
</div>
<script>
$(".btn").click(function () {
let old_password = $("#old_password").val();
let new_password = $("#new_password").val();
let re_password = $("#re_password").val();
let ids = ['old_password', 'new_password', 're_password'];
$.each(ids, function (index, value) {
if (!$('#' + value).val()) {
layer.msg($('#' + value).attr('msg'));
return;
}
});
$.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<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
这个时候图片才能正常的显示在页面
:param request:
:return:
'''
article_list = models.Article.objects.all()
try:
cur_username = request.session.get('username')
user_obj = models.UserInfo.objects.get(username=cur_username)
cur_avatar = user_obj.avatar
except:
return redirect('/login/')
return render(request, 'home.html', locals())
3.为表添加原信息
from django.db import models
"""
先写普通字段
之后再写外键字段
"""
from django.contrib.auth.models import AbstractUser
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)
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)
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()
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 = '评论表'
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5e4cbde13b89667967fa356ff24c82ee.png)
str(self)方法
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8d68e521be62c031a9df552fde8d13b0.png)
二、个人站点
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">posted</span>
<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):
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()
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':
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).annotate(
count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')
print(category_list)
'''
values和values_list的区别:
values的输出结果是列表套元组
如,<QuerySet [('xn', 2)]>
前端取值用 obj.0, obj.1, obj.2, ...
values_list的输出结果是列表套字典
如,<QuerySet [{'name': 'xn', 'count_article_num': 2}]>,
前端取值用 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('month', 'count_article_nums')
'''
侧边栏搜索功能
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())