虚拟环境搭配
安装和配置
安装虚拟环境的命令:
1)sudo pip install virtualenv #安装虚拟环境
2)sudo pip install virtualenvwrapper #安装虚拟环境扩展包
3)编辑家目录下面的.bashrc文件,添加下面两行。
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
4)使用source .bashrc使其生效一下。
使用
创建python3虚拟环境:
mkvirtualenv -p python3 bj19 # bj19是虚拟环境名
进入虚拟环境工作:
workon 虚拟环境名
查看机器上有多少个虚拟环境:
workon 空格 + 两个tab键
退出虚拟环境:
deactivate
删除虚拟环境:
rmvirtualenv 虚拟环境名
虚拟环境下安装包的命令:
pip install 包名
注意:不能使用sudo pip install 包名,这个命令会把包安装到真实的主机环境上而不是安装到虚拟环境中。
查看虚拟环境中安装了哪些python包:
pip list
pip freeze
安装django环境:
pip install django==1.8.2
创建项目
建立文件夹
mkdir bj19 # bj19是文件夹名 随便起
进入bj19 创建应用包
python manage.py startapp booktest # booktest是应用名,可以随便起
更改settings.py设置
# 注册应用 INSTALLED_APPS = ( \'django.contrib.admin\', \'django.contrib.auth\', \'django.contrib.contenttypes\', \'django.contrib.sessions\', \'django.contrib.messages\', \'django.contrib.staticfiles\', \'booktest\', # 注册创建的booktest应用 如果没有这个 创建数据库会报NO changes 错误 ) .... # 模板配置 TEMPLATES = [ { \'BACKEND\': \'django.template.backends.django.DjangoTemplates\', \'DIRS\': [os.path.join(BASE_DIR, \'templates\')], # 指定templates模板目录路径 \'APP_DIRS\': True, \'OPTIONS\': { \'context_processors\': [ \'django.template.context_processors.debug\', \'django.template.context_processors.request\', \'django.contrib.auth.context_processors.auth\', \'django.contrib.messages.context_processors.messages\', ], }, }, ] .... # 数据库配置 DATABASES = { \'default\': { \'ENGINE\': \'django.db.backends.mysql\', # 指定用什么数据库 \'NAME\': \'test1\', # 指定数据库名 \'USER\': \'root\', # 数据库账号 \'PASSWORD\': \'mysql\', # 数据库密码 \'HOST\': \'localhost\', # 数据库IP 本机用localhost \'PORT\': 3306, # 端口 } } .... # 本地化配置 LANGUAGE_CODE = \'zh-hans\' # 本地化语言 TIME_ZONE = \'Asia/Shanghai\' # 本地化时间 .... # 静态资源配置 STATIC_URL = \'/static/\' STATICFILES_DIR=[os.path.join(BASE_DIR, \'static\')] # 静态资源存放路径
创建模板文件夹 对应应用名在templates创建子目录
创建静态资源文件包
创建数据库test1
create database test1 charset=utf8;
项目中引用pymysql
test1/__init__.py文件中
import pymysql pymysql.install_as_MySQLdb()
建表
booktest/models.py中
from django.db import models from datetime import date # Create your models here. class BookInfo(models.Model): \'\'\'图书模型类\'\'\' # 书名 # btitle = models.CharField(verbose_name=\'书名\', max_length=20) # verbose_name= 在admin后台显示的标题 max_length=最大长度 btitle = models.CharField(\'书名\', max_length=20) # 如果标题放在第一位 则可以省略verbose_name= # 出版时间 bpub_date = models.DateField() # 阅读量 bread = models.IntegerField(default=0) # default 设置默认值 # 评论数 bcomment = models.IntegerField(default=0) # 是否删除 isDelete = models.BooleanField(default=False) # 定义数据表名称--元选项 class Meta: # 指定生成的数据表名 db_table = \'bookinfo\' class HeroInfo(models.Model): \'\'\'英雄模型类\'\'\' # 姓名 hname = models.CharField(\'英雄\', max_length=20) # 性别 hgender = models.BooleanField(default=False) # 功夫 hcomment = models.CharField(\'功夫\', max_length=128) # 关联书名 hbook = models.ForeignKey(\'BookInfo\') # 是否删除 isDelete = models.BooleanField(default=False) # 数据表名 class Meta: db_table = \'heroinfo\' class AreaInfo(models.Model): \'\'\'地域模型类\'\'\' # 地名 atitle = models.CharField(\'地名\', max_length=20) # 自关联pid aParent = models.ForeignKey(\'self\', null=True, blank=True) # 表名 class Meta: db_table = \'areainfo\'
迁移
python manage.py makemigrations
python manage.py migrate
报错
解决方案:
看一下settings.py文件 apps中有没有添加 booktest
迁移成功
查看数据库
默认值并不在数据库层面生效,而是在django创建对象时生效。
图书表结构
表heroinfo结构如下:
Django框架会根据关系属性生成一个关系字段,并创建外键约束。
测试数据
在数据库命令行中,复制如下语句执行,向booktest_bookinfo表中插入测试数据:
insert into bookinfo(btitle,bpub_date,bread,bcomment,isDelete) values (\'射雕英雄传\',\'1980-5-1\',12,34,0), (\'天龙八部\',\'1986-7-24\',36,40,0), (\'笑傲江湖\',\'1995-12-24\',20,80,0), (\'雪山飞狐\',\'1987-11-11\',58,24,0);
向booktest_heroinfo表中插入测试数据:
insert into heroinfo(hname,hgender,hbook_id,hcomment,isDelete) values (\'郭靖\',1,1,\'降龙十八掌\',0), (\'黄蓉\',0,1,\'打狗棍法\',0), (\'黄药师\',1,1,\'弹指神通\',0), (\'欧阳锋\',1,1,\'蛤蟆功\',0), (\'梅超风\',0,1,\'九阴白骨爪\',0), (\'乔峰\',1,2,\'降龙十八掌\',0), (\'段誉\',1,2,\'六脉神剑\',0), (\'虚竹\',1,2,\'天山六阳掌\',0), (\'王语嫣\',0,2,\'神仙姐姐\',0), (\'令狐冲\',1,3,\'独孤九剑\',0), (\'任盈盈\',0,3,\'弹琴\',0), (\'岳不群\',1,3,\'华山剑法\',0), (\'东方不败\',0,3,\'葵花宝典\',0), (\'胡斐\',1,4,\'胡家刀法\',0), (\'苗若兰\',0,4,\'黄衣\',0), (\'程灵素\',0,4,\'医术\',0), (\'袁紫衣\',0,4,\'六合拳\',0);
后台管理
本地化设置已经在settings.py设置好了
创建后台管理账号
python manage.py createsuperuser
运行项目 然后登陆后台
#python manage.py runserver 服务器IP:端口 python manage.py runserver 192.168.121.130:8000
登录后台查看
192.168.121.130:8000/admin
注册和登录
添加用户表
class AdminInfo(models.Model): # 管理员 user = models.CharField(max_length=20, unique=True) # 密码 password = models.CharField(max_length=128) # 是否删除 isDelete = models.BooleanField(default=False)
创建regist.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/js/jquery-1.12.4.min.js"></script> </head> <body> <form action="/regist_handle" method="post"> {% csrf_token %} <lable>账号:</lable> <input type="text" name="user"><br> <lable>密码:</lable> <input type="password" name="password"> <br> <input type="text" name="verify_code" placeholder="请输入验证码"> <img src="/verify_code" onclick="this.src=\'/verify_code?\'+ Math.random()" alt="" ><br> <-- 点击更换验证码 !--> <input type="submit" value="注册"> </form> </body> </html>
注册及验证码的视图函数views.py
def regist(request):
return render(request, \'booktest/regist.html\')
def regist_handle(request):
user = request.POST.get(\'user\') # POST或GET要大写
password = request.POST.get(\'password\')
verifycode = request.POST.get(\'verify_code\')
if verifycode != request.session[\'verifycode\']:
return HttpResponse(\'验证码不正确\')
u = AdminInfo()
u.user = user
u.password = password
u.save()
return redirect(\'/login\')
# 验证码
from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO
def verify_code(request):
#引入随机函数模块
import random
#定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(
20, 100), 255)
width = 100
height = 25
#创建画面对象
im = Image.new(\'RGB\', (width, height), bgcolor)
#创建画笔对象
draw = ImageDraw.Draw(im)
#调用画笔的point()函数绘制噪点
for i in range(0, 100):
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill=fill)
#定义验证码的备选值
str1 = \'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0\'
#随机选取4个值作为验证码
rand_str = \'\'
for i in range(0, 4):
rand_str += str1[random.randrange(0, len(str1))]
#构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont”
font = ImageFont.truetype(\'FreeMono.ttf\', 23)
#构造字体颜色
fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
#绘制4个字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
#释放画笔
del draw
#存入session,用于做进一步验证
request.session[\'verifycode\'] = rand_str
#内存文件操作
buf = BytesIO()
#将图片保存在内存中,文件类型为png
im.save(buf, \'png\')
#将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), \'image/png\')
注册路由urls.py
url(r\'regist$\', views.regist), url(r\'regist_handle$\', views.regist_handle), url(r\'verify_code\', views.verify_code),
登录页面login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login_check" method="post"> {% csrf_token %} <lable>账号:</lable> <input type="text" name="user"><br> <lable>密码:</lable> <input type="password" name="password"> <br> <input type="submit" value="登陆"> </form> </body> </html>
登录的视图函数views.py
# 登陆页面 def login(request): return render(request, \'booktest/login.html\') # 登陆校验 def login_check(request): user = request.POST.get(\'user\') password = request.POST.get(\'password\') userinfo = AdminInfo.objects.filter(user=user) # filter取的是一个questset集 要加get()才能取到值 如果不用filter()用get()取值的话 为空的时候会报错 if not userinfo: return HttpResponse("无数据") else: userinfo = userinfo.get() # QuestSet无法直接读取值 要用get()取值 if user==userinfo.user and password==userinfo.password: request.session[\'isLogin\'] = True request.session[\'user\'] = user return redirect(\'/index\') else: return redirect(\'/login\')
注册路由urls.py
url(r\'^login$\', views.login), url(r\'^login_check$\', views.login_check),
登录校验装饰器views.py
# 校验是否登录,如果没登录返回登录页面 def login_check(func): def wapper(request, *view_args, **view_kwarga): if not request.session.has_key(\'isLogin\'): return redirect(\'/login\') return func(request, *view_args, **view_kwarga) return wapper
上传图片
定义模型类models.py
class Uploads(models.Model): \'\'\'图片模型类\'\'\' pic = models.ImageField(upload_to=\'booktest\') # upload_to= 上传到哪个文件夹
设置路径settings.py
UPLOAD_ROOT = os.path.join(BASE_DIR, \'static/upload\') # 没有中括号
上传页面pic_upload.html
在模板中定义上传表单,要求如下:
- form的属性enctype="multipart/form-data"
- form的method为post
- input的类型为file
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="upload_handle" method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="pic"><br> <input type="submit" value="上传"> </form> </body> </html>
上传和处理的视图函数 views.py
# 上传页面 def pic_upload(request): return render(request, \'booktest/pic_upload.html\') # 上传处理 from django.conf import settings # 为了导入上传路径 def upload_handle(request): pic = request.FILES.get(\'pic\') # 图片用FILES.get() 接收 print(pic.name) p = Uploads() p.pic = \'booktest/%s\' % pic.name # 保存到数据库 p.save() pname = \'%s/booktest/%s\' % (settings.UPLOAD_ROOT, pic.name) with open(pname, \'wb\') as f: # 写入到指定文件夹 for content in pic.chunks(): # pic.chunks() 一段一段读取 f.write(content) return HttpResponse(\'ok\')
分页
Paginator类对象的属性:
|
属性名 |
说明 |
|
num_pages |
返回分页之后的总页数 |
|
page_range |
返回分页后页码的列表 |
Paginator类对象的方法:
|
方法名 |
说明 |
|
page(self, number) |
返回第number页的Page类实例对象 |
Page类对象的属性:
|
属性名 |
说明 |
|
number |
返回当前页的页码 |
|
object_list |
返回包含当前页的数据的查询集 |
|
paginator |
返回对应的Paginator类对象 |
Page类对象的方法:
|
属性名 |
说明 |
|
has_previous |
判断当前页是否有前一页 |
|
has_next |
判断当前页是否有下一页 |
|
previous_page_number |
返回前一页的页码 |
|
next_page_number |
返回下一页的页码 |
Paginator类实例对象
- 方法_init_(列表,int):返回分页对象,第一个参数为列表数据,第二个参数为每页数据的条数。
- 属性count:返回对象总数。
- 属性num_pages:返回页面总数。
- 属性page_range:返回页码列表,从1开始,例如[1, 2, 3, 4]。
- 方法page(m):返回Page类实例对象,表示第m页的数据,下标以1开始。
Page类实例对象
- 调用Paginator对象的page()方法返回Page对象,不需要手动构造。
- 属性object_list:返回当前页对象的列表。
- 属性number:返回当前是第几页,从1开始。
- 属性paginator:当前页对应的Paginator对象。
- 方法has_next():如果有下一页返回True。
- 方法has_previous():如果有上一页返回True。
- 方法len():返回当前页面对象的个数。
创建视图page_test.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>省</h1> <ul> {% for p in page %} <li>{{ p.atitle }}</li> {% endfor %} </ul> {#页码#} {#判断是否有上一页#} {% if page.has_previous %} <a href="/page_test/{{ page.previous_page_number }}">《上一页</a> {% endif %} {% for pindex in plist %} {# 判断当前页 撤销当前页a标签#} {% if pindex == page.number %} {{ pindex }} {% else %} <a href="/page_test/{{ pindex }}">{{ pindex }}</a> {% endif %} {% endfor %} {#判断是否有下一页#} {% if page.has_next %} <a href="/page_test/{{ page.next_page_number }}">下一页》</a> {% endif %} </body> </html>
分页的视图函数views.py
from django.core.paginator import Paginator # 分页需导入paginator包 def page_test(request, pindex): # 红字参数与路由要一致 # 查询所有省的名称 prov_list = AreaInfo.objects.filter(aParent__isnull=True) print(prov_list) # 分页 每页显示10条内容 provs = Paginator(prov_list,10) # 如果当前没有传递页码信息,则认为是第一页,这样写是为了请求第一页时可以不写页码 if pindex==\'\': pindex = 1 else: # 通过url匹配的参数都是字符串类型,转换成int类型 pindex = int(pindex) # 获取第pIndex页的数据 page = provs.page(pindex) # 获取所有的页码信息 plist = provs.page_range return render(request, \'booktest/page_test.html\', {\'page\':page, \'plist\':plist})
路由urls.py
url(r\'page_test/(?P<pindex>\d*)$\', views.page_test), # 红字部分要和views.py 中视图函数的形参保持一致
三级联动
创建视图areas.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>省市县选择案例</title> <script src="/static/js/jquery-1.12.4.min.js"></script> <script> $(function () { // 发起一个ajax请求 /prov,获取所有省级地区的信息 // 获取信息,使用get // 涉及到信息修改,使用post $.get(\'/prov\', function (data) { // 回调函数 // 获取返回的json数据 res = data.data // 获取prov下拉列表框 prov = $(\'#prov\') // 变量res数组,获取每一个元素:[地区id, 地区标题] /* for(i=0; i<res.length; i++){ id = res[i][0] atitle = res[i][1] option_str = \'<option value="\'+id + \'">\'+ atitle+ \'</option>\' // 向prov下拉列表框中追加元素 prov.append(option_str) }*/ $.each(res, function (index, item) { id = item[0] atitle = item[1] option_str = \'<option value="\'+id + \'">\'+ atitle+ \'</option>\' // 向prov下拉列表框中追加元素 prov.append(option_str) }) }) // 绑定prov下拉列表框的change事件,获取省下面的市的信息 $(\'#prov\').change(function () { // 发起一个ajax请求 /city,获取省下面市级地区的信息 // 获取点击省的id prov_id=$(this).val() $.get(\'/city\'+prov_id, function (data) { // 获取返回的json数据 res = data.data // 获取city下拉列表框 city = $(\'#city\') // 清空city下拉列表框 city.empty().append(\'<option>---请选择市---</option>\') // 获取dis下拉列表框 dis = $(\'#dis\') // 清空dis下拉列表框 dis.empty().append(\'<option>---请选择县---</option>\') // 变量res数组,获取每一个元素:[地区id, 地区标题] // 遍历取值添加到city下拉列表框中 $.each(res, function (index, item) { id = item[0] atitle = item[1] option_str = \'<option value="\'+id + \'">\'+ atitle+ \'</option>\' // 向city下拉列表框中追加元素 city.append(option_str) }) }) }) // 绑定city下拉列表框的change事件,获取市下面的县的信息 $(\'#city\').change(function () { // 发起一个ajax请求 /dis,获取市下面县级地区的信息 // 获取点击市的id city_id=$(this).val() $.get(\'/dis\'+city_id, function (data) { // 获取返回的json数据 res = data.data // 获取dis下拉列表框 dis = $(\'#dis\') // 清空dis下拉列表框 dis.empty().append(\'<option>---请选择县---</option>\') // 变量res数组,获取每一个元素:[地区id, 地区标题] // 遍历取值添加到dis下拉列表框中 $.each(res, function (index, item) { id = item[0] atitle = item[1] option_str = \'<option value="\'+id + \'">\'+ atitle+ \'</option>\' // 向dis下拉列表框中追加元素 dis.append(option_str) }) }) }) }) </script> </head> <body> <select id="prov"> <option>---请选择省---</option> </select> <select id="city"> <option>---请选择市---</option> </select> <select id="dis"> <option>---请选择县---</option> </select> </body> </html>
视图函数views.py
# /areas def areas(request): \'\'\'省市县选中案例\'\'\' return render(request, \'booktest/areas.html\') # /prov def prov(request): \'\'\'获取所有省级地区的信息\'\'\' # 1.获取所有省级地区的信息 areas = AreaInfo.objects.filter(aParent__isnull=True) # 2.变量areas并拼接出json数据:atitle id areas_list = [] for area in areas: areas_list.append((area.id, area.atitle)) # 3.返回数据 return JsonResponse({\'data\':areas_list}) def city(request, pid): \'\'\'获取pid的下级地区的信息\'\'\' # 1.获取pid对应地区的下级地区 # area = AreaInfo.objects.get(id=pid) # areas = area.areainfo_set.all() areas = AreaInfo.objects.filter(aParent__id=pid) # 2.变量areas并拼接出json数据:atitle id areas_list = [] for area in areas: areas_list.append((area.id, area.atitle)) # 3.返回数据 return JsonResponse({\'data\': areas_list})
路由配置urls.py
url(r\'^areas$\', views.areas), # 省市县选中案例 url(r\'^prov$\', views.prov), # 获取所有省级地区的信息 url(r\'^city(\d+)$\', views.city), # 获取省下面的市的信息 url(r\'^dis(\d+)$\', views.city), # 获取市下面的县的信息