从0到1搭建网站

原文

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 runserver

    http://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 check
     2、创建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

2、模板继承
    所有页面都有一个共同的布局,顶部是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参数来传递变量

2、文章详情页的路由

    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要尽量避免爬虫的干扰,体现真实用户的浏览量,这里是有窍门的 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值