Django实战(进阶)—— 搭建个人博客(未完成)

项目介绍


  • 目的:
  1. 通过完整的开发过程学习Django
  2. 对一本的网站开发流程有全面的认识

  • 主要流程:
    设计网站原型 —> 具体开发 —> 功能测试 —> 部署上线

  • 网站原型的具体设计:
    在这里插入图片描述

开始实战

1. 功能分析

在这里插入图片描述

每个功能模块都相当于一个Django应用!

2. 使用虚拟环境

具体内容参考博主另一篇博文:https://blog.csdn.net/jy_z11121/article/details/103970715

3. 初步创建Blog应用

此应用包括:博文、博客分类
逻辑关系:一篇博客一种分类 / 一篇博客多种分类

3.1 基本构建

创建blog应用
完善settings.py文件,将新应用添加到INSTALLED_APPS数组
完善blog/models.py文件
blog应用的数据库迁移python manage.py migrate
创建超级管理员python manage.py createsuperuser
自定义管理员界面——修改blog/admin.py文件
python manage.py makemigrations
python manage.py migrate
启动python manage.py runserver
进入管理界面,添加一些“实例对象”

构建博客主界面、子界面——blog/views.pyblog/templates/blog_list.htmlblog/templates/blog_detail.html
设置路由——blog/urls.py & urls.py

优化目录结构(主要是模板文件的存放位置。以后再写时就直接写到正确的位置了,这里由于是初学,所以多了此步骤)。

进一步调整网页转移逻辑(对路由设置进行完善、调整,使得逻辑更清晰)
在这里插入图片描述

3.2 网页样式初步设计

网页样式设计(技巧:先在网页端利用F12查看页面代码,在这里进行更改可以直接看到效果,如果是我们想要的效果,则再把它写进代码里)
在这里插入图片描述
写CSS文件、JS文件、图片等静态文件

这里又需要设置myblog/settings.py文件里的相关内容,目的是使我们的程序能够找到我们上面写的静态文件。设置工作如下:
在这里插入图片描述
设置成功后,我们可以通过以下的方式访问到里面的文本文件:
在这里插入图片描述

3.3 使用Bootstrap框架

这里不再自己写前端的内容,将使用CSS框架。首先部署BootStrap框架。

  • 选择CSS框架时应考虑的方面:易用性、兼容性、大小、效果、功能。综合以上因素,我们将使用BootStrap框架:文档齐全,使用简单;兼容较多的浏览器;非轻量级;扁平、简介;组件齐全、响应式。
    在这里插入图片描述
3.3.1 导航栏部分

将相关文件导入到templates/base.html
在这里插入图片描述
按照标准模板的格式重写templates/base.html<body>部分——公共部分——导航栏部分:

{% load staticfiles %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
	<meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<title>{% block title %}{% endblock %}</title>

	<!-- <link rel="stylesheet" href="/static/css/base.css" > -->
	<link rel="stylesheet" href="{% static 'css/base.css' %}" >
	<link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.min.css' %}" >
	{% block header_extends %}{% endblock %}
</head>
<body>
	<!-- ---------------------------Bootstrap框架---------------------------------------- -->
	<div class="navbar navbar-default" role="navigation">
		<div class="container-fluid">
			<div class="navbar-header">
				<a class="navbar-brand" href="{% url 'home' %}">
					我的个人博客网站
				</a>				
			</div>
			<ul class="nav navbar-nav">
				<li><a href="{% url 'home' %}">首页</a></li>
				<li><a href="{% url 'blog_list' %}">博客列表</a></li>
			</ul>
		</div>
	</div>


	<!-- -------------------------------自己写的------------------------------------ -->
	<div class="nav">
		<a class="logo" href="{% url 'home' %}">
			<h3>我的个人博客网站</h3>
		</a>
		<a href="{% url 'home' %}">首页</a>
		<a href="{% url 'blog_list' %}">博客列表</a>
	</div>
	
	<!-- -------------------------------------------------------------------------- -->
	{% block content %} {% endblock %}

	<script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}" ></script>
	<script type="text/javascript" src="{% static 'bootstrap-3.3.7/js/bootstrap.min.js' %}" ></script>
</body>
</html>

在这里插入图片描述
进一步完善导航栏(变为响应式效果):

{% load staticfiles %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>{% block title %}{% endblock %}</title>

	<!-- <link rel="stylesheet" href="/static/css/base.css" > -->
	<link rel="stylesheet" href="{% static 'css/base.css' %}" >
	<link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.min.css' %}" >
	
	<script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}" ></script>
	<script type="text/javascript" src="{% static 'bootstrap-3.3.7/js/bootstrap.min.js' %}" ></script>
	{% block header_extends %}{% endblock %}
</head>
<body>
	<!-- 导航栏 -->
	<div class="navbar navbar-default navbar-fixed-top" role="navigation">
		<div class="container-fluid">
			<div class="navbar-header">
				<a class="navbar-brand" href="{% url 'home' %}">
					我的个人博客网站
				</a>
				<!-- 满足响应式需求;此处的data-target="#navbar-collapse"对应下面id为navbar-collapse区域里的内容 -->	
				<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" 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>
			</div>
			<div id="navbar-collapse" class="collapse navbar-collapse">
				<ul class="nav navbar-nav">
					<li class="{% block nav_home_active %}{% endblock %}">
						<a href="{% url 'home' %}">首页</a>
					</li>
					<li class="{% block nav_blog_active %}{% endblock %}">
						<a href="{% url 'blog_list' %}">博客列表</a>
					</li>
				</ul>				
			</div>
		</div>
	</div>

	{% block content %} {% endblock %}

</body>
</html>

在这里插入图片描述

3.3.2 完善博客界面

下面的部分是对以下三个界面进行调整,其中包括了设计相应的CSS文件:
在这里插入图片描述

导航栏写完后,开始写templates/blog/blog_list.html界面——应用Bootstrap栅格、面板、响应式:

栅格的基本语法示例:

 	<div class="container">
		<div class="row">
			<div class="col-xs-12"> <!-- col-xs-等标签根据需求进行设置,具体请看官方文档 -->
				...	
			</div>
		</div>
	</div>
{% extends 'base.html' %}

{% block title %}
	我的博客网站
{% endblock %}

{% block nav_blog_active %}
	active
{% endblock %}

{% block content %}
	<div class="container">
		<div class="row">

			<!-- ---------------------前8格用来显示博客列表--------------------- -->
			<div class="col-md-8">
				<!-- Bootstrap面板框架 -->	
				<div class="panel panel-warning">
					<div class="panel-heading">
						博客列表({{ blogs|length }}篇)
					</div>
					<div class="panel-body">
						{% for blog in blogs %}
							<a href="{% url 'blog_detail' blog.pk %}">		
								<h3>{{ blog.title }}</h3>
							</a>	
							<p>作者:{{ blog.author }}</p>
							<p>博客分类:
								<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">
									{{ blog.blog_type.type_name }}
								</a>
							</p>
							<p>发表日期:{{ blog.created_time|date:"Y-m-d G:n:s" }}</p>	 
							<p>内容预览:{{ blog.content|truncatechars:30 }}</p> <!-- 只显示内容部分的前30个字 -->
							{% empty %}
								<p>-- 暂无博客,敬请期待 --</p>
						{% endfor %}						
					</div>
				</div>
			</div>

			<!-- ---------------------后4格用来显示博客的所有分类--------------------- -->
			<div class="col-md-4">		
				<!-- Bootstrap面板框架 -->		
				<div class="panel panel-warning">
					<div class="panel-heading">
					    <h3 class="panel-title">博客分类</h3>
					</div>
					<div class="panel-body">
						<ul style="list-style-type:none;">
							{% for blog_type in blog_types %}
								<li>
									<a href="{% url 'blogs_with_type' blog_type.pk %}">{{ blog_type.type_name }}</a>
								</li>
							{% empty %}
								<li>暂无分类</li>
							{% endfor %}
						</ul>
					</div>
				</div>
			</div>
		</div>
	</div>

	<!-- 统计博客数量:有以下两种方式 -->
	<!-- 我的博客数量:{{ blogs_count }}  blogs_count是views.py文件中初始化的 -->
	<!-- 我的博客数量:{{ blogs|length }} -->
{% endblock %}

在这里插入图片描述
进一步调整templates/blog/blog_list.html界面:
在这里插入图片描述
博客列表界面完成后,开始修整templates/blog/blogs_with_type.html界面,此界面是列出某一分类下的所有文章,与博客列表界面相同的是:都需要把指定文章列出来。所以我们使用模板继承的方式重新改写templates/blog/blogs_with_type.html文件,从而使得代码更清晰,只需做稍微调整即可:

{% extends 'blog/blog_list.html' %}

{% block title %}
	我的博客|{{ blog_type.type_name }}
{% endblock %}

{% block blog_list_title %}
	{{ blog_type.type_name }}({{ blogs|length }}篇)
	<a href="{% url 'blog_list'  %}">查看全部博客 </a>
{% endblock %}

在这里插入图片描述
修整templates/blog/blogs_detail.html界面:

未调整前:

 {% extends 'base.html' %}
  
 {% block title %}
 	{{ blog.title }}
 {% endblock %}
  
 {% block nav_blog_active %}
 	active
 {% endblock %}
 
 {% block content %}
 	<h3>{{ blog.title }}</h3>
	<p>博客分类:
		<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">
			{{ blog.blog_type.type_name }}
		</a>
	</p>
	<p>发表日期:{{ blog.created_time|date:"Y-m-d G:n:s" }}</p>	 
	<p>{{ blog.content }}</p>
{% endblock %}

在这里插入图片描述

{% extends 'base.html' %}
{% block title %} {{ blog.title }} {% endblock %}
{% block nav_blog_active %} active {% endblock %}

{% load staticfiles %}
{% block header_extends %}
	<link rel="stylesheet" href="{% static 'blog/blog.css' %}">
{% endblock %}

{% block content %}
	<div class="container">
		<div class="row">
			<div class="col-xs-10 col-xs-offset-1"> <!-- 将col-xs-10向右偏移了一个栅格的宽度 -->
				<h3>{{ blog.title }}</h3>
				<ul class="blog-info-description">
					<li>作者:{{ blog.author }}</li>
					<li>
						博客分类:
						<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">
							{{ blog.blog_type.type_name }}
						</a>
					</li>
					<li>发表日期:{{ blog.created_time|date:"Y-m-d" }}</li>
				</ul>
				<div class="blog-content">{{ blog.content }}</div>				
			</div>
		</div>
	</div>
{% endblock %}

在这里插入图片描述

3.4 分页功能和shell命令行模式

  • 博客文章数过多 --> 全部一次性加载出来太过耗时 —> 分页加载 基于这一结果,我们将学习分页功能;除此之外,本节还涉及使用shell命令行模式快速新增和编辑博客内容、分页器。
  • 主要涉及myblog\blog\templates\blog\blog_list.htmlmyblog\blog\templates\blog\blogs_with_type.html界面的优化。
3.4.1 Shell命令行模式快速添加博客
# 1. 进入Python的命令行模式下
python manage.py shell
# 2. 导入需要的模型类
from blog.models import Blog
# 3. 新增模型类的实例
>>> blog.title = "shell_test1"
>>> blog.content = "shellshellshellshellshellshell"
>>> blog.blog_type = blog_type
>>> from django.contrib.auth.models import User
>>> User.objects.all()
<QuerySet [<User: admin>]>
>>> user = User.objects.all()[0]
>>> blog.author = user
># 4. 保存到数据库
blog.save()


# 其他
Blog.objects.all() # 查看Blog模型类中的所有实例
Blog.objects.all().count() # 查看Blog模型类中实例总数
dir() # 查看已存在的所有实例对象
dir(实例对象) # 查看实例对象的方法或属性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# 为进一步提高效率,我们使用for循环的方式添加博客
>>> from blog.models import Blog, BlogType
>>> blog_type = BlogType.objects.all()[0]
>>> from django.contrib.auth.models import User
>>> user = User.objects.all()[0]
>>> for i in range(1, 31):
...     blog = Blog()
...     blog.title = "for %s" % i
...     blog.content = "forforforforfor: %s" % i
...     blog.blog_type = blog_type
...     blog.author = user
...     blog.save()
...
>>> Blog.objects.all().count()
34

在这里插入图片描述

3.4.2 分页器实现分页

首先修改blog/models.py文件,设置文章展示在页面上时的展示顺序——最新发表的文章在最前面展示:

from django.db import models
from django.contrib.auth.models import User


class BlogType(models.Model):
	type_name = models.CharField(max_length=15)

	def __str__(self):
		return self.type_name

class Blog(models.Model):
	title = models.CharField(max_length=50)
	blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
	content = models.TextField()
	author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
	created_time = models.DateTimeField(auto_now_add=True)
	last_updated_time = models.DateTimeField(auto_now=True)

	def __str__(self):
		return "<Blog: %s>" % self.title

	# 设置文章展示在页面上时的展示顺序:最新发表的文章在最前面展示
	class Meta:
		ordering = ['-created_time']

进入shell命令行模式:

# 1. 引用分页器
from django.core.paginator import Paginator
# 2. 实例化一个分页器
blogs = Blog.objects.all()
paginator = Paginator(blogs, 10) # 每页10项,共4页

>>> paginator.page_range # 查看页数取值范围
range(1, 5)
>>> paginator.num_pages # 查看总页数
4
>>> paginator.count
34

>>> page1 = paginator.page(1) # 取到第一页
>>> page1
<Page 1 of 4>
>>> dir(page1)
['__abstractmethods__', '__class__', '__contains__', '__delattr__', '__dict__', '__dir__', 
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', 
'__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', 
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 
'__weakref__', '_abc_impl', 'count', 'end_index', 'has_next', 'has_other_pages', 'has_previous', 
'index', 'next_page_number', 'number', 'object_list', 'paginator', 'previous_page_number', 'start_index']
>>> page1.object_list
<QuerySet [<Blog: <Blog: for 30>>, <Blog: <Blog: for 29>>, <Blog: <Blog: for 28>>, <Blog: <Blog: for 27>>, <Blog: <Blog: for 26>>, <Blog: <Blog: for 25>>, <Blog: <Blog: for 24>>, <Blog: <Blog: for 23>>, <Blog: <Blog: for 22>>, <Blog: <Blog: for 21>>]>

在这里插入图片描述


3.4.3 分页的使用

前端:发送请求——请求打开具体分页内容
后端:相应请求——返回具体分页内容

修改blog/views.py里的blog_list(request)函数:

  • 未修改前
from django.shortcuts import render, render_to_response, get_object_or_404
from .models import BlogType, Blog


def blog_list(request):
	context = {}
	context['blogs'] = Blog.objects.all()
	context['blogs_count'] = Blog.objects.all().count()
	context['blog_types'] = BlogType.objects.all()
	return render_to_response('blog/blog_list.html', context)
  • 修改后
from django.shortcuts import render, render_to_response, get_object_or_404
from django.core.paginator import Paginator
from .models import BlogType, Blog


def blog_list(request):
	
	blogs_all_list = Blog.objects.all()
	paginator = Paginator(blogs_all_list, 10)
	# 获取url的页面参数,如果此参数不存在,则默认为1(GET请求)
	page_num = request.GET.get('page', 1)
	# page_num不一定是int型的页数,所以使用get_page()方法将page_num转换成一个具体的页数
	page_of_blogs = paginator.get_page(page_num)

	context = {}
	context['page_of_blogs'] = page_of_blogs
	# context['blogs'] = page_of_blogs.object_list 
	# 即context['page_of_blogs'].object_list
	# 所以在使用的时候,我们直接在页面里引用context['page_of_blogs'].object_list即可
	context['blogs_count'] = Blog.objects.all().count()
	context['blog_types'] = BlogType.objects.all()
	return render_to_response('blog/blog_list.html', context)
  • 效果(GET请求,request)
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 由于blog/views.py里的blog_list(request)函数里相关数据的变化,所以对应的需要修改blog_list.html里的相关数据。

  • 使用Bootstrap分页框架完善博客展示界面blog/templates/blog/blog_detail.html

<!-- Bootstrap分页框架 -->	
<div>
	<ul class="pagination">
		{% if page_of_blogs.has_previous %} <!-- 如果上一页存在 -->
		    <li>
		      <a href="?page={{ page_of_blogs.previous_page_number }}" aria-label="Previous">
		        <span aria-hidden="true">&laquo;</span>
		      </a>
		    </li>
		{% else %}
			<span aria-hidden="true">&laquo;</span>
		{% endif %}


		{% for page_num in page_of_blogs.paginator.page_range %}
			<li><a href="?page={{ page_num }}"><span>{{ page_num }}</a></li>
		{% endfor %}	

	    {% if page_of_blogs.has_next %} <!-- 如果下一页存在 -->
	    <li>
	      <a href="?page={{ page_of_blogs.next_page_number }}" aria-label="Next">
	        <span aria-hidden="true">&raquo;</span>
	      </a>
	    </li>
	    {% else %}
			<span aria-hidden="true">&laquo;</span>
	    {% endif %}
	</ul>
</div>

在这里插入图片描述

3.4.4 优化分页展示
  1. 将当前页的页码显示为高亮状态
    在这里插入图片描述
{% for page_num in page_of_blogs.paginator.page_range %}
	<!-- 如果是当前页,则显示为高亮状态 -->
	{% if page_num == page_of_blogs.number %} 
		<li class='active'><a href="?page={{ page_num }}"><span>{{ page_num }}</a></li>
	<!-- 否则,无'active'标签 -->
	{% else %}
		<li><a href="?page={{ page_num }}"><span>{{ page_num }}</a></li>							
	{% endif %}				
{% endfor %}	
  1. 若页码太多,则只显示部分
    在这里插入图片描述

blog/views.py中的blog_list()方法中添加如下语句:

 # 获取当前页码
current_page_num = page_of_blogs.number
# 取当前页码的前后两页,共5页,保存为一个列表
page_range = [current_page_num-2, current_page_num-1, current_page_num, current_page_num+1, current_page_num+2]

context['page_range'] = page_range

效果:
在这里插入图片描述
上面的情况不是我们想要的结果,需进一步优化:

current_page_num = page_of_blogs.number
page_range = list(range(max(1, current_page_num-2), current_page_num)) + \
			list(range(current_page_num, min(current_page_num+2, paginator.num_pages)+1))

context['page_range'] = page_range

效果:
在这里插入图片描述
再进一步优化,优化后的最终效果为:
在这里插入图片描述
对应的代码为:

current_page_num = page_of_blogs.number # 获取当前页码
page_range = list(range(max(1, current_page_num-2), current_page_num)) + \
			list(range(current_page_num, min(current_page_num+2, paginator.num_pages)+1))

if page_range[0] - 1 >= 2:
	page_range.insert(0, '...')
if page_range[0] != 1:
	page_range.insert(0, 1)

if page_range[-1] + 2 < paginator.num_pages:
	page_range.append('...')
if page_range[-1] != paginator.num_pages:
	page_range.append(paginator.num_pages)

context['page_range'] = page_range
{% for page_num in page_range %}
	{% if page_num == page_of_blogs.number %} <!-- 如果是当前页,则显示为高亮状态 -->
		<li class='active'><span>{{ page_num }}</span></a></li>
	{% else %}
		{% if page_num == '...' %}
			<li><span>{{ page_num }}</span></a></li>
		{% else %}
			<li><a href="?page={{ page_num }}">{{ page_num }}</a></li>							
		{% endif %}
	{% endif %}				
{% endfor %}

blog/blog_list.html安排好后,再随之修改blog/blogs_with_type.html页面,修改对应的blog/views.py中的内容。

3.5 博客具体内容页面的优化

  • 主要涉及myblog\blog\templates\blog\blog_detail.html界面的优化。
3.5.1 上下篇博客
  • fliter()(用于筛选出符合条件的条目)常用筛选条件:
    在这里插入图片描述
  • 实例:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

返回类型是QuerySet

  • exclude()(用于排除带有指定内容的条目)常用排除条件:
    【同 fliter()的常用筛选条件】
  • 实例:
    在这里插入图片描述

修改blog/views.pyblog_detail()函数,添加下面两条语句:

context['last_blog'] = Blog.objects.filter(created_time__gt=blog.created_time).last()
context['next_blog'] = Blog.objects.filter(created_time__lt=blog.created_time).first()

myblog\blog\templates\blog\blog_detail.html中添加:

<div class="blog-more">
	<p>上一篇:
		{% if last_blog %}
			<a href="{% url 'blog_detail' last_blog.pk %}">{{last_blog.title}}</a>
		{% else %}
			无
		{% endif %}
	</p>
	
	<p>下一篇:
		{% if next_blog %}
			<a href="{% url 'blog_detail' next_blog.pk%}">{{next_blog.title}}</a>
		{% else %}
			无
		{% endif %}
	</p>
</div>	

效果:
在这里插入图片描述

3.5.2 按月分类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值