标签: django python
django的安装比较简单,在命令行里执行下面的命令。
pip install django
为了能使用django已有的模板代码,需要使用django-admin命令来执行诸如创建项目,创建项目下的子模块(在django中称为app)的工作。django-admin会帮我们准备好一系列目录结构和文件,在保持文件和目录规范同时减少我们的工作量。
下面以创建项目mysite为例说明创建过程,执行
$ django-admin startproject mysite
''' 项目目录结构
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
'''
进入mysite目录,执行
$ python manage.py startapp app1
''' app1结构
app1/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
'''
为了激活新增加的app1模块,需要修改mysite.settings.py,在INSTALLED_APPS的列表项中增加一项’app1’。
此外django自带一套管理后台系统,支持简单的用户和组管理功能以及对应的权限管理,为了启用此功能,可以执行下面初始化工作。
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver 0.0.0.0:8000
这样就可以通过本地localhost:8000/admin便可见到admin后台的登录页面了。
django使用了MVT的模式,数据模型使用Model来建模,View用来接收/处理/返回数据,而Template用来渲染展现内容。通过MVT模式将数据/处理/表现三层进行了分离。
Model支持ORM,根据需要,可以创建普通的只有数据项模型,也可以创建带有业务逻辑的模型。
对于简单的应用,业务逻辑可以放在View里处理,如果是复杂系统,那就需要创建单独的业务层,在view里借助业务层来完成相应的功能。
Template使用了自定义的模板语法,可以根据情况使用。如果有独立的前端支持,完全可以不借用模板。前后端只通过数据api接口交互,前端可以使用各种流行框架来解决展现逻辑。如果人员或模块划分没那么细分,使用模板在后端进行全部页面渲染或者部分渲染都是可行的。
如上节所述,View层用于接收请求,处理请求,并返回数据。模块的view处理逻辑都位于的各模块的views.py文件中,下面的代码示例了一个没有什么作用的处理器,它返回一个显示一行"hello world"的页面。更复杂的功能包括数据库访问,业务规则应用,远程过程调用等,都可以在这样一个函数中进行流程的组织。
# app1/views.py
from django.http import HttpResponse
def test(request):
return HttpResponse('Hello world')
目前客户是访问不到这个页面的,因为还没有为这个页面设置uri,或者说没有在django中为其设置路由。
django的路由在文件urls.py中进行配置。在以项目名命名目录下的urls.py中进行全局路由分派,其会将子模块的请求派发到相应的app.urls中进行二级路由,下面展示示例中的mysite的urls文件内容:
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app1/', include('app1.urls'))
]
简单的说,所有路由规则都定义在urlpatterns的列表内,每一项都是一条规则。定义了一个路径对应的处理函数或要派发模块的urls配置。
下面示例了将上节views.test返回的页面在app1.urls.py中添加路由规则的代码:
# app1/urls.py
from django.conf.urls import url
from app1 import views
urlpatterns = [
url(r'^test/$', views.test),
]
这样就可以访问http://localhost:8000/app1/test页面,可看到其中展示的"hello world"。
在django中定义数据模型十分便捷,其ORM框架可以十分方便的生成对应的DDL语句,并通过表管理器生成DQL/DML,向开发人员屏蔽了底层的数据库及SQL的知识。
下面以演员,电影和签约为例说明django中model的使用。假设影视公司需要跟踪演员和电影的签约和支付情况,建立下面的简化模型。Actor和Movie对应现实中的演员和电影,使用Contact来记录二者之间的合同关系,以及演员出演电影的角色信息,薪资情况等。
# app1/models.py
from django.db import models
class Actor(models.Model):
name = models.CharField(max_length=128, db_index=True)
birth = models.DateField()
def __unicode__(self):
return self.name
class Movie(models.Model):
name = models.CharField(max_length=128, db_index=True)
actors = models.ManyToManyField(Actor, through='Contact')
def __unicode__(self):
return self.name
class Contact(models.Model):
actor = models.ForeignKey(Actor)
movie = models.ForeignKey(Movie)
role = models.CharField(max_length=64)
payment = models.IntegerField()
sign_date = models.DateField()
def __unicode__(self):
return self.role + self.sign_date.strftime('%Y-%m-%d')
在django中,models下定义了各种类型的字段,如DateField, IntegerField等,对应了SQL创建的各种column类型。此外还定义了PramaryKey, ForeignKey字段来声明主外键。这些字段都会对应到具体数据库表中的字段。
为了声明演员和电影之间的这种关系,需要使用 ManyToManyField,其声明的字段并不会在表中存在,但它告诉django框架,二者之间存在多对多的关系,并指明关系模型的名称。在一对多对多关系中,只需要在其中任意一个模型中声明即可,不需要在声明两次。
在定义了模型之后,就可以让框架帮我们创建对应的数据表了。在项目目录下执行命令:
python manage.py makemigrations app1
python manage.py migrate
其中第一行用来为app1生成一个迁移文件,存储在对应模块的migration目录下。
第二行是执行具体的数据库创建/修改工作。
在执行migrate之前,可以执行sqlmigrate app1 0001来查看自动生成的SQL语句。如下所示。
BEGIN;
CREATE TABLE "app1_actor" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(128) NOT NULL, "birth" date NOT NULL);
CREATE TABLE "app1_contact" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "role" varchar(64) NOT NULL, "payment" integer NOT NULL, "sign_date" date NOT NULL, "actor_id" integer NOT NULL REFERENCES "app1_actor" ("id"));
CREATE TABLE "app1_movie" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(128) NOT NULL);
ALTER TABLE "app1_contact" RENAME TO "app1_contact__old";
CREATE TABLE "app1_contact" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "role" varchar(64) NOT NULL, "payment" integer NOT NULL, "sign_date" date NOT NULL, "actor_id" integer NOT NULL REFERENCES "app1_actor" ("id"), "movie_id" integer NOT NULL REFERENCES "app1_movie" ("id"));
INSERT INTO "app1_contact" ("sign_date", "movie_id", "payment", "role", "actor_id", "id") SELECT "sign_date", NULL, "payment", "role", "actor_id", "id" FROM "app1_contact__old";
DROP TABLE "app1_contact__old";
CREATE INDEX "app1_actor_name_b00a760a" ON "app1_actor" ("name");
CREATE INDEX "app1_movie_name_2c168972" ON "app1_movie" ("name");
CREATE INDEX "app1_contact_actor_id_64e85f27" ON "app1_contact" ("actor_id");
CREATE INDEX "app1_contact_movie_id_2ae6d051" ON "app1_contact" ("movie_id");
COMMIT;
从SQL中我们可以知道,创建的表名为{子模块名_模型名},表中自动创建了自增类型的主键id。
下面以电影碟中谍为例来说明新增模型到数据表的方式
from models import Actor, Movie, Contact
# 使用表管理器objects来新增记录
movie = Movie.objects.create(name='碟中谍6:全面瓦解')
# 使用模型对象新增记录
actor = Actor()
actor.name = '汤姆·克鲁斯'
actor.birth = '1962-07-03'
actor.save()
# 添加签约关系
Contact.objects.create(movie=movie, actor=actor,role='伊森·亨特', payment=800, sign_date='2018-01-10')
# 添加另一个演员及签约
actor = Actor.objects.create(name='丽贝卡·弗格森', birth='1983-10-19')
actor.save()
Contact.objects.create(movie=movie, actor=actor,role='伊尔莎·浮士德', payment=400, sign_date='2018-01-20')
下面列出一些常用的查询方式:
# 单表查询
actors = Actor.objects.filter(name__contains='汤姆·克鲁斯')
actor = actors[0]
print actor.name, actor.birth # 汤姆·克鲁斯 1962-07-03
# 由对象进行关联查询
tom_movies = actor.movie_set.all()
print tom_movies # ]>
tom_contacts = actor.contact_set.all()
print tom_contacts # ]>
# 表关联查询
contacts = Contact.object.filter(actor__name__contains='汤姆·克鲁斯', movie__name__contains='6')
contact = contacts[0]
print contact.actor.name, contact.movie.name, contact.role
# 汤姆·克鲁斯 碟中谍6:全面瓦解 伊森·亨特
# 反向关联查询
actors = Actor.objects.filter(contact__sign_date__gt='2018-01-01')
print actors # , ]>
actors = Actor.objects.filter(contact__movie__name__contains='6')
print actors
# , ]>
模型的修改与删除更加简单,调用对象上的delete即可删除对象,将对象修改后进行save即可实现数据的更新。
上节所有查询结果返回的是一个支持迭代的QuerySet,如果需要排序,在结果集上调用order_by(‘field’)即可。
如果需要限制返回的结果数量,像数据一样进行切片即可,如Actor.objects.all()[0:10]
django的查询集是lazyload的,只有真的需要数据的时候才会执行数据库查询。
from django.db.models import Sum, Avg
# 计算碟6所有演员的费用
Contact.objects.filter(movie__name__contains='碟中谍6').aggregate(total=Sum('payment'))
# Out[17]: {'total': 1200}
# 计算每部电影演员费用的汇总
qs = Contact.objects.values('movie__name').annotate(cost=Sum('payment'))
#
print qs.query
# SELECT "app1_movie"."name", SUM("app1_contact"."payment") AS "cost" FROM "app1_contact" INNER JOIN "app1_movie" ON ("app1_contact"."movie_id" = "app1_movie"."id") GROUP BY "app1_movie"."name"
Template可以是纯html文档,在view中通过将html文档作为字符串返回即可。
如果需要django在后端动态渲染数据,例如返回符合条件的结果列表,可以使用template。在template中可以对传入的对象进行求值,从而生成实例页面。
template的求值语法为{{value}},同时支持条件及循环等tag,tag语句需要使用{%tag sentence %}将sentence进行包裹,并需要以{%endtag%} 来声明tag结束。
下面是一个template页面。
# template/app1/test.html
<html>
<head>
<title>{{title}}title>
head>
<body>
<ul>
{%for actor in actors%}
<li>{{actor.name}} -- {{actor.birth}}li>
{%endfor%}
ul>
body>
为了将数据传入这个模板页面,需要在views中函数中传递相应的参数给模板。
# app1/views.py
from app1.models import Actor
from django.shortcuts import render
def test(request):
actors = Actor.objects.all()
context = {
'title': '所有演员',
'actors': actors
}
html = render(request, 'app1/test.html', context)
return html
django中的admin站点可以通过注册的机制,快速将模型转化为管理页面,在页面中可以实现列表查询,数据的增删改等各种操作。
下面列出了将Actor模型注册到admin站点的代码,其中声明的ContactsInline类是为了在演员的编辑页面中可以关联编辑签约模型数据。
from django.contrib import admin
from web.models import *
class ContactsInline(admin.TabularInline):
model = Contact
extra = 0
class ActorAdmin(admin.ModelAdmin):
list_display = ['name', 'birth']
inlines = [ContactsInline, ]
admin.site.register(Actor, ActorAdmin)
完成之后,打开admin站点,便可以看到新增的Actor管理页面入口。
在项目的根目录下存放着项目的Settings,如果要指定不同的设置,可以通过环境变量DJANGO_SETTINGS_MODEULE来设置。
export DJANGO_SETTINGS_MODULE=mysite.settings
另一种是在python代码中通过os模块来设置
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
在代码中可以动态的修改当前的配置
from django.conf import settings
settings.DEBUG = True
有时可能需要在非django项目中复用django的功能或代码,例如引用已经定义的django model,这也是可以的。参见下面的代码片段:
import sys
import os
setting_path = '/path/of/settings.py'
setting_dir = os.path.dirname(setting_path)
sys.path.append(setting_dir)
os.environ['DJANGO_SETTINGS_MODULE'] = 'setting_dir.settings'
import django
django.setup()