我如何用Django开发一个项目

前言

网络上关于Django的内容其实已经不少了,包括本身也是从网络上的内容一点一从零开始学习Django的,可是这部份内容都过于零散,能够说大部分都是在讲怎么从django-admin startproject到建立Model,再到写一个TODO List或者一个Blog之类的内容,对于已经了解Django的人来讲,并无太好渠道去了解一些别人的实践,在开发本身的项目时,遇到的需求很难去了解其余人会怎么设计,固然这也是社区存在的通病,你们更多在讨论语言层面的问题.
我但愿能把本身平时在工做中总结出来的tips分享出来,也许在某些地方能帮助到别人,也算是本身梳理经验,自我提升的一个过程.
既然标题是如何开发,那我就按照一个项目的生命周期来整理.前端

需求分析

讨论需求的事情就不谈了.

一个项目需求给到以后,第一步确定不是django-admin startproject,而是选型,换句话说,咱们得先决定要不要使用django.它适合什么样的项目,不适合什么样的项目 java

避免websocket
Django出现的太早了,它甚至和ajax的年纪差没几年,因此对不少现代化的需求和功能支持都不太好,好比websocket,好比异步任务.
若是需求中有一些功能会比较依赖websocket,那么就建议不要选择Django,并非说彻底不行,可是Django Channelasgi目前来讲都算不上很成熟的解决方案,不得不认可在这方面django的确是没有优点.python

避免对性能敏感的需求
这是老生常谈了,我说一下我本身遇到的性能问题都出如今哪里(不谈恶俗的abtest).git

服务端RSA加解密 web

这是我偶然发现的,python进行rsa运算简直慢出天际.可是这个问题并不会很是明显的爆发,毕竟只算一次的话,最多也就是零点几秒甚至更少的时间,当访问量不大的时候并无那么明显,很难发现.
可是有一天我使用多线程进行rsa加密的时候,发现我不管使用多少个线程,加密的速度都是同样的慢,才算是发现这个问题.它很容易成为服务器性能的瓶颈.
固然这个问题仍是能够解决的.用Golang写了一个加解密的模块,编译成.so给python调用,这个问题就算解决了.ajax

相似上传Excel导入数据数据库

这是一个很复杂的状况.大文件传过来以后,究竟选择什么策略进行处理不能一律而论.好比前期我选择直接读出数据写入到数据库中,后来给我来了一个10W行的excel,直接GG了,python的循环操做是很是慢的,1W行的文件已经须要加载好久了,这就致使服务端处理Excel并实时对数据进行格式检查和去重并当即返回结果是很不合理的需求,固然不是说别的语言能作的完美,而是想说由于django不能直接处理异步任务,完成相似需求就必须提升系统复杂度,引入消息队列和额外的worker才行,不划算. django

实际上使用Django真正能遇到性能问题的时候很少,由于绝大部分产品不存在性能瓶颈,一个产品QPS能达到10,就已经美滋滋了,这个吞吐量Django彻底没问题,到了Django不行了那个时候换性能更好的方案彻底不是问题.json

个人通用选型

我全部使用Django开发的项目,架构上都差很少.后端

  • Nginx 没什么说的,配合gunicorn使用proxy_pass,方即可靠,省心.
  • gunicorn 性能优秀,使用简单,可靠,彻底对uwsgi没兴趣
  • Django
  • Redis 在python技术栈中自然扮演着缓存和消息队列的双重身份,与DjangoCelery配合完美.省一个系统组件.
  • Celery 弥补Django异步任务缺陷,妙用无穷.
  • MySQL 其实postgreSQLDjango更搭,不过各个云服务都是对MySQL支持更好一些.

这套东西我使用起来能够说并无遇到过解决不了的问题,不说无所不能,可是混口饭吃绝对是没问题了.很是适合中小型项目快速开发试错,基本上不须要开发基础功能.

前100行代码

好久没写后端渲染的项目了,这里都是先后端分离的内容

一个Django项目初始化以后,第一个要考虑的就是模块的划分,app创建好以后,我通常会在settings.py那个项目同名文件夹下创建以下几个文件:

basic.py

这个文件我用来定义一些基础的组件,最经常使用的是这个.
继承于HttpResponse,定义返回结构的类

import json

from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse

from Myapp.settings import DEBUG 


class Response(HttpResponse):

    def __init__(self, data=None, msg="成功",
                 status_code=200, encoder=DjangoJSONEncoder,
                 json_dumps_params=None, **kwargs):
        if json_dumps_params is None:
            json_dumps_params = {}
        if DEBUG:
            # 开发模式增长缩进,方便人工检查数据
            json_dumps_params["indent"] = 2

        json_dumps_params["ensure_ascii"] = False
        kwargs.setdefault('content_type', 'application/json')
        ret = dict(
            status_code=status_code,
            msg=msg,
        )
        if data:
            ret["data"] = data
        s = json.dumps(ret, cls=encoder, **json_dumps_params)
        super().__init__(content=s, **kwargs)

继承DjangoHttpResponse类,参考(抄)了django.http.JsonResponse的结构.接收status_code,msg,data,避免在视图函数中重复的构建返回结构,只须要关心业务数据就能够了.同时能够约束一块儿开发的人,避免不当心写错返回结构之类的事儿.与之配合的还有一个:
code.py

# 请求成功
OK = 200

# 资源不存在
NOT_FOUND = 404

这样在构建返回结构的时候,直接

return Response(msg="资源不存在",status_code=NOT_FOUND)

前端接收到的返回就是统一格式的JSON,开发模式带有缩进,生产环境DEBUG模式关掉就是普通JSON字符串了

{
    "status_code":404,
    "msg":"资源不存在"
}

归纳的说就是,将返回结构抽象成一个对象,而且统一管理返回码,这样在开发中能少些一些重复代码,节省精力,最重要的是方便维护.

自定义中间件:

Django自己继承了模板引擎,默认开启,可是如今流行的先后端分离方案下,模板是能够放弃的(可是初期的管理后台我更喜欢用自带的admin模块,能够经过配置两套不一样的settings.py文件和manage.py,实现使用不一样的项目配置).一样,基于session的用户验证是否要继续采用也是能够灵活选择的.这里我说一下咱们是怎么用JWT来代替session的.
Django自己是经过SessionMiddlewareAuthenticationMiddleware来管理session和用户身份的.咱们使用JWT代替session,那么天然就要用一个中间件来代替SessionMiddleware,AuthenticationMiddleware依赖于session一样也就没法使用了.咱们用下边这个中间件来代替他们

class JwtMiddleware(object):

    def __init__(self, get_response=None):
        self.get_response = get_response
    def __call__(self, request, *args, **kwargs):
        auth_token = request.META.get("HTTP_AUTHORIZATION")
        try:
            _id = jwt.decode(auth_token, SECRET_KEY)["id"]
            user = User.objects.get(id=_id)

        except:
            user = AnonymousUser()
        request.user = user

        response = self.get_response(request, *args, **kwargs)
        return response

和前端约定在header中携带Authorization字段,做为身份证实.这个token是登陆接口签发的,这么作的好处:

  • 能够先不开发用户系统,使用djangoadmin模块进行登录,进行开发,这意味着用户管理等模块和业务模块能够直接拆分,在两个分支上开发
  • LoginRequiredMixin等框架自身依赖request.user接口的功能依然可用,固然相似的功能咱们通常也本身写了.不过在尽可能不破坏框架的可用性的前提下进行自定义,是咱们快速开发的基本原则之一.能够有效下降团队沟通学习成本,文档都省了,业务逻辑部分彻底不须要关注自定义以后的用户鉴权方式.

值得注意的地方是,读取用户是一个数据库操做,Django原生中间件使用了懒加载的模式来操做,这么作的缘由(我猜的)是,django做为框架,没法肯定是否须要用到user对象,若是每次都加载会很蠢,因此采用懒加载,第一次加载的时候才获取一次并缓存user,避免第二次读库.咱们的项目要求每次操做都要读,而且只会读一次,就不须要使用懒加载了.更好的设计是根据本身的需求选择是直接读取用户仍是使用懒加载读取用户信息.

小结: 还有一个将请求body读取成dict绑定到request的中间件,避免业务逻辑进行json.loads(request.body)的操做,由于这个是须要try..except..的操做,最少也须要四行代码才能作完.经过这些,算是约定了一个团队内部的开发基本规则,保证你们写的逻辑返回数据结构是能够统一维护的,定义的errcode都集中在一个文件.业务逻辑中须要的信息(request.user/request.json)都已提供.

数据逻辑与业务逻辑的划分

咱们写业务逻辑的时候,可能会遇到将一个Model对象序列化为一个JSON的状况,大名鼎鼎的drfDjango restful framework就是作这个的,可是今天不提他了.

Model序列化为JSON的的代码,我一般是写在Model中,哪怕他只用了一次我也习惯定义在Model中,View层只调用方法而不须要将数据分别取出进行构建.这样咱们View层的逻辑会显得很是简洁,一样也更容易维护.

除了最终的序列化,Model不少时候还会出现计算属性.

  • 内容已经发表了多久?
  • 优惠当前是否在可用时段?

这些若是写在业务逻辑里,不免要写(datetime.datetime.now()-obj.publish_time).total_seconds()这种又臭又长的代码,可能还会在多个地方使用到.这些数据虽然不是直接写在数据库中,但依然属于Model层该作的事儿,那么咱们就写在Model就完了.配合@property,进一步保证了代码可读性.

在项目外访问ORM

假如如今咱们须要作一个定时任务,定时从网上抓取信息存入Django项目的数据库中.咱们能够选择直接写SQL语句插入,可是有些问题不太好处理.

  • 数据库迁移,要多维护一个数据库链接配置
  • 开发中手动维护SQL语句使其和Model行为一致

因此我推荐使用在定时任务中使用Django ORM,保证和项目内代码逻辑一致,下降维护成本.

官方文档

如今咱们须要编写一个独立于项目运行的脚本,官方给出的例子是:

import django
from django.conf import settings
from myapp import myapp_defaults

settings.configure(default_settings=myapp_defaults, DEBUG=True)
django.setup()

# Now this script or any imported module can use any part of Django it needs.
from myapp import models

我经常使用的是

import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Myapp.settings')
    django.setup()

其实都差很少,我也建议使用官方的方法,能够避免出现环境变量路径不对的问题.
这个操做至关于加载了django项目的配置,在这以后就能够引用你想用的项目Model,进行必要的操做了.必须在django.setup()以后引入Model
经过这个方法,也能够不用django-celery了,配合处理异步任务.

结尾

Python进行WEB开发的惟一优点大概就是速度了.并非说用python的人比用java的人快,而是说同一我的用python开发会更快一些.
使用Django而不是Flask或者Tornado也是为了开发快,迭代快.作这些边边角角的工做,作这些细节的东西,都是为了快.写一行代码也许须要5秒钟,改一行代码可能须要一成天.也许今天多花几分钟定义的Response能够避免和前端扯皮的致使冲突升级打架斗殴住院一个月.仍是很是值得的.本身看着本身写的代码整洁干净,心情也更好不是?
https://luliangce.gitee.io/bl...

相关文章
相关标签/搜索