谈谈 django 应用实践
python 的 web 框架非常多,比较出名的有 django, flask, tornado。django 作为一个老牌框架,无论是文档还是代码质量都非常高,另外他自带的 admin 后台和一些有用的 app,如果你的需求是做 cms 之类的 web 应用的话,基本上不用开发多少代码就能出一个成品。不过很多新手可能一开始不太适应他的设计模式,遇到问题后基本就懵了,所以这里我按照自己用 django 的经验,写一下 django 的一些应用实践,可能写的比较零散,大家见谅。
整体流程
首先我们得了解下 django 这个框架整体的处理流程,假设我们采用 nginx + uwsgi + django 的 web server 模式
- 一个请求过来后,首先经过 nginx 做反向代理,将请求转发到 uwsgi (python 用 wsgi 这种协议来解析 http 请求,uwsgi 是一个 解析 wsgi 的应用),uwsgi 再将解析过的数据传到 django。
- django 收到请求后,首先会经过一组全局的中间件 (middleware),调用 process_request 作为预处理,比如解析用户状态,检验 csrf_token (post 请求),如果有问题,则直接返回 response,不再调用 view 函数。否则,调用 process_view ,如果没问题进入 view 函数。
- 进入 view 函数后开发者可以写自己的逻辑,比如操作数据库,更新缓存,最后返回一个 response。
- 接下来 跳出 view 函数,重新进入 middleware,调用 process_response,对 response 做些最后的修饰,返回给用户。
views 模块
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")
python 的装饰器很好用,也可以用于 views 函数, 比如下面的装饰器用于登录用户的检测
@login_required
def simple_view(request):
return HttpResponse()
urls 模块
urls.py 这个文件将访问的 url 跟 view 模块对应起来,按从上到下的顺序匹配
采用 include 函数可以包含其他 app 的 urls,namespace 参数定义后可以在模板中直接调用,比如
{% url 'articles'%}
url(r'^articles/$', include(articles.urls), namespace="articles"),
models 模块
model 是具有处理数据库的一种面向对象的方法的类,能够让不熟悉数据库语句的程序员也能快速操作数据库
采用面向对象的方式创建类,加上 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):
···
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
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()
Person.objects.all(), 这里面的 objects 其实是个 manager, 实现了 all, filter 等函数,可以自定义。
class Person(models.Model):
#...
object = models.Manager()
django 的 queryset 是惰性的,只有在真正用的时候才会去数据库查询,并且查询一次后,会有缓存,当再次遍历这个 queryset 的时候,不会再去查询。
person_set = Person.objects.filter(first_name="Dave")
数据库第一次创建后,会有再次更新字段的需求。django 在 1.4 版本前并没有这个功能,得用第三方库 south 来更新,后来的版本 django 自带了 migration 功能,能够将最新的 model 版本和数据库的字段作对比,自动生成 migration 文件
python manage.py makemigrations # 生成 migration 文件
python manage.py migrate # 将更改应用到数据库
orm 不能直接看到 raw sql 语句,可以通过如下语句查看 sql
from django.db import connection
connection.queries
可以使用 django-debug-toolbar 插件查看慢查询,也能对对哪些页面载入较慢有个大致的了解。
在 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 模块
前端传上来一个表单的值,但是没法确认这些值是不是为空,是不是类型正确,这个时候当然可以自己一个个值判断,也可以采用 forms 模块去做验证,用过 django-rest-framework 的同学会知道和里面的 serializers 是同一个概念
class PersonDetailsForm(forms.Form):
name = forms.CharField(max_length=100)
age = forms.IntegerField()
如果你用的是模板渲染的方式,那么展示的时候更简单,表单能自动生成 html 的表单。
f = PersonDetailsForm()
print(f.as_p())
<label for="id_name">Name:</label> <input id="id_name" maxlength="100" name="name" type="text" />
<label for="id_age">Age:</label> <input id="id_age" name="age" type="number" />
admin 模块
admin 基本上开箱即用,如果需要定制的话,也能做一些组件的定制,不过这些东西得看文档去详细了解了。
commands 模块
django 提供了后台脚本模块,可以自己集成 BaseCommand 类去自定义脚本
python mannage.py -h
这个命令就能看到所有的 commands 命令
如果不想用他的模块,又想引入 django 项目的一些模块,可以采用下面的方法解决
os.environ.setdefault(“DJANGO_SETTINGS_MODULE”, “myproject.settings”)
django.setup()
settings 模块
- 所有的 settings 默认值都在这里面能找到, https://github.com/django/django/blob/master/django/conf/global_settings.py
- 上线的时候记得把 DEBUG 改成 False, 然后加上 ALLOWED_HOSTS
- 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/ 。
- 一般部署上线的时候,用 nginx 直接渲染静态文件
location /media {
alias /root/example_project/public/media;
}
location /static {
alias /root/example_project/static;
}
contrib 模块
- 这里面的模块是 django 自带的 apps,用的比较多的有 admin, auth, sessions
- admin 模块其实就是 django 的后台
- auth 模块就是用户模块,功能上比较齐全,基本上可以基于这个模块做扩展。
- session 模块结合 auth ,用来做用户的登录状态,对于大型网站来说,session 的存储可能是个问题,django 也提供了不同的 backends,支持 db, cache, file 等存储方式。
middleware 模块
- django 自带的 middlreware 对于普通网站的需求已经蛮完善了,比如 CsrfMiddleware , HttpMiddleware。
- 如果有自己的需求,也可以自定义,比如记录详细 log 信息,LogMiddleware,还有跨域的 CorsMiddleware。
其他
- 网上有人说 django 只能做小网站,不支持大的并发,其实这个说法还是很片面的。django 自身只是一个框架,如果网站请求处理慢,很多情况下是自己写的逻辑没有优化。确认是 django 框架问题的话,django 虽然设计上略复杂,但是很多模块还是可以拆分去自定义的。最后实在不济,靠增加机器数量也能顶一阵。
- django 的国际化模块做的很不错
- setting 文件可分成 product, 和 dev 两份,根据开发环境和生产环境不同引入不同的 setting 文件。
- django-celery 用来执行异步任务
- django 的插件非常多, https://github.com/rosarior/awesome-django
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论