Django 路由、视图与ORM
路由urls.py
视图views.py
Django模板
ORM 一对多、多对多操作
练习:书籍管理系统
一、路由系统
URLconf配置
from django.conf.urls import url #1.11语法 urlpatterns = [ url(正则表达式, views视图函数, 参数, 别名), ] #2.0语法 from django.urls import path urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
根据不同app分发不同url
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^web/', include('web.urls')), ] #这条关系的意思是将url为”web/“的请求都交给web下的urls去处理 #其次,在web下创建一个urls.py文件,用来处理请求的url,使之与views建立映射 from django.conf.urls import url,include from web.views import home urlpatterns = [ url(r'^index/', home.index), ]
命名url和url反向解析
优点:防止将url硬编码到我们的业务逻辑代码中,给url起别名
通过别名,反向找到 url
#urls.py中的配置 url(r'login/',views.login,name="logind"), #login.html中配置 <form class="form-horizontal" action="{% url 'logind' %}" method="POST">
二、Django的视图Views
FBV:基于函数的view
CBV:基于类的view
#FBV版本 def login(request): url = reverse('logind') #返回的是url /login/ if request.POST: name_inp = request.POST.get('login_username') passwd_inp = request.POST.get("login_password") if name_inp == "alex" and passwd_inp == "alexdsb": return redirect('/user_list/') return render(request,'login.html') #CBV版本,前提先引入views包 from django import views class Login(views.View): def get(self,request): return render(request, 'login.html') def post(self,request): name_inp = request.POST.get('login_username') passwd_inp = request.POST.get("login_password") if name_inp == "alex" and passwd_inp == "alexdsb": return redirect('/user_list/') #注意:在使用CBV时,urls.py中url需改动 #urls.py views.Login.as_view() url(r'^login/',views.Login.as_view(),name="logind"),
练习:Django上传文件小程序
#编辑urls.py url(r'^upload/$',view.Upload.as_view()), #编辑views.py from django from views class Upload(views.View): def get(self,request): return render(request,'upload.html') #编辑upload.html <form action="" method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="code"> <input type="submit" value="提交"> </form> #继续编辑views.py def post(self,request): #获取用户上传的文件数据 file_obj = request.FILES.get('code') #拿到用户上传文件的文件名 filename = file_obj.name #在服务端创建一个同名的文件 with open(filename,'wb') as f: for i in file_obj.chunks(): f.write(i) return HttpResponse('上传成功')
给视图加装饰器
使用装饰器装饰FBV
#FBV本身就是一个函数,所以和普通的函数加装饰器一样 def wrapper(func): def inner(*args, **kwargs): start_time = time.time() ret = func(*args, **kwargs) end_time = time.time() print("used:", end_time-start_time) return ret return inner # FBV版添加班级 @wrapper def add_class(request): if request.method == "POST": class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/") return render(request, "add_class.html")
使用装饰器装饰CBV
#Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。 # CBV版添加班级 from django.views import View from django.utils.decorators import method_decorator class AddClass(View): @method_decorator(wrapper) def get(self, request): return render(request, "add_class.html") def post(self, request): class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/")
JsonResponse
#专门用来返回JSON格式数据的响应对象 from django.http import JsonResponse class JsonTest(views.View): def get(self,request): res = {'code':0,'data':'alex'} return JsonResponse(res)
三、Django模板系统
变量相关用{{}}
逻辑相关用{% %}
变量
{{ 变量名 }} 变量名由字母数字和下划线组成 点(.)在模板语言中有特殊含义,用来获取对象相应属性值 模板中支持的写法: {# 取l中的第一个参数 #} {{ l.0 }} {# 取字典中key的值 #} {{ d.name }} {# 取对象的name属性 #} {{ person_list.0.name }} {# .操作只能调用不带参数的方法 #} {{ person_list.0.dream }}
Filters
语法:{{ value|filter_name:参数}}
#default
{{ value|default:"nothing"}}
如果value值为空显示nothing
#length
{{ value|length}}
返回value的长度,注意 '|' 左右没有空格
#filesizeformat
{{ value|filesizeformat}}
将值格式化一个'人类可读的'文件尺寸,KB, MB, GB等
#slice
{{ value|slice:"2:-1"}}
切片
#date
{{ value|date:"Y-m-d H:i:s"}}
#safe
value = "<a href='#'>点我</a>"
{{ value|safe}}
for循环
#for循环可用的参数 forloop.counter 当前循环的索引值(从1开始) forloop.counter0 当前循环的索引值(从0开始) forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始) forloop.first 当前循环是不是第一次循环(布尔值) forloop.last 当前循环是不是最后一次循环(布尔值) forloop.parentloop 本层循环的外层循环
if,elis
{% if user_list %}
用户人数:{{ user_list|length }}
{% elif black_list %}
黑名单数:{{ black_list|length }}
{% else %}
没有用户
{% endif %}
#if和else
{% if user_list|length > 5 %}
七座豪华SUV
{% else %}
黄包车
{% endif %}
母板
母板的优点:不同页面有大量重复的代码,可用把公用的部分拿出来放在单独的一个文件中
母板的使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ html_title }}</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3" style="margin-top: 70px"> {% block page-main %} {% endblock %} </div> </div> </div> </body> </html>
{% extends 'mother.html' %}
{% block page-main %}
<form action="" method="POST">
{% csrf_token %}
<input type="text" name="id" value="{{ data.id }}" style="display: none">
<input type="text" name="username" placeholder="{{ data.username }}" style="display: none">
<input type="password" name="password" placeholder="{{ data.password }}" style="width: 300px">
<button class="btn btn-info btn-sm">修改密码</button>
</form>
{% endblock %}
#总结 #母板中使用 {% block xx %}{% endblock %} #子页面使用 #一定要放在子页面的最上方 {% extends 模板名 %} {% block xx %} html代码 {% endblock %}
组件
可用将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方按如下语法导入即可
{% include 'navbar.html' %}
#这是将导航条代码写入navbar.html中,在其他页面引入
csrf_token
该标签用于跨站请求伪造保护
在页面的form表单里写{% csrf_token %}
四、ORM
#ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的胡不匹配的现象的技术 简单说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中 ORM在业务逻辑层和数据库层之间充当了桥梁作用 #ORM优势 ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段 ORM提供了对数据库的映射,不用直接编写SQL代码,只需像操作对象一样从数据库操作数据 #ORM劣势 ORM缺点是会在一定程度上牺牲程序的执行效率 ORM用多了SQL语句就会退化
Django项目使用Mysqsl数据库
1、在Django项目的settings.py文件中,配置数据库连接信息:
DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": "你的数据库名称", # 需要自己手动创建数据库 "USER": "数据库用户名", "PASSWORD": "数据库密码", "HOST": "数据库IP", "POST": 3306 } }
2、在Django项目的__init__.py文件中写如下代码,告诉Django使用pymsql模块连接Mysql数据库
import pymysql pymsql.install_as_MySQLdb()
ORM常用参数和字段
常用字段
AutoField #int自增列,必须填入参数primary_key=True。当model中没有自增列,会自动创建一个列名为id的列 IntegerField #一个整数类型,范围-2147483648 to 2147483647 CharField #字符类型,必须提供max_length参数,表示字符长度 DateField #日期字段,日期格式 YYYY-MM-DD DateTimeField #日期时间字段格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): # 自动创建一个列名为id的且为自增的整数列 username = models.CharField(max_length=32) class Group(models.Model): # 自定义自增列 nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) SmallIntegerField(IntegerField): - 小整数 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正小整数 0 ~ 32767 IntegerField(Field) - 整数列(有符号的) -2147483648 ~ 2147483647 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正整数 0 ~ 2147483647 BigIntegerField(IntegerField): - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 BooleanField(Field) - 布尔值类型 NullBooleanField(Field): - 可以为空的布尔值 CharField(Field) - 字符类型 - 必须提供max_length参数, max_length表示字符长度 TextField(Field) - 文本类型 EmailField(CharField): - 字符串类型,Django Admin以及ModelForm中提供验证机制 IPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 GenericIPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 - 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both" URLField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField(CharField) - 字符串类型,格式必须为逗号分割的数字 UUIDField(Field) - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 - 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 FileField(Field) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField(FileField) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串) DateTimeField(DateField) - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DD TimeField(DateTimeCheckMixin, Field) - 时间格式 HH:MM[:ss[.uuuuuu]] DurationField(Field) - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 FloatField(Field) - 浮点型 DecimalField(Field) - 10进制小数 - 参数: max_digits,小数总长度 decimal_places,小数位长度 BinaryField(Field) - 二进制类型 字段合集
字段参数
null #表示某个字段可为空 unique #如果设置unique=True,该字段在表中必须是唯一 db_index #如果db_index=True表示此字段设置索引 default #该字段设置默认值
DateField 和 DateTimeField
auto_now_add #配置auto_now_add=True,创建数据时把当前时间添加到数据库 auto_now #配置auto_now=True,每次更新数据记录时更新该字段
关系字段 ForeignKey
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在'一对多'中'多'的一方
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系
字段参数
to #设置要关联的表 to_field #设置要关联的表字段 related_name #反向操作,使用的字段名,用于代替原反向查询时的'表名_set'
on_delete
#当删除关联表中的数据时,当前表与其关联的行的行为 models.CASCADE #删除关联数据,与之关联也删除 models.DO_NOTHING #删除关联数据,引发错误IntegrityError models.SET_NULL #删除关联数据,与之关联的值设置为null models.SET_DEFAULT #删除关联数据,与之关联的值设置为默认值 models.SET #删除关联数据 #与之关联的值设置为指定值,设置:models.SET(值) #与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
ORM单表操作必会13条
all() #查询所有结果 filter() #包含了筛选条件所匹配的对象 get() #获取一个唯一的值,不存在则抛出错误 exclude() #与filter相反,留下不符合条件的 values('字段名',...) #返回一个QuerySet,字典格式 values_list('字段名',...) #返回一个QuerySet,元祖格式 order_by() #对查询结果排序 reverse() #对一个有序查询结果集做反转,有序 distinct() #去重,跨表查询时去掉重复记录 count() #返回数据条数 first() #取第一个数据 last() #取最后一条数据 exists() #判断表里有没有数据
返回QuerySet对象的方法
all()
filter()
exclude()
order_by()
reverse()
distinct()
特殊的QuerySet
values() #返回一个可迭代的字典序列 values_list() #返回一个可迭代的元祖序列
返回具体对象的
get()
first()
last()
返回布尔值的
exists()
返回数字的
count()
单表查询之神奇的双下划线
models.Tb1.objects.filter(id__lt=10,id__gt=1) #获取id大于1,小于10的值 models.Tb1.objects.filter(id__in=[11,22,33]) #获取id等于11、22、33的数据 models.Tb1.objects.exclude(id__in=[11,22,33]) #not in models.Tb1.objects.filter(name__contains="ven") #获取name字段包含"ven"的 models.Tb1.objects.filter(name__icontains="ven") #icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1,3]) #id范围是1到3,类似sql的between and 类似还有:startswith,istartswith,endswith,iendswith date字段还可以: models.Tb1.objects.filter(first_day__year=2017)
ForeignKey操作
正向查找
语法: 对象.关联字段.字段 示例: book_obj = models.Book.objects.first() #第一本书对象 print(book_obj.publish) #得到这本书关联的出版社对象 print(book_obj.publish.name) #得到出版社对象的名称
语法: 关联字段__字段 示例: print(models.Book.objects.values("publish__name"))
反向查找
语法: obj.表名_set 示例: obj = models.Publish.objects.first() # 找到第一个出版社对象 books = obj.book_set.all() #找到第一个出版社出版的所有书 ret = books.values("title") #找到第一个出版社出版的所有书的书名
语法: 表名__字段 示例: titles = models.Publish.objects.values("book__title")
ManyToManyField
关联管理器,是在一对多或多对多的关联上下文中使用的管理器 它存在于下面两种情况: 1、外键关系的反向查询 2、多对多关联关系 简单说就是当 点后面的对象 可能存在多个的时候就可以使用以下方法
方法
# create() 创建一个新的对象,保存对象,并将它添加到关联对象中,返回新创建的对象 models.Author.object.first().book_set.create(title="番茄物语") # add() 把指定的model对象添加到关联对象集中 # set() 更新model对象的关联对象 book_obj = models.Book.objects.first() book_obj.authors.set([2,3]) # remove() 从关联对象集中移除执行的model对象 # clear() 从关联对象集中移除一切对象 #注意: #对于ForeignKey对象,clear()和remove()方法仅在null=True时存在 #所有类型的关联字段,add()、create()、remove()和clear()、set()都会马上更新数据库,换句话说,在关联的任何一端,不需要再使用save()方法
用于表示多对多的关联关系。在数据库中通过第三张表来简历关联关系 #字段参数 to #设置要关联的表 related_name #同ForeignKey字段 related_query_name #同ForeignKey字段 symmetrical #仅用于多对多自关联时,指定内部是否创建反向操作的字段,默认True
through #使用ManyToManyField字段时,可以手动创建第三张表来管理多对多关系,就需要用through指定第三张表名
through_fields #设置关联的字段
db_table #默认创建第三张表时,数据库中表的名称
多对多关联关系的三种方式:
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") # 自己创建第三张表,分别通过外键关联书和作者 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 通过ORM自带的ManyToManyField自动创建第三张表 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", related_name="authors")
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 自己创建第三张表,并通过ManyToManyField指定关联 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book")) # through_fields接受一个2元组('field1','field2'): # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
注意:
当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式。
但是当我们使用第三种方式创建多对多关联关系时,就无法使用set、add、remove、clear方法来管理多对多的关系了,需要通过第三张表的model来管理多对多关系。
多对多关联关系的使用
# 出版社 class Publisher(models.Model): name = models.CharField(max_length=32) # 书籍表 class Book(models.Model): title = models.CharField(max_length=32) publisher = models.ForeignKey(to="Publisher",on_delete=models.CASCADE) # 作者表 class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField(to="Book",related_name="authors")
# 多对多的正向查询 author_obj = models.Author.objects.first() ret = author_obj.books.all() print(ret) # 多对多的反向查询 book_obj = models.Book.objects.first() #默认是按照表名(全小写) _set.all() #ret = book_obj.author_set.all() #注意:当设置了related_name属性时 #ret = book_obj.authors.all() print(ret)
obj = models.Application.objects.get(id=1) obj.name # 添加 obj.books.add(1) obj.books.add(2) obj.books.add(2,3,4) obj.books.add(*[1,2,3,4]) # 删除 obj.books.remove(1) obj.books.remove(2,4) # 清除obj所对应的所有主机 obj.books.clear() # 设置,可以理解为删除原来的,设置成新的 obj.books.set([3,5,7]) # 所有相关的主机对象"列表" obj.books.all() #查询第一个作者写过的书名 # 基于对象的查询 ret = models.Author.objects.first().books.all().values("title") print(ret) # 基于QuerySet的双下划线查询 ret = models.Author.objects.filter(id=1).values("books__title") print(ret) # 基于QuerySet的双下划线的反向查询 # 由书找作者 ret = models.Book.objects.filter(id=1).values("authors__name") print(ret)
练习题
写一个书籍管理系统,要求实现 增删改查功能
1 url(r'^book_list/',views.book_list), 2 url(r'^edit_book/(?P<edit_id>\d+)/$',views.EditBook.as_view()), 3 url(r'^del_book/(?P<edit_id>\d+)/$',views.del_book), 4 url(r'^add_book',views.AddBook.as_view()),
1 def book_list(request): 2 # 去数据库查询所有书籍 3 data = models.Book.objects.all() 4 5 return render(request,'book_list.html',{'data':data})
1 {% extends 'mother.html' %} 2 3 {% block page-main %} 4 <table class="table table-bordered"> 5 <thead> 6 <tr> 7 <th>#</th> 8 <th>书名</th> 9 <th>出版社</th> 10 <th>操作</th> 11 </tr> 12 </thead> 13 <tbody> 14 {% for book in data %} 15 <tr> 16 <td>{{ forloop.counter }}</td> 17 <td>{{ book.title }}</td> 18 <td>{{ book.publisher.name }}</td> 19 <td> 20 <a href="/edit_book/{{ book.id }}">编辑</a> 21 <a href="/del_book/{{ book.id }}">删除</a> 22 </td> 23 </tr> 24 {% endfor %} 25 {# <input type="submit" value="添加">#} 26 27 </tbody> 28 <tfoot> 29 <a href="/add_book/" class="btn btn-info">添加书名</a> 30 </tfoot> 31 </table> 32 {% endblock %}
1 class AddBook(views.View): 2 def get(self,request): 3 publisher_list = models.Publisher.objects.all() 4 return render(request,'add_book.html',{'publisher_list':publisher_list}) 5 def post(self,request): 6 book_title = request.POST.get('name') 7 publisher = request.POST.get('publisher') 8 models.Book.objects.create(title=book_title,publisher_id=publisher) 9 return redirect('/book_list/')
1 {% extends 'mother.html' %} 2 3 {% block page-main %} 4 <form action="" method="post"> 5 {% csrf_token %} 6 <input type="text" name="name"> 7 <select name="publisher"> 8 {% for publisher in publisher_list %} 9 <option value="{{ publisher.id }}">{{ publisher.name }}</option> 10 {% endfor %} 11 </select> 12 <input type="submit" value="提交"> 13 </form> 14 {% endblock %}
1 class EditBook(views.View): 2 def get(self,request,edit_id): 3 book_obj = models.Book.objects.get(id=edit_id) 4 publisher_list = models.Publisher.objects.all() 5 return render(request,'edit_book.html',{'book':book_obj,'publisher_list':publisher_list}) 6 def post(self,request,edit_id): 7 book_obj = models.Book.objects.get(id=edit_id) 8 new_title = request.POST.get("name") 9 new_publisher_id = request.POST.get("publisher") 10 #更新 11 book_obj.title = new_title 12 book_obj.publisher_id = new_publisher_id 13 book_obj.save() 14 return redirect('/book_list/')
1 {% extends 'mother.html' %} 2 3 {% block page-main %} 4 <form action="" method="post"> 5 {% csrf_token %} 6 <input type="text" name="name" value="{{ book.title }}"> 7 <select name="publisher"> 8 {% for publisher in publisher_list %} 9 {% if book.publisher == publisher %} 10 <option selected value="{{ publisher.id }}">{{ publisher.name }}</option> 11 {% else %} 12 <option value="{{ publisher.id }}">{{ publisher.name }}</option> 13 {% endif %} 14 {% endfor %} 15 </select> 16 <input type="submit" value="提交"> 17 18 </form> 19 {% endblock %}
1 def del_book(request,edit_id): 2 models.Book.objects.get(id=edit_id).delete() 3 return redirect('/book_list/')
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>{{ html_title }}</title> 6 <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> 7 </head> 8 <body> 9 <div class="container"> 10 <div class="row"> 11 <div class="col-md-6 col-md-offset-3" style="margin-top: 70px"> 12 13 {% block page-main %} 14 15 {% endblock %} 16 </div> 17 </div> 18 </div> 19 20 </body> 21 </html>

浙公网安备 33010602011771号