一、首页
路由
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),
]
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.为表添加原信息
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
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 = '评论表'
str(self)方法
二、个人站点
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())