前言

在学习Python的过程中,觉得有必要学一学Python做web开发,而django作为一种优秀的Python Web框架,自然成为了我的首选,经过不到一个月的学习,总算搞清楚了django的大部分结构和基本的语法,如果你想学django,就和我一起来做一个用django开发的小型个人博客吧!

了解django

django是遵循MVC设计模式的框架,MVC是Model、View、Controller这三个单词的缩写,分别代表了模型,视图,控制器。对于我个人的理解来讲,模型用来与数据库进行交互,视图用来封装html、js、css,控制器则用来处理程序的请求、逻辑结构等。

准备工作

Python3+django2.0+PycharmPro2018

创建项目

你可以使用Pycharm创建,更加方便。

你也可以使用命令行创建,django-admin startproject myblog,当然前提是你已经安装了django,并且cd到了你想把项目创建到哪个目录的具体位置。一行命令创建了一个myblog的项目,接下来我们就该创建app了,先来讲下app与项目之间的关系。

项目和app之间的关系

对于django来说,项目只是一个大框架,他的具体功能还需要在app里面实现,app所对应的就是一个一个的功能模块,比如拿chabug来说,有用户模块,也有文章模块,也有专题模块。

你可以进入到项目的目录,然后这样来创建apppython3 manage.py startapp blog

运行

在Pycharm里面你只需要点击右上角的运行按钮就可以运行了,默认是运行在本地的8000端口,也就是127.0.0.1:8000,如果你需要在命令行里面运行,你可以这样python3 manage.py runserver 8000,当然这样运行的只能在本地访问,如何把我们的项目发布出去对公网用户开放呢?下面会讲到。

项目结构

manage.py 项目管理脚本,你可以通过python3 manage.py help查看你能够干什么
settings.py 项目的设置都存放在这
urls.py 这个是用来配置项目的url,例如127.0.0.1/chabug/,或者127.0.0.1/chuyu
wsgi.py 部署的时候用到的,一般上用不到

URL与视图

视图

视图一般写在APP的views.py中,并且第一个参数永远是request对象,这个对象包含了一些请求信息,比如:请求方法GET、POST,头部headers信息等等,然后视图必须返回一个HttpResponseBase,比如下面这个:

from django.shortcuts import render,HttpResponse

def hello(request):
    return HttpResponse("hello")

当然,你现在并不能在浏览器中看到HttpResponse返回的hello,因为我们还没有定义urls.py中的内容,下面就跟我来配置一波把!

URL映射

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

这是默认的配置,其中admin这一条是django系统自带的映射后台,不用管他。我们来添加一条url来映射我们的hello,首先从app导入views.py

form blog import views

然后添加一条映射关系

from django.contrib import admin
from django.urls import path
from blog import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.hello),
]

django会自动从urlpatterns中寻找当前请求url所对应的规则,返回相应的信息。

这个时候在访问127.0.0.1:8000则会出现我们在视图中返回的HttpResponse信息hello

URL传参

两种方法

views.py

def A(request,id):
    text = "你请求的id是:%s " % id
    return HttpResponse(text)

urls.py

from django.contrib import admin
from django.urls import path
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.hello),
    path('A/<id>', views.A),
]

你可以这样访问127.0.0.1/A/id

第二种

views.py

def B(request):
    id = request.GET.get['id']
    text = "你请求的id是:%s " % id
    return HttpResponse(text)

urls.py

from django.contrib import admin
from django.urls import path
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.hello),
    path('B/', views.B),
]

你可以这样访问 127.0.0.1:8000/B/?id=1

URL反转

之前我们都是通过url来访问视图函数。有时候我们知道这个视图函数,但是想反转回他的url。这时候就可以通过 reverse 来实现。示例代码如下:

reverse("list")
> /book/list/

传递参数

reverse("book:detail",kwargs={"book_id":1})
> /book/detail/1

URL和视图我们就暂时讲到这里,接下来我们要开始写了,模板的知识会融在其中。

创建模型

from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=50)     #文章标题
    category = models.CharField(max_length=50, blank=True)  #文章分类
    datetime = models.DateTimeField(auto_now_add=True)  #文章发表日期
    content = models.TextField(blank=True, null=True)   #文章内容

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['-datetime']    #以日期倒序

创建完模型后我们需要把模型同步到数据库中

python3 manage.py makemigrations
python3 manage.py migrate

然后在admin.py中注册下我们创建的模型

from django.contrib import admin
from blog.models import Article
# Register your models here.
admin.site.register(Article)

注册模型让我们在django自带的后台管理中显示出来127.0.0.1:8000/admin

发布文章什么的都可以在这操作。

URL设计

我直接贴我的代码

from django.contrib import admin
from django.urls import path
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name='index'),
    path('<int:id>/', views.detail, name='detail'),
    path('archives/', views.archives, name='archives'),
    path('tag/<str:tag>', views.tags, name='tag'),
    path('search/', views.search, name='search')
]

视图设计

from django.shortcuts import render, redirect
from blog.models import Article
from django.http import Http404

# 首页视图,展示所有的文章
def index(request):
    sessionid = request.COOKIES.get('sessionid')
    post_list = Article.objects.all()
    return render(request,'index.html',{'post_list': post_list, 'sessionid':sessionid})

#文章详情视图,展示详细的文章内容
def detail(request,id):
    try:
        post=Article.objects.get(id=id)
    except:
        raise Http404
    return render(request,'post.html',{'post':post})

#文章归档
def archives(request):
    try:
        post_list = Article.objects.all()
    except Article.DoesNotExist :
        raise Http404
    return render(request, 'archives.html', {'post_list': post_list, 'error': False})

#分类作为标签,展示同分类下的文章
def tags(request, tag):
    post_list = Article.objects.filter(category__iexact=tag)
    return render(request,'tags.html', {'post_list': post_list})

#搜索
def search(request):
    if 's' in request.GET:
        s=request.GET['s']
        if not s:
            return render(request,'index.html')
        else:
            post_list=Article.objects.filter(title__icontains=s)
            if len(post_list)==0:
                return render(request, 'archives.html',{'post_list':post_list,'error':True})
            else:
                return render(request, 'archives.html',{'post_list':post_list,'error':False})
    return redirect('index')

模板设计

父模板导航条完成

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index{% block title %} - MyBlog{% endblock %}</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="http://picturebag.qiniudn.com/blog.css">
    <link rel="stylesheet" href="{% static "vim.css" %}">
    {% block head %}{% endblock %}
    <style>
        img{
            margin-top: -20px;
            max-height: 60px;
            max-width: 150px;
        }
        .panel-body{
            width: 750px;
            margin-left: auto;
            margin-right: auto;
        }
    </style>
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <a class="navbar-brand" href="{% url 'index' %}">
                <img src="{% static 'logo.png' %}" alt="logo">
            </a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="{% url 'index' %}">首页<span class="sr-only">(current)</span></a></li>
                <li><a href="{% url 'archives' %}">归档</a></li>
            </ul>
            <form class="navbar-form navbar-right" action="/search/" method="get">
                <div class="form-group">
                    <input type="text" class="form-control" placeholder="Search" name="s">
                </div>
                <button type="submit" class="btn btn-default">搜索</button>
            </form>
            <ul class="nav navbar-nav navbar-right">
                {% block nav %}
                    {% if sessionid %}
                        <li><a href="/admin">进入后台</a></li>
                    {% else %}
                        <li><a href="/admin">登陆</a></li>
                    {% endif %}
                {% endblock %}
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>
{% block content %}
{% endblock %}
<footer class="footer hidden-xs">
    <div class="container">
            Copyright © 2013 ChaBug. All Rights Reserved.
    </div>
</footer>
</body>
</html>

首页完成

{% extends 'base.html' %}

{% block content %}
    <div class="container">
        {% for post in post_list %}
            <div class="panel-body">
                <div class="entry-header page-header">
                    <div class="entry-title h4">
                        <a href="{% url 'detail' id=post.id %}">{{ post.title }}</a>
                    </div>
                    <div class="entry-meta">
                        <p>Time:<time>{{ post.datetime|date:"Y-m-d" }}</time>
                            分类:<a href="{% url 'tag' tag=post.category %}">{{ post.category }}</a>
                        </p>
                    </div>
                    <div class="entry-content" itemprop="description">
                        {% load markdown_deux_tags %}
                        {{ post.content|truncatechars_html:50 }}
                        <a href="{% url 'detail' id=post.id %}">Read More</a>
                    </div>
                </div>

            </div>
        {% endfor %}
    </div>
{% endblock %}

归档页面

{% extends "base.html" %}

{% block content %}
    <div class="container">
        {% if error %}
            <h1>没找到相关文章</h1>
        {% else %}
            <section class="posts-collapse" id="post">
                <span class="archive-move-on"></span>
                <span class="archive-page-counter">
                OK! 共找到 {{ post_list.count }} 篇日志。 继续努力。
                </span>
            </section>
        {% endif %}
        {% for post in post_list %}
            <article class="post post-type-normal">
                <header class="post-header">
                    <div class="entry-title h4">
                        <a href="{% url 'detail' id=post.id %}"> <span itemprop="name">{{ post.title }}</span></a>
                    </div>
                    <div class="post-meta">
                        <time class="post-time">{{ post.datetime|date:"Y-m-d" }}</time>
                    </div>
                </header>
            </article>
        {% endfor %}
    </div>
{% endblock %}

我把归档和同分类文章用了同一个模板,通过if判断来展示不同的页面。

标签页面

{% extends 'base.html' %}

{% block content %}
    <div class="container">
        分类:<b>{{ post_list.first.category }}</b>下有{{ post_list.count }}篇日志。 继续努力。
        {% for post in post_list %}
            <div class="panel-body">
                <div class="entry-header page-header">
                    <div class="entry-title h4">
                        <a href="{% url 'detail' id=post.id %}">{{ post.title }}</a>
                    </div>
                    <div class="entry-meta">
                        <p>Time:<time>{{ post.datetime|date:"Y-m-d" }}</time>
                            分类:<a href="{% url 'tag' tag=post.category %}">{{ post.category }}</a>
                        </p>
                    </div>
                    <div class="entry-content" itemprop="description">
                        {% load markdown_deux_tags %}
                        {{ post.content|truncatechars_html:50 }}
                        <a href="{% url 'detail' id=post.id %}">Read More</a>
                    </div>
                </div>

            </div>
        {% endfor %}
    </div>
{% endblock %}

文章页面

{% extends 'base.html' %}
{% block content %}
    <div class="container">
        <div class="panel-body">
            <div class="entry-header page-header">
                <h1 class="entry-title">{{ post.title }}</h1>
            </div>
            <div class="entry-meta">
                <p>Time:<time>{{ post.datetime|date:"Y-m-d" }}</time>
                    分类:<a href="{% url 'tag' tag=post.category %}">{{ post.category|title }}</a>
                </p>
            </div>
            <div class="entry-content">
                {% load markdown_deux_tags %}
                {{ post.content|markdown }}
            </div>
        </div>
    </div>
{% endblock %}

markdown语法

安装markdown插件,

pip3 install django-markdown-deux

然后需要在settings.py中设置

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'article',
    'markdown_deux'
]

在模板中像我那样调用

<div class="entry-content">
   {% load markdown_deux_tags %}
   {{ post.content|markdown }}
</div>

后记

我们的django开发个人博客总算是结束了,前端模板写的不好,你们可以自己写一波好看的。