python 的 web 框架非常多,比较出名的有 django, flask, tornado。django 作为一个老牌框架,无论是文档还是代码质量都非常高,另外他自带的 admin 后台和一些有用的 app,如果你的需求是做 cms 之类的 web 应用的话,基本上不用开发多少代码就能出一个成品。不过很多新手可能一开始不太适应他的设计模式,遇到问题后基本就懵了,所以这里我按照自己用 django 的经验,写一下 django 的一些应用实践,可能写的比较零散,大家见谅。
整体流程
首先我们得了解下 django 这个框架整体的处理流程,假设我们采用 nginx + uwsgi + django 的 web server 模式
1. 一个请求过来后,首先经过 nginx 做反向代理,将请求转发到 uwsgi (python 用 wsgi 这种协议来解析 http 请求,uwsgi 是一个 解析 wsgi 的应用),uwsgi 再将解析过的数据传到 django。
2. django 收到请求后,首先会经过一组全局的中间件 (middleware),调用 process_request 作为预处理,比如解析用户状态,检验 csrf_token (post 请求),如果有问题,则直接返回 response,不再调用 view 函数。否则,调用 process_view ,如果没问题进入 view 函数。
3. 进入 view 函数后开发者可以写自己的逻辑,比如操作数据库,更新缓存,最后返回一个 response。
4. 接下来 跳出 view 函数,重新进入 middleware,调用 process_response,对 response 做些最后的修饰,返回给用户。
views 模块
1. view 不仅可以用函数,也可以用通用视图类(generic_view),好处是:代码更加清晰,可以复用继承,并且结合 mixin 能够开发更加灵活的 view 模块
def hello_fn(request, name="World"): return HttpResponse("Hellp {}!".format(name)) class FeedMixin(object): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["feed"] = models.Post.objects.viewable_posts(self.request.user) return context class MyFeed(FeedMixin, generic.CreateView): model = models.Post template_name = "myfeed.html" success_url = reverse_lazy("my_feed") ——————
2. python 的装饰器很好用,也可以用于 views 函数, 比如下面的装饰器用于登录用户的检测
@login_required def simple_view(request): return HttpResponse()
urls 模块
1. urls.py 这个文件将访问的 url 跟 view 模块对应起来,按从上到下的顺序匹配
2. 采用 include 函数可以包含其他 app 的 urls,namespace 参数定义后可以在模板中直接调用,比如
{% url 'articles'%} url(r'^articles/$', include(articles.urls), namespace="articles"),
models 模块
1. model 是具有处理数据库的一种面向对象的方法的类,能够让不熟悉数据库语句的程序员也能快速操作数据库
2. 采用面向对象的方式创建类,加上 abstract = True 则为抽象类
class Postable(models.Model): created = models.DateTimeField(auto_now_add=True) modified = modified.DateTimeField(auto_now=True) message = models.TextField(max_length=500) class Meta: abstract = True class Post(Postable): ... class Comment(Postable): ···
3. django 表对应关系有一对一(OneToOneField),一对多 (ForeignKey),多对多 (ManyToManyField),其中 多对多的模式通过创建一个中间表来实现。
class Book(models.Model): place = models.OneToOneField(Place, primary_key=True) pub=models.ForeignKey(Publisher) authors=models.ManyToManyField(Author)
上面的例子中间表包括 author_id 和 book_id
4. django 的 signal 实现 hook 数据库写行为,比如,pre_save, post_save,pre_delete, post_delete,你也可以自定义 signal, hook 其他行为。
@receiver(post_save, sender=TransactionDetail, dispatch_uid="update_stock_count") def update_stock(sender, instance, **kwargs): instance.product.stock -= instance.amount instance.product.save()
5. Person.objects.all(), 这里面的 objects 其实是个 manager, 实现了 all, filter 等函数,可以自定义
class Person(models.Model): object = models.Manager()
6. django 的 queryset 是惰性的,只有在真正用的时候才会去数据库查询,并且查询一次后,会有缓存,当再次遍历这个 queryset 的时候,不会再去查询。
person_set = Person.objects.filter(first_name="Dave")
7. 数据库第一次创建后,会有再次更新字段的需求。django 在 1.4 版本前并没有这个功能,得用第三方库 south 来更新,后来的版本 django 自带了 migration 功能,能够将最新的 model 版本和数据库的字段作对比,自动生成 migration 文件
python manage.py makemigrations # 生成 migration 文件 python manage.py migrate # 将更改应用到数据库
8. orm 不能直接看到 raw sql 语句,可以通过如下语句查看 sql
from django.db import connection connection.queries
9. 可以使用 django-debug-toolbar 插件查看慢查询,也能对对哪些页面载入较慢有个大致的了解。
10. 在 orm 中使用 select_related() 减少查询数据库次数:,select_related() 会自动扩展外键关系
class Province(models.Model): name = models.CharField(max_length=10) class City(models.Model): name = models.CharField(max_length=5) province = models.ForeignKey(Province) citys = City.objects.select_related().all()
不过对于高并发的应用来说外键不是很推荐。
forms 模块
1. 前端传上来一个表单的值,但是没法确认这些值是不是为空,是不是类型正确,这个时候当然可以自己一个个值判断,也可以采用 forms 模块去做验证,用过 django-rest-framework 的同学会知道和里面的 serializers 是同一个概念
class PersonDetailsForm(forms.Form): name = forms.CharField(max_length=100) age = forms.IntegerField()
2. 如果你用的是模板渲染的方式,那么展示的时候更简单,表单能自动生成 html 的表单。
>>> f = PersonDetailsForm() >>> print(f.as_p()) <p><label for="id_name">Name:</label> <input id="id_name" maxlength="100" name="name" type="text" /></p> <p><label for="id_age">Age:</label> <input id="id_age" name="age" type="number" /></p>
admin 模块
1. admin 基本上开箱即用,如果需要定制的话,也能做一些组件的定制,不过这些东西得看文档去详细了解了。
commands 模块
1. django 提供了后台脚本模块,可以自己集成 BaseCommand 类去自定义脚本
python mannage.py -h
这个命令就能看到所有的 commands 命令
2. 如果不想用他的模块,又想引入 django 项目的一些模块,可以采用下面的方法解决
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings") django.setup()
settings 模块
1. 所有的 settings 默认值都在这里面能找到,https://github.com/django/django/blob/master/django/conf/global_settings.py
2. 上线的时候记得把 DEBUG 改成 False, 然后加上 ALLOWED_HOSTS
3. MEDIA_ROOT, MEDIA_URL, STATIC_ROOT, STATIC_URL 这几个之前经常搞混,其实可以分为两类,MEDIA_ROOT 指的是上传文件的目录,MEDIA_URL 指的是前缀名,http://example.com/media/, 其中的media 就是 MEDIA_URL, 同样,STATIC_ROOT 指的是静态文件的目录,一般放 css, js 之类,STATIC_URL 值得是前缀名,http://example.com/static/。
4. 一般部署上线的时候,用 nginx 直接渲染静态文件
location /media { alias /root/example_project/public/media; } location /static { alias /root/example_project/static; }
contrib 模块
1. 这里面的模块是 django 自带的 apps,用的比较多的有 admin, auth, sessions
2. admin 模块其实就是 django 的后台
3. auth 模块就是用户模块,功能上比较齐全,基本上可以基于这个模块做扩展。
4. session 模块结合 auth ,用来做用户的登录状态,对于大型网站来说,session 的存储可能是个问题,django 也提供了不同的 backends,支持 db, cache, file 等存储方式。
middleware 模块
1. django 自带的 middlreware 对于普通网站的需求已经蛮完善了,比如 CsrfMiddleware , HttpMiddleware。
2. 如果有自己的需求,也可以自定义,比如记录详细 log 信息,LogMiddleware,还有跨域的 CorsMiddleware。
其他
1. 网上有人说 django 只能做小网站,不支持大的并发,其实这个说法还是很片面的。django 自身只是一个框架,如果网站请求处理慢,很多情况下是自己写的逻辑没有优化。确认是 django 框架问题的话,django 虽然设计上略复杂,但是很多模块还是可以拆分去自定义的。最后实在不济,靠增加机器数量也能顶一阵。
2. django 的国际化模块做的很不错
3. setting 文件可分成 product, 和 dev 两份,根据开发环境和生产环境不同引入不同的 setting 文件。
4. django-celery 用来执行异步任务
5. django 的插件非常多,rosarior/awesome-django