使用Django创建一份在线简历

一、开篇

去年十二月的时候,我曾跟着追梦人物的Django博客教程葫芦依样,开发出了一个自己的博客Black&White,那时候的我对网站的结构,网站运行的模式懵懵懂懂,只会跟着教程一步步做下去,遇到问题去找解决方案的过程也很艰辛,找不到出现问题的关键点,最后成品做了出来,但因为只是模仿,没能力创新,使得最后自己的博客url是demo.lightl.fun/ , 连二级域名都不会修改。之后几个月因为其他事缠身,也就没继续django的学习。适逢将要毕业,我花了几个月将本科知识全部回顾了一遍,对计算机网络的认识更上一层楼,乘着将要找工作,不如重新实践,利用Django创建一份在线简历。

二、设计思路

由于做过的工程太少,很多时候设计思路只是一个方向,具体实现过程会对需求做各种变动,随机应变吧。

简单的设计思路就是,开始一个Django项目,开始一个新的简历应用,从网上找到前端界面模板,然后作为static文件放到简历应用中,根据模板可以提供的数据输出位置设计模型(数据库),然后生成数据库,存入真实数据,用django提供的数据库接口获得数据,在视图函数中作为参数传给前端界面,再在前端界面中使用模板渲染的方法给传来的数据进行渲染,最后使用nginx部署在公网服务器上,实现在线简历功能。

三、具体实现及遇到的问题

具体实现过程中,参考自强学堂-Django 基础教程以及追梦人物的Django博客教程。此时的实现,会考虑每一步做的意义以及能实现的效果,同时Django已经升级到了2.0.5,而网上教程多集中在1.8,有少许区别,参照自强学堂提出的以及google可以解决,(同时Django2.0.5可以兼容Django1.8创建的项目,但在模型部分有修改)。

先是常规的建立项目,建立应用,添加应用信息到settings.py,给view.py添加一个index方法,给urls.py配置url导向,这里没什么太多可说的。

#JL/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'jianli',
]
#JL/urls.py
from django.contrib import admin
from django.urls import path
from jianli import views as jianli_views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', jianli_views.index, name='jianli_view'),#新版url写法更舒服了
]

之后找简历模板,最后选择了简历模板,下载,放入应用的static文件夹中,将index.html放入templates文件夹中,给html文件中指向的css、js位置进行修改,此时出现了第一个渲染的数据base_dir,它指向应用的static文件夹,如果今后移动该文件夹,只需要修改base_dir内容,index.html就可以正常工作。

#jianli/views.py
from django.shortcuts import render

def index(request):
    base_dir = "../../static/"
    return render(request, 'jianli/index.html', {"base_dir":base_dir})

说来惭愧,理应很早就开始的模型设计一直到后面才做到。我先是根据模板能提供显示的内容,写了一些数据放在视图函数中,然后生成一个字典作为render方法参数传递给html界面,然后在html界面中使用{{}}、{%%}等对数据进行渲染,此时的语法参考模板设计者文档,但奇怪的是,jinja2提出的很多语法在我这个项目里行不通,例如数字计算,in语法,当时没想太多,只是让视图函数传递的数据多了很多项,也就是flag值,方便我在html里面选择性渲染。

有一天晚上,觉得视图函数传递的数据很多项只用一次,数据量总体太庞大,而且很多时候我感觉可以使用jinja2的方法进行快捷的处理,比如我可以根据数据id%2的结果给出不同的呈现方式,但必须得用is_single的flag来判断。不胜其烦开始查找资料,才发现django默认使用的是DjangoTemplatesLanguage而不是jinja2。惊了,开始安装jinja2,修改settings.py的templates项以jinja2进行渲染,然后大改html文件,使用%、for---if---语句、if---xxx--in--list语句大幅度的减少了渲染量。

#JL/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [os.path.join(BASE_DIR,'jianli/templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'environment':'jianli.jinja2_env.environment'
        },
    },
]
<!-- 部分修改后html元素 -->
<div class="col-md-6 skill-right ">
    <div class="scrollbar scrollbar1">
    {% for skill in detail_skills if skill.id in [1,2,4,5] %}
        {% if loop.index % 2 == 1 %}
            <div class="more-gds">
                <div class="col-sm-3 more-left">
                    <span class="{{skill.icon}}" aria-hidden="true"></span>
                </div>
                <div class="col-sm-9 more-right">
                    <h4>关于{{ skill.name }}</h4>
                    <p>{{ skill.content }}</p>
                </div>
                <div class="clearfix">
                </div>
            </div>
        {% else %}
            <div class="more-gds yes_marg">
                <div class="col-sm-9 more-right2">
                    <h4>关于{{ skill.name }}</h4>
                    <p>{{ skill.content }}</p>
                </div>
                <div class="col-sm-3 more-left">
                    <span class="{{ skill.icon }}" aria-hidden="true"></span>
                </div>
                <div class="clearfix">
                </div>
            </div>
        {% endif %}
    {% endfor %}                
        <div class="clearfix"></div>
    </div>
    </div>

其实代码写到这里,效果已经足够了,部署在服务器就成了。但数据明文放在视图函数,显得笨拙,传递的字典构造到手都疼,该转移到数据库了。但这是遇到了第二个问题也是最麻烦的问题。很明显的,这些数据的结构是kv,其中v部分亦可以是列表,(简言之就是符合json传递),将value的列表存于MySQL中,要么考虑多表,主表外键连接,要么将列表转为str存入MySQL主表中,取出来的时候再解回列表。按照这个思路开始了模型的编写。完成了存数据,到了取数据过程,开始复杂,django给出的接口返回的数据是queryset类,可以通过db.objects.values()以字典方式获得数据,然后把刚刚value的列表数据再取出来,这个代码量,处理过程还不如直接存数据到views.py中,而且这个数据库只有一条数据。

退而求其次,既然需要json.dumps(list)和json.loads(str),那么为什么不直接生成一个json文件,直接在views.py中json.load(xx.json)多好,然后就有了如下的代码,瞬间清爽。列表的循环交给jinja2处理。

#jianli/views.py
from django.shortcuts import render

import json
# Create your views here.
def index(request):    
    with open('jianli/data.json','r') as f:
        data = json.load(f)
    dic = {}
    for k,v in data.items():
        dic[k] = v

    return render(request, 'jianli/index.html', dic)

最后开始了部署阶段,继续使用nginx+gunicorn的方式部署应用,这时参考使用 Nginx 和 Gunicorn 部署 Django 博客,完全可行,如果一台服务器多个应用,那就写多个配置,都可以监听80端口,我对于Linux的socket文件不是很了解,只能大概的猜测是根据nginx、guincorn的方法,底层使用sockcet编程的思想接收对网站的访问请求。那么我就可以把demo.lightl.fun改成blog.lightl.fun,多写一个配置即可。这里贴一个nginx配置文件怎么写。

#/etc/nginx/sites-available/cv.lightl.fun

server {
    charset utf-8;
    listen 80;
    #服务的域名为 cv.lightl.fun
    server_name cv.lightl.fun; 

    #所有URL 带有 /static 的请求均由 Nginx 处理,alias 指明了静态文件的存放目录
    location /static { 
        alias /home/light/sites/fun.lightl.fun/JL/static; 
    }

    #其它请求转发给 Django 处理proxy_pass 后面使用了 unix 套接字其作用是防止端口冲突.
    location / { 
        proxy_set_header Host $host;
        proxy_pass http://unix:/tmp/cv.lightl.fun.socket;
    }
}

四、QA

1、为什么在django中jinja2的语法用不了?

因为django默认使用DTL,需要手动安装jinja2并指定,请参考本文第三节。

2、为什么django无法读取json文件,出现Expecting property name enclosed in double quotes?

json文件是严格的键值对,但值可以有多种写法,请注意json文件中没有多余的逗号(大部分配置文件都喜欢在列表的最后一项添加逗号,但json不可以,影响读取)。

3、为什么nginx启动后,访问网页出现502?

原因很多,总体来说是配置文件没写对,同时也要在服务器上应用内进行一次静态文件收集。(有说是nginx default文件的问题,但我没遇到)。

五、沉思

在这个项目中,主要 - 学到了django框架快速搭建网站的过程,要配置views.py、urls.py - 学到了django中jinja2如何进行页面的渲染 - 学到了怎么使用nginx+gunicorn部署服务 - 学到了怎么使用django的queryset api来读取数据库数据 - 学到了怎么修改HTML文件、同时对bootstrap有了认识。 同时还有很多不足,例如 - 需求多变,没有确切的实现过程,耽误了一些时间 - 没使用到fabric自动化部署,下次有机会直接上fabric

项目地址:halysl/job-cv


发表评论

评论列表,共 0 条评论

    暂无评论