1、Django的介绍
Django是Python下的一款优秀的Web框架。
(1)什么是框架?
框架是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法。另一种定义认为,框架是可被应用开发者定制的应用骨架。
(2)Django的优点
- 自带轮子众多,方便快速开发,如 Authentication,Cache,模板;
- 天生的MVC设计模式;
- 实用的管理后台;
- 自带ORM,Template,Form,Authentication核心组件;
- 简洁的URL设计;
- 周边插件丰富。
(2)Django的缺点
Django的设计哲学:大量内置各种插件。Django比Flask、Tornado重量级。
Flask:轻量级,只设计引擎,其他都是作为周边插件,生态系统较完善,需要安装插件。
Tornado:用于多并发,异步场合,生态系统相对Django和Flask少,很多都需要自己实现。
2、Django的历史
最初开发者:Adrian 和 Simon。
开发背景: World Online维护几个新闻站点,要求快速发布新闻,快速建立其它新闻站点(快速开发,数据驱动)。
开源时间:2005年夏天 World Online小组宣布开源。
官方网站:https://www.djangoproject.com/
文档地址:https://docs.djangoproject.com/en/3.0/
3、Django的安装
使用版本: Django 3.0.2 和 Python3.7.3
Django安装
$ pip install django
$ python -c "import django; print(django.get_version())"
4、Django的组件
(1)示例需求:在web页面上,要求列出最新的10本书。
1)使用python CGI
#!/usr/bin/env python
import MySQLdb
print "Content-Type: text/html\n"
print "<html><head><title>Books</title></head>"
print "<body>"
print "<h1>Books</h1>"
print "<ul>"
connection = MySQLdb.connect(user='me', passwd='letmein', db='my_db')
cursor = connection.cursor()
cursor.execute("SELECT name FROM books ORDER BY pub_date DESC LIMIT 10")
for row in cursor.fetchall():
print "<li>%s</li>" % row[0]
print "</ul>"
print "</body></html>"
connection.close()
2)使用Django
# models.py (the database tables)
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=50)
pub_date = models.DateField()
# views.py (the business logic)
from django.shortcuts import render
from models import Book
def latest_books(request):
book_list = Book.objects.order_by('-pub_date')[:10]
return render(request, 'latest_books.html', {'book_list': book_list})
# urls.py (the URL configuration)
from django.conf.urls import url
import views
urlpatterns = [
url (r'^latest/$', views.latest_books),
]
# latest_books.html (the template)
<html><head><title>Books</title></head>
<body>
<h1>Books</h1>
<ul>
{% for book in book_list %}
<li>{{ book.name }}</li>
{% endfor %}
</ul>
</body></html>
(2)Django的基本组件
1)Model(models.py)
Model文件主要用一个 Python 类来描述数据表,我们 称之为 模型(model)。运用这个类,我们可以通过简单的 Python 的代码来创建、检索、更新、删除数据库中的记录而无需写一条又一条的SQL语句。
2)View(views.py)
View文件包含了页面的业务逻辑,称之为视图。 latest_books()函数叫做视图函数。
3)URL(urls.py)
该文件指出了什么样的 URL 调用什么视图。 在这个例子中 /latest/ URL 将会调用 latest_books() 这个函数。 换句话说,如果你的域名是example.com,任何人浏览网址http://example.com/latest/将会调用latest_books()这个函数。
4)Template(templates目录)
templates是 html 模板,它描述了这个页面的设计是如何的。 templates使用带基本逻辑声明的模板语言,如{% for book in book_list %}。
(3)基本响应流程
graph TD
A[用户请求] --> B[urls.py]
B --> C[views.py]
C --> D[models.py]
C --> E[templates]
C --> A
5、Django的基本使用
(1)案例需求
- 一个网站可以让用户投票,可以查看投票结果
- 后台管理网站,可以添加删除修改投票
(2)新建项目
$ django-admin startproject project-name
$ django-admin startproject mysite
(3)运行项目
$ python manage.py runserver $bind_host:$listen_port
$ python manage.py runserver
$ python manage.py runserver 0.0.0.0:8000
(4)说明
- 启动的是django内置webserver,仅用于开发测试。
- 修改mysite/mysite/settings.py中的ALLOWED_HOSTS=[],将允许的主机或域名添加进来,可以允许访问该域名或主机的django的web页面。
- 更改python文件后server会自动reload。
(5)目录结构
~/project $ tree mysite
mysite
├── manage.py # Django管理工具
└── mysite
├── __init__.py
├── asgi.py
├── settings.py # project的配置文件
├── urls.py
└── wsgi.py
1 directory, 6 files
(6)实例一:hello world
# urls.py
from django.conf.urls import url
from django.contrib import admin
from django.http import HttpResponse
def index(request):
return HttpResponse('This is index page.')
def hello(request):
return HttpResponse('Hello world')
urlpatterns = [
url(r'^$', index),
url(r'^hello/$', hello)
]
- urlpatterns:包含着URL实例的列表
- url函数 (regex, view, **kwargs, name)
- view接收function object,而不是执行函数,index和hello后不需要加()
- urlpatterns中,路径前面的/不能加,后面的/必须加,当后面的/加上时,在浏览器中的地址最后的/可写可不写,不写时,会重定向到加/的地址,而urlpatterns如果后面的/不加,浏览器中最后的/不加的话正常访问,浏览器中加上/出现404页面
- 项目入口urls:urls.py中的urlpatterns
- request
- HttpResponse
6、Django app
(1)Django app与Django project的区别
1)Django app是Django框架编写的应用,Django project是Django的配置文件。
2)Django app才能使用model。
3)Django app设计是可插拔的,新建的app可以在project目录中,也可以不在project目录中,通过项目目录中的settings文件中的INSTALLED_APPS=[]列表管理,新建的app必须写入该列表,app作为模块导入项目,模块路径必须在python path中。
(2)新建app
$ python manage.py startapp app-name
$ python manage.py startapp polls
$ django-admin startapp polls2
(3)目录结构
~/projects/mysite $ tree polls
polls
├── __init__.py
├── admin.py # 管理后台设置
├── apps.py # app的配置文件
├── migrations # 数据迁移,将表结构更改写入数据库
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
(4)配置project settings导入app
# mysite/settings.py
...
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
...
(5)编写app view
# polls/views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, at polls index")
(6)编写urls
# mysite/urls.py
from django.conf.urls import url, include
...
urlpatterns = [
url(r'^$', index),
url(r'^hello/', hello),
url(r'^polls/', include('polls.urls')),
#url(r'admin', admin.site.urls),
]
# polls/urls.py (默认不存在)
from django.conf.urls import url
from . import views
urlpatterns = [
url('^$', views.index, name='index')
]
7、Django数据库支持
(1)Django默认支持以下数据库:
- sqlite
- MySQL(MariaDB)
- PostgreSQL
- Oracle
(2)数据库设置
# mysite/settings.py
# 默认使用 sqlite
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# 使用 mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_name',
'USER': 'user',
'PASSWORD': 'password',
'HOST': '127.0.0.1',
'PORT': 3306,
}
}
(3)Python 3.5 Mysql Driver
MySQLdb驱动不支持 Python3.5,可选驱动有:
- pymysql:python写的,速度慢,使用方法见 https://my.oschina.net/waston/blog/544329
- mysqlclient:fork的mysqldb,支持 python3.3+,速度快,django官方推荐
- 在对db没有要求的情况下,postgre或许是个更好的选择,详见:https://docs.djangoproject.com/en/1.10/ref/databases/
8、编写 App models
(1)ORM介绍
- 用python方法、数据结构访问db
- 可以兼容不同数据库
(2)投票app的models编写
# polls/models.py
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
(3)field的类型及属性
field的类型:CharField,DateTimeField,IntegerField
field的属性:ForeignKey,on_delete,max_length,default,blank,null
(4)Models的迁移(Models Migrate)
1)应用场景
编写完数据模型需要应用到数据库,django为了能跟踪表结构的变化,增加了migrations版本控制功能,如果没有该功能每次表结构变化,就需要重新生成表结构,重新导入数据。
2)Models Migrate命令
$ ls polls/migrations
$ python manage.py makemigrations
Migrations for 'polls':
0001_initial.py:
- Create model Choice
- Create model Question
- Add field question to choice
$ ls polls/migrations
$ python manage.py sqlmigrate polls 0001_initial
$ python manage.py migrate # 应用数据迁移到数据库
$ sqlite3 db.sqlite3 # 查看sqlite数据库
SQLite version 3.6.20
sqlite> .tables
django_migrations # 存放元数据
$ python manage.py shell # 进入ipython
>>> from django.utils import timezone
>>> from polls.models import Question, Choice
>>> q = Question(question_text=“What's new?”, pub_date=timezone.now())
>>> q.save() # 保存到数据库中
>>> q.id; q.pk; q.question_text; q.pub_date; # 访问属性
django中的时间有两种表示方式:
from django.utils import timezone
pub_date=timezone.now() # 带时区的表示方式
import datetime
pub_date=datetime.datetime.now() # 不带时区的本地时间表示方式
project的settings.py文件可以设置使用什么时区,其中I18N表示数据库中存的都是UTC时间,不同时区的用户请求再做转换。
(5)说明
- 一个class代表一张表,多对多会产生额外一张关系表
- 默认主键为id, 也可以自定义主键
- 表名默认为
${app_name}_${class_name_lower}
(6)models的增删改查
增加:
# 方式一:
q = Question(**kwargs)
q = Question(question_text='what is up?', pub_date=timezone.now())
q.save()
# 方式二:(objects管理器)
q = Question.objects.create(**kwargs)
q = Question.objects.create(question_text='what is wrong?', pub_date=timezone.now())
q.id
3
q.question_text
'what is wrong?'
查询:
Question.objects.all() # 查询表中所有数据,获取QuerySet,一个类list
Question.objects.filter(question_text="What's up?") # 获取QuerySet,一个类list
Question.objects.filter(id_gt=1) # 获取id大于1的object
Question.objects.get(id=1) # 获取一个object,Question object
更改:
q = Question.objects.get(id=1)
q.question_text = 'some text'
q.save()
Question.objects.filter(id=1).update(question_text='Why ?') # 返回int类型,表示更改了几条数据
1
Question.objects.filter(id_gt=1).update(question_text='What is it ?')
2
删除:
q = Question.objects.get(id=1)
q.delete() # 返回删除了几条数据
(1, {'polls.Choice':0, 'polls.Question': 1})
Question.objects.filter(id=1).delete()
Question.objects.all().delete()
(7)给models增加一些方法
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
说明:
- __str__ :返回的不是object,而是定义的text,方便调试,python3的方法。
- __unicode__:返回的不是object,而是定义的text,方便调试,python2的方法。
- timezone 带来的 datetime aware 和 naive问题:timezone是aware的,即带时区的,datetime是naive的,即不带时区的。
运行查看:
$ python manage.py shell
>>> from polls.models import Question, Choice
>>> from django.utils import timeozne
>>> q = Question.objects.create(question_text="How are you ?", pub_date=timezone.now())
>>> q = Question.objects.first() # 没有上面create语句,需要执行该句。create不需要获取数据,可以直接得到
>>> q
<Question: How are you ?>
>>> q.was_published_recently()
>>> True
>>> d = timezone.now() - timezone.timedelta(days=2)
>>> q.pub_date = d
>>> q.save() # create不用save,修改才需要save
>>> q.was_published_recently()
>>> False
(8)View中使用Model
需求:列出最近的5个问题。
# polls/views.py
from django.http import HttpResponse
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5] # 倒序,支持切片
latest_question_list = Question.objects.order_by('pub_date')[:5] # 正序
latest_question_list = Question.objects.all().order_by('-pub_date')[:5] # 省略了all()函数调用
latest_question_list = Question.objects.order_by('-pub_date').filter(id=5) # 可以调用filter函数
output = ', '.join([q.question_text for q in latest_question_list])
return HttpResponse(output)
(9)完成详情,提供投票,显示投票结果
# polls/views.py
def detail(request, question_id):
return HttpResponse('Your are looking at question {}'.format(question_id))
def results(request, question_id):
response = "You are looking at results of question {}".format(question_id)
return HttpResponse(response)
def vote(request, question_id):
return HttpResponse('Your are voting on question {}'.format(question_id))
# polls/urls.py
urlpatterns = [
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
说明:
- (?P<question_id>[0-9]+):命名组捕获,获取一个key-value对,key为question_id,值为0-9的数字,相当于调用detail(request, question_id=123)。不用命名捕获相当于调用detail(request, 123)。
- detail(request, question_id):调用方式。
- args, kwargs always string:多个命名捕获就传入多个参数,即相当于调用:detail(request, **kwargs)。从URL中获取到的参数都是string类型,如果需要其他类型,需要做转换。