django是基于python的一个web框架,大致结构如下:
在pycharm,python2.7,django1.8,mysql都装好的情况下,现在开始django的初试:
一、基础启动django项目:
1、打开pycharm新建一个django的project,有的pycharm没有创建django的选项,则打开pycharm下面的控制台,进入想要创建项目的根目录下,输入django-admin.py startproject firstSite(firstSite为你自己创建的文件名)
然后在pycharm打开这个目录File->Open->找到目录,打开,可见如下目录:
我们可以看见fistSite目录下还有一个firstSite目录是项目的容器,里面有 __init__.py, settings.py, urls.py, wsgi.py 四个文件和manage.py文件:
__init__.py:一个空文件,告诉 Python 该目录是一个 Python 包;一般不需要修改它
settings.py:该 Django 项目的设置/配置
urls.py:该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"
wsgi.py:一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目
manage.py:一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互
2、了解这些之后,我们要启动服务器:在pycharm控制台中进入到项目容器firstSite下输入:python manage.py runserver 8000,不指定端口的话就是默认端口127.0.0.1:8000,如下:
3、接下来我们在浏览器地址栏中输入:http://127.0.0.1:8000
二、(视图,URL)-1;
接下来我们就要进行一些视图和URL配置;我们的目标是在界面上显示你的姓名,性别和年龄
1、在firstSite 文件夹下创建一个view.py文件,名字你可以自己起;然后再view.py中导入声明并且写入完整的函数:
# -*- coding:utf-8 -*- from django.http import HttpResponse #从django.http模块导入(import)HttpReponse类 def info(requst): #定义一个叫info的视图函数,每个函数至少有一个参数(request) return HttpResponse("张三,25,男") #返回一个HttpResponse实例,括号里面是参数即返回内容
2、接下来我们要让 firstSite项目感知到这个视图,就要配置url:打开urls.py文件如下:
删除里面的注释,然后导入from django.conf.urls模块下的url类,然后添加view中的info函数功能:
# -*- coding:utf-8 -*- from django.conf.urls import url #导入 django.conf.urls 模块下的url类 from . import view #绑定 URL 与视图函数(view.py文件) urlpatterns = [ #url(r'^admin/', include(admin.site.urls)), url(r'^information$',view.info), ]
3、再访问127.0.0.1:8000:
因为我们在url中绑定了information视图函数,所以要访问:127.0.0.1:8000/information/
到此为止呢,我们的第一个小任务就完成了!
那在我们看到页面上显示的内容之前,python-django都做了哪些工作呢?是这样了:
(1)进来的请求转入/info/
(2)Django通过在ROOT_URLCONF配置来决定根URLconf
(3)Django在URLconf中的所有URL模式中,查找第一个匹配/info/的条目
(4)如果找到匹配,将调用相应的视图函数
(5)视图函数返回一个HttpResponse
(6)Django转换HttpResponse为一个适合的HTTP Response,以Web page展示出来
三、(视图,URL)-2
上面的例子是一个静态的网页例子,你访问的/info/永远只能看到那些内容,相当于一个静态的HTML网页,并且将视图和数据混在一起,不符合django的MVC思想。
(1)我们的第二个任务是当前的日期和时间在网页上,不涉及数据库,只是显示时间。这个视图有两件事要做:1、计算当前的日期和时间(datetime)2、返回包含这些值的HttpResponse
1、在view.py中添加currentTime函数(导入datetime模块)
import datetime #导入datetime模块就可以计算获取当前时间 def currentTime(requst): now = datetime.datetime.now() #获取当前时间 html = '<html><body><p>It is now %s</p></body></html>'% now #变量html是页面上要现实的内容,即要返回的内容 return HttpResponse(html)
2、配置url
# -*- coding:utf-8 -*- from django.conf.urls import url #导入 django.conf.urls 模块下的url类 from . import view #绑定 URL 与视图函数(view.py文件) urlpatterns = [ #url(r'^admin/', include(admin.site.urls)), url(r'^information$',view.info), url(r'^time$',view.currentTime), ]
3、访问127.0.0.1:8000/time/
成功了,
(2)下面我们来升级一下这个功能,我们下一个目标是实现一个显示当前的时间和加上时间偏移量的时间:设计如下:/time/plus/1/显示当前时间和一个小时的页面,/time/plus/2/显示当前时间和两个小时的页面,以此类推;
1、我们先来实现view.py里面的方法函数
# -*- coding:utf-8 -*- from django.http import Http404, HttpResponse #从django.http模块导入(import)HttpReponse类 import datetime #导入datetime模块就可以计算获取当前时间 def hour_ahead(requst,offset): #offset从url中提取出来是时间的偏移量,为字符串格式 try: offset = int(offset) #如果该值可以转化为整型,则转化为整型 except ValueError: raise Http404() #否则抛出ValueError异常,提示404(页面不存在),datetime.timedelta()要求参数hours必须是整型
dt = datetime.datetime.now() + datetime.timedelta(hours = offset) #将偏移量加到现在的时间后面
html = '<html><body><p>In %s hous(s),it will be %s.</p></body></html>' % (offset,dt)
return HttpResponse(html)
2、接下来配置url;他们的相同点是/time/plus/,后面的数字可以用数字的通配符来代替,则我们的urls的正则表达式可以设计为:/time/plus/\d+/,我们限制时间偏移量在99个小时之内,则限制数字只能显示1-2个数字。则我们的url修改代码为:
url(r'^time/plus/(\d{1,2})$',view.hour_ahead)
3、然后 访问127.0.0.1:8000/time/plus/1/
4、接下来再测试一下是否只能接收1-2个数字:结果显示多余两个数字也不行,字符串也不行:
四、模板
通过上面的例子我们已经可以将url和视图视图函数都建立映射关系了,是不是很棒棒呢。不过你会发现我们实现到页面上时要在视图函数里面使用字符串拼接的方式写出HTML代码存放在变量里,在return这个变量。可以是可以,但是很麻烦,如果我们要给页面加上什么css效果就会很不方便,不便于维护修改,而且也不符合django的MVC思想。所以呢,我们就要实现视图与模板分离。那应该怎么做呢?
(1)首先我们来了解一下模板; 模板:是一个文本,用于分离文档的表现形式和内容
(2)简单的模板除了HTML,CSS,JS之外还有中有以下几种奇怪符号:
1、{{ *** }}:两个大括号里面的文字(如:{{ person_name }})称为变量(variable)。就是说在此处插入指定变量的值;
2、{% *** %}:被大括号和百分号包裹的文本是 模板标签(template tag),标签:仅通知模板系统完成某些工作的标签;比如说for标签和if标签就类似于python中的for语句和if语句:{% for item in list_1 %}...{% endfor %};{% if 条件 %}... ...{% else %}... ...{% endif %}
(3)那么说,模板怎么用呢?
1、在firstSite根目录下新建一个templates目录,并在该目录下新建一个名为currentTime的HTML文件
2、下面设置模板的路径,打开settings.py文件,找到TEMPLATES这项设置,将DIRS这一项修改为“[BASE_DIR + "/Templates"]”。这告诉django在哪里找到目标模板。
3、然后再新建的currentTime.html文件中添加:
<p>It is now {{ currentTime }} </p>
4、然后打开view.py修改代码:
# -*- coding:utf-8 -*- from django.template.loader import get_template from django.template import Context from django.http import Http404, HttpResponse #从django.http模块导入(import)HttpReponse类 import datetime #导入datetime模块就可以计算获取当前时间 def current_datetime(request): now = datetime.datetime.now() t = get_template('currentTime.html') html = t.render(Context({'currentTime':now})) return HttpResponse(html)
5、为这个设置新的url
url(r'^current_time$',view.current_datetime),
6、访问127.0.0.1:8000/current_time/
这个方法原理是:先载入一个模板文件,然后用Contenxt渲染它,最后返回这个处理好的HttpResponse对象返回
(4)我们在对这个进行方法简化,使用render_to_response()函数一次性的载入这个模板文件,渲染它,然后将此作为HttpResponse返回;view.py文件修改如下:
from django.shortcuts import render_to_response import datetime def current_datetime(request): now = datetime.datetime.now()
return render_to_response('currentTime.html',{'currentTime':now}) #render_to_response()函数的第一个参数必须是要使用的模板名称;如果要给定第二个参数,那么该参数必须是为该模板创建Context时所使用的字典
或者
def current_datetime(request): currentTime = datetime.datetime.now() #注意:locals()包括所有的局部变量,他们可能比你想要模板访问的多,所以要慎用
return render_to_response('currentTime.html',locals())
当然,如果你想要把模板存放于模板目录的子目录中,只需要在render_to_HttpResponse的第一个参数前加上子目录名和‘/’;即:render_to_HttpResponse('子目录名/currentTime.html',{'currentTime': now}),或者使用get_template的话为:t = get_template('子目录名/currentTime.html');
(5)模板继承
如果你想要在一个网页中嵌入另一个网页(多个模板出现相同的代码时),则可以使用include标签;例如一套网站有相同的header和footer等等;举个例子:
1、定义基础模板 base.html
<head> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> <style> header{ font-size:36px; font-weight:400; color: #c0ebd7; margin-bottom:20px} footer{ font-size:30px; color: #bbcdc5} </style> </head> <body> <header>My helpful timestamp site</header> {% block content%}{% endblock %} {% block footer%} <hr> <footer>Thanks for visiting my site</footer> {% endblock %} </body>
2、修改currentTime.html来使用它(继承)
<body> {% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %} <p>It's now {{ currentTime }}.</p> {% endblock %} </body>
3、启动服务器,现在访问127.0.0.1:8000/currentTime
4、好,我们再来试一下之前写的hour_ahead,其代码如下:
def hour_ahead(requst,offset): #offset从url中提取出来是时间的偏移量,为字符串格式 try: offset = int(offset) #如果该值可以转化为整型,则转化为整型 except ValueError: raise Http404() #否则抛出ValueError异常,提示404(页面不存在) dt = datetime.datetime.now() + datetime.timedelta(hours = offset) #将偏移量加到现在的时间后面,datetime.timedelta()要求参数hours必须是整型 return render_to_response('hour_ahead.html', {'hour_offset':offset, 'next_time':dt})
然后新建一个hour_ahead.html来做他的模板
<head> <meta charset="UTF-8"> <title>Title</title> <style type="text/css"> .content{ font-size:20px; color: #665757; } </style> </head> <body> {% extends "base.html" %} {% block title %}Future Time{% endblock %} {% block content %} <p class="content">In {{hour_offset}} hour(s),it will be {{next_time}}.</p> {% endblock %} </body>
5、访问http://127.0.0.1:8000/time/plus/4
这样呢,就完成了一个简单的模板继承;他的工作流流程是这样的;在加载currentTime.html模板时,模板引擎发现了{% extands %}标签,就得知这个模板是一个子模板。模板引擎立刻加载父模板(base.html),在此时,模板引擎发现了base.html里面的三个{% block %}标签,则用子模板里相应的内容替换这些block;如果父模板定义的block子模板中没有的话(比如说{% footer %})系统将使用父模板中定义的值。
并且呢,继承并不会影响上下文,即任何处在继承书上的模板都可以访问到你传到模板中的每一个模板变量。并且你可以根据需要进行嵌套继承;
模板好用,不过这里有几个值得注意的地方;
1、如果使用模板继承,则必须将{% extends %}标签为模板中的第一个模板标记,否则模板继承将不起作用;
2、一般来说,基础模板中的{% block %}越多越好;子模板不必定义父模板中的所有代码块,因此可以利用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行重定义;
3、如果发觉自己在多个模板之间拷贝代码,则你需要考虑将该代码放置到某个父模板的的某个{% block %}中;
4、如果你需要访问父模板中的块的内容,可以使用{% block.super %};这是一个充满魔力的标签,它会表现出父模板中的内容;可以做到不仅仅是覆盖父模板,而是在父模板基础上进行一定的修改。这就给了我们灵活性:既可以完全重写,也可以复用父模板,也可以在复用的基础上扩展。
5、不允许在同一个模板中定义多个同名的{%block %};
五、模型
模型:数据存取层,该层处理与数据相关的所有事物:如何存取、如何验证有效性、包括哪些行为以及数据之间的关系等;
(1)数据库的配置; 在我们完成了了MySQL的安装与激活并且连接了python-mysql之后,接下来我们来配置数据库;
打开settings.py文件,找到DATABASES这一部分并配置相关参数;一般要配置六个参数:
(2)好了,配置完成后我们就来实际操作一下吧;我们先来创建一个Django app——一个包含模型、视图和Django代码,并且形式为独立的 python 包的完整的django应用。那么app和project有什么区别和联系呢?
1、(a)一个project包含多个django app以及对他们的配置;一个app是一套django功能的集合,通常包括模型和视图按python的包结构的方式存在。app很容易被一直到其他的project上,被许多project复用 即:project是配置,app是代码
(b)系统对app有一个约定:如果你使用了django的数据库层(模型),则必须创建一个django app,模型必须放在apps中。
2、在控制台进入firstSite目录,在该目录下输入:pyhton manage.py startapp books ;这是完成之后的目录:
3、我们的目标是创建一下字段关系的表;(a)作者:名,姓,email;(b)出版商:名称,地址,所在城市、省,国家,网站;(c)书籍:书名,出版日期,一个或多个作者(多对多),出版商(一对多)为外键
接下来打开models.py来创建这些表:
# -*- coding:utf-8 -*- from django.db import models # Create your models here. #作者表 class Author(models.Model): first_name = models.CharField(max_length= 30) last_name = models.CharField(max_length= 40) email = models.EmailField() def __unicode__(self): return u'%s %s'(self.first_name,self.last_name)
#出版商表
class Publisher(models.Model):
name = models.CharField(max_length= 30)
address = models.CharField(max_length= 50)
city = models.CharField(max_length= 60)
state_province = models.CharField(max_length= 30)
country = models.CharField(max_length= 30)
website = models.URLField()
def __unicode__(self):
return self.name
#书籍表
class Book(models.Model):
title = models.CharField(max_length= 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __unicode__(self):
return self.title
注意:(a) 我们并没有显式地为这些模型定义任何主键。除非你单独指明,否则django会自动为每个模型生成一个自增长的整数主键字段,每个django模型都要求有单独的主键id;
(b) __unicode__方法的作用是美化打印出来的结果,使人类更方便查看。举个例子:
class Test: def __init__(self, name, job): self.name = name self.job = job def __str__(self): return 'Name:' + self.name instance = Test('xiaoming', 'Teacher') print(instance)
如果没有__unicode__方法,打印的结果是<__main__.Test object at 0x0000022D6D1387B8>格式,有了__unicode__方法后,打印时会按照__unicode__定义的格式来打印,打印结果为Name:xiaoming;
在Django中,如果用的是Python3的话就只能用__str__方法,如果是Python2的话就使用__unicode__方法;
4、现在我们来在django项目中激活这些模型,打开settings.py文件,找到 INSTALLED_APPS 和 MIDDLEWARE_CLASSES,注释掉默认内容并将我们创建的app添加进去,如下:
INSTALLED_APPS = ( # 'django.contrib.admin', # 'django.contrib.auth', # 'django.contrib.contenttypes', # 'django.contrib.sessions', # 'django.contrib.messages', # 'django.contrib.staticfiles', 'books', ) MIDDLEWARE_CLASSES = ( # 'django.contrib.sessions.middleware.SessionMiddleware', # 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', # 'django.contrib.auth.middleware.AuthenticationMiddleware', # 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', # 'django.contrib.messages.middleware.MessageMiddleware', # 'django.middleware.clickjacking.XFrameOptionsMiddleware', # 'django.middleware.security.SecurityMiddleware', )
5、现在我们开始创建数据库表,首先用“python manage.py validate”来验证模型的有效性,如果正确的话你将会看到:
6、模型确认没问题的之后使用来生成CREAT TABLE语句(a)python manage.py makemigrations books:将books这模型中models.py编译到0001_initial.py文件当中
(b)python manage.py sqlmigrate books 0001:将0001_initial.py文件编译成sql语言
(c)python manage.py migrate:完成创建
(3)创建好了之后我们来尝试操作数据库,先进行数据库的增添操作;
1、在templates目录下新建一个adddata.html文件,内容如下:
<div>{{ theAdd }}</div>
2、在urls.py文件中进行配置
from books import views url(r'^adddata$',views.addData),
3、打开books下的views.py文件,我们来尝试着操作数据库;先将books下的Publisher导入;然后我们来试着向Publisher表添加元组;具体代码如下:
# -*- coding:utf-8 -*- from django.shortcuts import render_to_response from books.models import Publisher # Create your views here. def addData(request): p1 = Publisher.objects.create(name = '张三', address = '322 花园小区 高新区', city = '西安', state_province = '陕西省', country = 'China', website = 'http://www.zhangsan.com') p2 = Publisher.objects.create(name='李四', address='3908 关明小区 雁塔区', city='西安', state_province='陕西省', country='China', website='http://www.lisi.com') publisher_list = Publisher.objects.all() return render_to_response('adddata.html', {'theAdd': publisher_list})
4、接下来访问http://127.0.0.1:8000/adddata(前面是之前添加进去的数据)
(4)在进行数据库的修改操作——插入和更新
1、改动数据(改)可以用save()或update(),修改其中一个id=1的name字段,再save(),相当于SQL的UPDATE
def changeData(request): change1 = Publisher.objects.get(id= 1) change1.name = '王五' change1.save() #update() Publisher.objects.filter(id= 2).update(name= '小强') #修改一整列 publisher_list = Publisher.objects.all() publisher_list.update(address= '888 乌鲁木齐') render_to_response('databases_operation.html', {'response_data':publisher_list})
(5)数据查询;数据查询是我们最常用的一种方法,一下有几种数据查询的方法:
1、之前用过的:publisher_list = Publisher.objects.all()——这个方法返回数据库中的所有记录;
2、filter()方法:举例:Publisher.objects.filter(name= '张三',country= 'China');
filter可以传递一个到多个参数来缩小查询取值的范围;不仅如此,filter还可以进行模糊匹配;举例:Publisher.objects.filter(name__contains='press')
3、当然,除了filter这样返回列表的以外,我们还可以返回单个对象:表名.objects.get(筛选条件);这个方法返回的是单个对象,而不是一个列表;所以当查询的结果不止一个(即返回为列表)时,则会抛出异常;如果查询结果为空的话也会抛出异常;DoesNotExist异常就是返回结果为空的异常;对此我们可以做以下处理:
try: p = Publisher.objects.get(name= 'Apress') except Publisher.DoesNotExist: print "Apress is not in database yet" else: print "Apress is in database"
4、数据排序
表名.objects.order_by('字段名1','字段名2')就可以进行数据排序了,如果有多个参数,在第一个参数值相同的情况下就按照第二个参数的标准进行排序;如果在字段名前加上减号例如:Publisher.objects.get('-name')就可以指定逆向排序;
filter()和order_by()也可以进行连锁查询
(6)数据删除
删除数据库只需要调用delete()方法就可以了
1、删除数据函数
# -*- coding:utf-8 -*- from django.shortcuts import render_to_response from books.models import Publisher import pdb # Create your views here. def all_the_data(request,table): list1 = table.objects.all() data = [] data1 = [] for item in list1: #data1 = [item.id,item.name.encode('GBK'),item.address,item.city,item.state_province,item.country] data.append(item) #pdb.set_trace() return data def delete_data(request,p_id): p = Publisher.objects.filter(name = p_id).delete() #按照name进行筛选删除 data = all_the_data(request,Publisher) return render_to_response('databases_operation.html', {'cue':'数据删除成功', 'all_data':data})
2、配置url
url(r'^operation/plus/(\w{2,6})$',views.delete_data)
3、访问http://127.0.0.1:8000/operation/plus/张三