谈谈 django 应用实践
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/djang o/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 {