1、技术栈:web框架选择dijango,前端框架直接用django模板渲染,没有选择angular等,因为seo不友好
2、服务端容器选择:在tomcat、apeche httpd、nginx等web服务器下游,需要部署python的应用服务器容器,选择uwsgi,类似nginx,通过一个守护进程把不同的http请求转交给子进程并发处理,并且支持多线程的方式,性能较高,更重要的,django会自动帮我们生成wsgi的配置,天然对uwsgi友好。
3、安装开发和运行的基本环境:python2.7;pip install django; pip install uwsgi(web容器)
4、创建开源代码库:在github中创建仓库myweb,https://github.com/copperdong/myweb.git
5、创建django工程:django-admin startproject myweb /home/dong/develop/server/web/myweb
执行如下命令自动创建一个完整的工程目录(其中最后一个参数是工程目录,倒数第二个参数是工程名)
这时能够找到自动创建的manage.py文件(一个工具脚本,不需要修改),和工程总目录shareditor(里面包含了配置文件settings.py、总路由配置urls.py、wsgi协议配置文件wsgi.py)
6、创建网站app:django-admin startapp web
我们看到它自动创建了web目录,并且自动帮我们组织了一些文件,包括:
admin.py:数据库表的后台管理类一般定义在这里
apps.py:这个app的配置信息,这个文件一般不动
migrations目录:存储数据库迁移相关的临时文件,不需要动
models.py:和数据库对应的model类一般定义在这里
tests.py:自动化脚本
views.py:视图层脚本,我一般会把控制逻辑写到这里
7、运行:python manage.py runserverhttp://127.0.0.1:8000/
8、Helloword
web/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse('Hello World!')
shareditor/urls.py,路由规则
from web import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', views.index)
]
重新执行python manage.py runserver
9、部署一个专业的网站
上面只是django的一个用于开发和调试的方法,只是一个进程一个线程在运行,无法支持网站的高并发访问。
首先配置web容器,在myweb目录下创建uwsgi.ini
[uwsgi]
chdir = /home/dong/develop/server/web/myweb
http = 127.0.0.1:8080
http-keepalive = 1
module = myweb.wsgi:application
master = true
processes = 4
daemonize = /home/dong/develop/server/web/myweb/logs/uwsgi.log
disable-logging = 1
buffer-size = 16384
harakiri = 5
post-buffering = 8192
post-buffering-bufsize = 65536
pidfile = /home/dong/develop/server/web/myweb/logs/uwsgi.pid
enable-threads = true
single-interpreter = true
创建logs目录
uwsgi --ini myweb/uwsgi.ini
查看一下logs/uwsgi.log文件,如果没有异常信息说明网站已经部署成功了,我们ps ux|grep uwsgi看一下进程
可以看到启动了4个进程,其中一个守护进程用来接收和分发请求,3个子进程(对应配置文件里的processes = 4)用来处理请求
这时我们打开浏览器访问:http://127.0.0.1:8080/又能看到Hello World!了
10、高可用性部署:挂掉一台机器不影响服务,至少要部署两台完全对等的web服务来同时提供服务,那么用户访问是哪个机器呢?这里有两种实现方案,一种是配置DNS记录,同一个域名对应多个ip,那么当一个ip不可用时浏览器会自动尝试另外的ip,还有一种方法就是通过稳定的代理服务器(如nginx、apache httpd等)来配置成一个负载均衡代理,对外暴露的一个ip,对内链接到多台web服务器。
三、数据库与model
选择了阿里云的云数据库RDS,创建db_shareditor:create database db_shareditor
1、myweb/settings.py里的DATABASE
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_shareditor',
'USER': 'root',
'PASSWORD': 'dong',
'HOST': 'localhost',
'PORT': '3306',
'OPTIONS': {'sql_mode': 'traditional',}
}
}
检查配置是否正确:python manage.py check2、创建model并自动生成数据库表:model就是数据库表在内存里的数据结构
创建一个BlogPost的model,修改web/models.py
class BlogPost(models.Model):
title = models.CharField(max_length=255, verbose_name='文章标题')
body = models.TextField(verbose_name='文章内容')
create_time = models.DateTimeField(verbose_name='创建时间')
myweb/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'web',
]
自动根据结构定义生成对应的数据库表:python manage.py migrate
数据库表多出了web_blogpost
3、创建有关联关系的数据库表:利用django的models里的外键类型来关联。每篇文章都会有一个类别,每个类别对应多篇文章。
修改web/models.py
# python manage.py makemigrations; python manage.py migrate
再看数据库表多出了web_subject,同时web_blogpost也自动多出了一个subject_id字段
用PyCharm打开myweb工程
4、创建多对多关系的数据库表:每篇文章都会有多个标签,每个标签会对应多篇文章,也就是多对多的关系,用models里的ManyToMany类型来关联
添加Tag类,并为BlogPost类添加如下成员:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Tag(models.Model):
name = models.CharField(max_length=255, verbose_name='标签名称')
class Subject(models.Model):
name = models.CharField(max_length=255, verbose_name='类别名称')
introduce = models.CharField(max_length=255, verbose_name='类别简介')
image = models.ImageField(verbose_name='类别图片')
class BlogPost(models.Model):
title = models.CharField(max_length=255, verbose_name='文章标题')
body = models.TextField(verbose_name='文章内容')
create_time = models.DateTimeField(verbose_name='创建时间')
subject = models.ForeignKey(Subject, verbose_name='类别', null=True)
tags = models.ManyToManyField(Tag, verbose_name='标签')
重新执行:
python manage.py makemigrations; python manage.py migrate
四、强大的后台管理工具django-admin
每个web框架都配套有一个admin组件,用来管理后台数据库
1、django-admin的账号管理:http://127.0.0.1:8000/admin
初始化一个超级用户:python manage.py createsuperuser
2、配置admin管理数据库:将BlogPost、Subject、Tag,加入django-admin管理后台
修改web/admin.py
from django.contrib import admin
# Register your models here.
from .models import Subject
class SubjectAdmin(admin.ModelAdmin):
list_display = ('name',)
admin.site.register(Subject, SubjectAdmin)
from .models import Tag
class TagAdmin(admin.ModelAdmin):
list_display = ('name',)
admin.site.register(Tag, TagAdmin)
from .models import BlogPost
class BlogPostAdmin(admin.ModelAdmin):
list_display = ('title', 'create_time', 'subject')
admin.site.register(BlogPost, BlogPostAdmin)
3、管理界面的定制化:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Tag(models.Model):
name = models.CharField(max_length=255, verbose_name='标签名称')
class Meta:
verbose_name_plural = '标签'
def __unicode__(self):
return self.name
class Subject(models.Model):
name = models.CharField(max_length=255, verbose_name='类别名称')
introduce = models.CharField(max_length=255, verbose_name='类别简介')
image = models.ImageField(verbose_name='类别图片')
class Meta:
verbose_name_plural = '类别'
def __unicode__(self):
return self.name
class BlogPost(models.Model):
title = models.CharField(max_length=255, verbose_name='文章标题')
body = models.TextField(verbose_name='文章内容')
create_time = models.DateTimeField(verbose_name='创建时间')
subject = models.ForeignKey(Subject, verbose_name='类别', null=True)
tags = models.ManyToManyField(Tag, verbose_name='标签')
class Meta:
verbose_name_plural = '文章'
def __unicode__(self):
return self.name
创建如下目录templates/admin,并新建base_site.html文件
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}{{ title }} | {% trans 'SharEDITor后台管理' %}{% endblock %}
{% block branding %}
<h1 id="site-name">{% trans 'SharEDITor后台管理' %}</h1>
{% endblock %}
{% block nav-global %}{% endblock %}
修改shareditor/settings.py文件,在TEMPLATES => DIRS配置项中添加'templates',这样就可以自动找到模板目录了,实际上这里的admin/base_site.html是重写了django-admin的源码
5、图片管理:
为image字段选择一张图片,图片实际上上传到根目录下。这种方式存在一些问题:
1)如果要在网站中展示这张图片需要为其单独指定路由;
2)如果网站多机部署无法实时同步数据;
3)如果图片很大,会耗费很多带宽,响应慢。
为了解决如上问题:引入阿里云的对象存储OSS服务,它的优点是有CDN加速,也就是不同地域都有镜像,访问快,而且价格低廉,可比同样的网络带宽便宜多了。
OSS的使用请见官方文档
我们发现图片已经不再保存到本地文件了,而在阿里云的OSS里找到了上传的文件,而在我们的数据库里存储了这个图片在阿里云OSS中的url,可以直接访问
五、开启网站首页的大门
1、模板渲染:模板引擎有很多,无非就是模板继承、模板引入、读透传的变量、循环、逻辑判断、filters、自定义标签等。这里直接用django-template
创建web/templates/web/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Hello {{ name }}</h1>
</body>
</html>
web/views.py
def index(request):
return render(request, 'web/index.html', {'name': 'lichuang'})
网页显示Hello lichuang
所有页面都有一个共同的布局,顶部是logo,底部是版权声明,抽象出一个基类模板
web/templates/web/base.html
<!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, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css">
<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<title>{% block title %}SharEDITor - 关注大数据技术{% endblock title %}</title>
{% block head %}
{% endblock head %}
</head>
<body>
<div class="row bg-primary" style="margin-right: 0">
<div class="col-sm-1 col-xs-1"></div>
<div class="col-sm-2 col-xs-11">
<h1><a href="" style="text-decoration: none;color: white;">SharEDITor</a></h1>
</div>
<div class="col-sm-6"></div>
</div>
{% block body %}
{% endblock body %}
<div class="row" style="margin-right: 0">
<div class="col-sm-2"></div>
<div class="col-sm-8 col-xs-12 text-center" style="color: #959595;margin-bottom: 10px;">
Copyright © <a href="">shareditor.com</a> | 京ICP备13047771号 | shareditor.com^_^gmail.com
</div>
</div>
</body>
</html>
block的代码块,它的作用是声明可重写的部分
重新修改web/templates/web/index.html
{% extends "web/base.html" %}
{% block title %}
{% endblock %}
{% block body %}
<div class="row jumbotron" style="margin-right: 0">
<div class="col-md-1 col-xs-1"></div>
<div class="col-md-10 col-xs-10"><h1>Welcome Big Data ITors!</h1></div>
<div class="col-md-1 col-xs-1"></div>
</div>
{% endblock %}
3、动态数据展示
web/views.py
from .models import Tag
def index(request):
tags = Tag.objects.all()
return render(request, 'web/index.html', {'tags': tags})
重新修改web/templates/web/index.html
{% extends "web/base.html" %}
{% block title %}
{% endblock %}
{% block body %}
<div class="row jumbotron" style="margin-right: 0">
<div class="col-md-1 col-xs-1"></div>
<div class="col-md-10 col-xs-10"><h1>Welcome Big Data ITors!</h1></div>
<div class="col-md-1 col-xs-1"></div>
</div>
<div class="row" style="margin-right: 0">
<div class="col-sm-1 col-xs-1"></div>
{% for tag in tags %}
<div class="col-sm-2 col-xs-12">
<div class="thumbnail">
<img src="{{ tag.image }}" alt="tag">
<div class="caption">
<h3>{{ tag }}({{ tag.blogpost_set.count }})</h3>
<p>
{% for blogpost in tag.blogpost_set.all %}
{{ blogpost.title }}
{% endfor %}
</p>
<h5>更多>>></h5>
</div>
</div>
</div>
{% endfor %}
<div class="col-sm-1 col-xs-1"></div>
</div>
{% endblock %}
4、展示最新文章
在页面底部显示最新发表的文章
from django.http import HttpResponse
from .models import Tag
from .models import BlogPost
def index(request):
tags = Tag.objects.all()
latest_blog_posts = BlogPost.objects.order_by('create_time')[0:5]
return render(request, 'web/index.html', {'tags': tags, 'latest_blog_posts': latest_blog_posts})
修改web/templates/web/base.html
<div class="row navbar navbar-inverse" style="margin:0">
<div class="row" style="margin:0">
<div class="col-sm-1 col-xs-1"></div>
<div class="col-sm-5 col-xs-5">
<h4 style="color: #FFFFFF; border-bottom: 1px solid #695d69; padding-bottom: 10px; margin-top: 30px;">最新文章</h4>
{% for blogpost in latest_blog_posts %}
<div class="row" style="margin: 10px; margin-left: 0; overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
<span style="color: #959595;">({{ blogpost.create_time|date:"Y-m-d" }})</span>
<a title="{{ blogpost }}" style="color: #959595;">{{ blogpost }}</a>
</div>
{% endfor %}
</div>
<div class="col-sm-1 col-xs-1"></div>
</div>
</div>
六、路由的使用
路由是web框架的关键内容,定义了不同的url规则映射到的处理逻辑
1、指定标签的文章列表页
修改shareditor/urls.py
url(r'^bloglistbytag', views.blog_list_by_tag, name='blog_list_by_tag')
意思是说对于url路径为bloglistbytag的网页,直接调用views.blog_list_by_tag来执行逻辑
在web/views.py中添加如下函数
def blog_list_by_tag(request):
if 'tagname' in request.GET:
tag_name = request.GET['tagname']
blog_posts = BlogPost.objects.filter(tags__name=tag_name)
latest_blog_posts = BlogPost.objects.order_by('create_time')[0:5]
return render(request, 'web/blog_list_by_tag.html', {'tag_name': tag_name, 'blog_posts': blog_posts,
'latest_blog_posts': latest_blog_posts})
else:
return HttpResponse('404')
首先通过获取GET请求的tagname参数来获取到标签名,然后通过model层查询数据库获取导数据,并通过web/templates/web/blog_list_by_tag.html这个模板来渲染的
{% extends "web/base.html" %}
{% block title %}
{{ tag_name }}
{% endblock %}
{% block body %}
<div class="row" style="margin-right: 0">
<div class="col-sm-3 col-xs-1"></div>
<div class="col-sm-6 col-xs-10">
<h1>{{ tag_name }}</h1>
</div>
<div class="col-sm-3 col-xs-1"></div>
</div>
<div class="row" style="margin-right: 0">
<div class="col-sm-3 col-xs-1"></div>
<div class="col-sm-6 col-xs-10">
{% for blog_post in blog_posts %}
<h4><a href="">{{ blog_post }}</a>({{ blog_post.create_time|date:'Y-m-d' }})</h4>
{% endfor %}
</div>
</div>
{% endblock %}
打开这个url可以看到效果:http://127.0.0.1:8000/bloglistbytag?tagname=%E4%BB%8E0%E5%88%B01%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99
怎么通过首页链接进到这个列表页呢?修改web/templates/web/index.html
把<h3>{{ tag }}({{ tag.blogpost_set.count }})</h3>
改成<h3><a href="{% url 'blog_list_by_tag' %}?tagname={{ tag }}">{{ tag }}({{ tag.blogpost_set.count }})</a></h3>
见识到了url模板语法,他根据指定的路由名来去urls.py中寻找对应的逻辑函数并执行,同时通过url参数来传递变量
https://github.com/warmheartli/shareditor/blob/master/web/templates/web/blog_show.html
七、在django模板中使用自定义属性
稍复杂的一些展现需求,都会使得model自带的属性无法满足,因此需要做一些自定义的工作,便于在模板里使用,本节来介绍一下model的自定义属性
1、展示简单的文章标题
修改web/models.py,为BlogPost类添加如下方法
def get_simple_title(self):
return self.title.replace(self.tags.first().name, '')
修改web/templates/web/blog_show.html
2、获取最新n篇文章
在web/models.py的Tag类中添加
def get_latest_blogpost(self, count=5):
return self.blogpost_set.order_by('id').reverse()[0:count]
八、django中集成ckeditor
为了让我们的文章能够图文并茂,格式丰富,我们引入第三方插件ckeditor来实现富文本编辑和代码语法高亮等的展示
九、实现真实用户的pv计算
你需要知道哪一篇最受用户欢迎,还需要展现在自己的网站上,让用户也找到最受欢迎的那篇,统计pv要尽量避免爬虫的干扰,体现真实用户的浏览量,这里是有窍门的