一、基础
查看Django版本
...\> py -m django --version
1.创建项目
会在当前目录下创建一个mysite目录
...\> django-admin startproject mysite
目录结构如下:
mysite/ 根目录只是项目的容器,可以将它重命名为任何名字,没有影响
manage.py
mysite/ 包含你的项目,它是一个纯 Python 包
init.py
settings.py
urls.py
asgi.py
wsgi.py
2.创建应用
会在当前目录下创建一个应用
...\> py manage.py startapp polls
目录结构如下:
polls/
init.py
admin.py
apps.py
migrations/
init.py
models.py
tests.py
views.py
3.运行服务器
浏览器访问http://localhost:8000/…
...\> py manage.py runserver
#更换服务器监听端口
...\> py manage.py runserver 8080
#更换服务器监听IP,在端口之前输入新的(0 是 0.0.0.0 的简写)
...\> py manage.py runserver 0:8000
4.创建管理员账号
http://127.0.0.1:8000/admin/
...\> py manage.py createsuperuser
Username: admin
Email address: admin@example.com
#最后在输入密码
5.Admin页面
向Admin页面加入应用
#在polls的admin中注册
from django.contrib import admin
from .models import Question
admin.site.register(Question)
6.应用中的views和urls文件
**视图views必须要做的事:返回的是一个 HttpResponse ,或者抛出一个异常。**响应可以是一个 HTML 页面、一个 404 错误页面、重定向页面、XML 文档、或者一张图片…代码写在哪里都可以,只要在 Python 目录下面,一般放在项目的 views.py 文件中。
polls的views:
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
polls的urls:
rom django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
函数path():
参数route:route 是一个匹配 URL 的准则(类似正则表达式),这些准则不会匹配 GET 和 POST 参数或域名
参数view:当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个 HttpRequest 对象作为第一个参数
参数kwargs:任意个关键字参数可以作为一个字典传递给目标视图函数。
参数name:为你的 URL 取名能使你在 Django 的任意地方唯一地引用它
mysite的urls:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
函数 include() 允许引用其它 URLconfs。每当 Django 遇到 include() 时,它会截断与此项匹配的 URL 的部分,并将剩余的字符串发送到 URLconf 以供进一步处理。
urls.py中的path(),url(),re_path()
url是Django 1.x中写法,Django2.1中开始舍弃url的写法,引入path和re_path,其中re_path可看作url函数的替代。
主要的区别:
- 模块不同(引入文件不同)
- url参数表示不同
url
url参数规则:
- 需要开始符^和结尾符$
- 参数匹配一个 ()就是一个匹配参数
(?P<匹配的字段名>正则表达式)
- 进行匹配是不包括get或者post请求方式的参数及域名比如www.qq.com/blog?num=1并不会匹配?后边的字符
from django.conf.urls import url
urlpatterns=[
url(r'^page/$',views.page),
url(r'^page(?P<num>[0-9]+)$',views.page)
]
#views
def page(request,num='1'):
pass
path
url参数规则:
- 直接写路径,不用开始符号结束符号,不支持正则表达
- 参数匹配用尖括号比如<int:a>,尖括号内前边代表参数的类型,后面代表参数的名称。这个转换器也有许多类型:
- int 匹配0和正整数
- str 匹配任何空字符串但不包括/
- slug 可理解为注释 匹配任何ascii码包括连接线和下划线
- uuid 匹配一个uuid对象(该对象必须包括破折号—,所有字母必须小写)
- path 匹配所有的字符串 包括/(意思就是path前边和后边的所有)
from django.urls import path
re_path
当URL不能被路径和转换器语法定义时,就需要使用正则表达式,也就是说要用re_path了
具体语法之后再补充
from django.urls import re_path
正则表达式的使用
正则路径中的无名分组
无名分组按位置传参,一一对应。views 中除了 request,其他形参的数量要与 urls 中的分组数量一致。
#urls.py
urlpatterns = [
path('admin/', admin.site.urls),
re_path("^index/([0-9]{4})/$", views.index),
]
#views.py
from django.shortcuts import HttpResponse
def index(request,year):
print(year) # 一个形参代表路径中一个分组的内容,按顺序匹配
return HttpResponse('菜鸟教程')
正则路径中的有名分组
(?P<组名>正则表达式)
有名分组按关键字传参,与位置顺序无关。views 中除了 request,其他形参的数量要与 urls 中的分组数量一致, 并且 views 中的形参名称要与 urls 中的组名对应。
#urls.py
urlpatterns = [
path('admin/', admin.site.urls),
re_path("^index/(?P[0-9]{4})/(?P[0-9]{2})/$", views.index),
]
#views.py
from django.shortcuts import HttpResponse
def index(request, year, month):
print(year,month) # 一个形参代表路径中一个分组的内容,按关键字对应匹配
return HttpResponse('菜鸟教程')
name=“路由别名”
在urls.py文件中,path等函数的第三个参数可设置路由别名。当redirect重定向时reverse反向解析时特别有用,用别名就免去url修改之后要修改对应其他文件url的烦恼。
在模板 templates 中的 HTML 文件中,利用 {% url “路由别名” %} 反向解析。
<form action="{% url 'login' %}" method="post">
<form action="{% url 'login' 10 %}" method="post">
<form action="{% url 'login' year=3333 %}" method="post">
#普通路径path("login1/", views.login, name="login")
return redirect(reverse("login"))
#正则路径的无名分组re_path(r"^login/([0-9]{2})/$", views.login, name="login")
return redirect(reverse("login",args=(10,)))
#正则路径的有名分组re_path(r"^login/(?P<year>[0-9]{4})/$", views.login, name="login")
return redirect(reverse("login",kwargs={"year":3333}))
命名空间
路由别名 name 没有作用域,Django 在反向解析 URL 时,会在项目全局顺序搜索,当查找到第一个路由别名 name 指定 URL 时,立即返回。当在不同的 app 目录下的urls 中定义相同的路由别名 name 时,可能会导致 URL 反向解析错误。
所以可以使用命名空间解决问题,可以使用下面的include函数解决,或者在app的urls.py文件中加入
#polls/urls.py
app_name = 'polls'
include()函数
用法:如果需要在当前urls中调用其他urls时比较好用
作用:用于urlpatterns中剔除掉path前面匹配到的部分,将剩下的部分交给include中的urls处理
1. include(module, namespace=None)
- module:URLconf module (or module name)
#主项目下的urls.py
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
每当 Django 遇到 include() 时,它会截断前面匹配的 URL 的部分,并将剩余的字符串发送到inlcude中的URLconf 以供进一步处理。
2. include(pattern_list)
- pattern_list:Iterable of path() and/or re_path() instances 必须是一个可迭代的path() 或者 re_path() 清单
我这么理解的:也可以丢给同一个文件下的另一个匹配列表
extra_patterns = [
url(r'^reports/$', credit_views.report),
url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),
url(r'^charge/$', credit_views.charge),
]
urlpatterns = [
url(r'^$', main_views.homepage),
url(r'^help/', include('apps.help.urls')),
url(r'^credit/', include(extra_patterns)),
]
3. include((pattern_list, app_namespace), namespace=None)
#主项目下的urls.py
#include(("app名称.urls","app名称"))
path("app01/", include(("app01.urls","app01")))
#app01下的urls.py
path("login/", views.login, name="login")
#reverse("app名称:路由别名")
return redirect(reverse("app01:login")
#{% url "app名称:路由别名" %}
<form action="{% url 'app01:login' %}" method="post">
views从urls接收参数
views:
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
urlpatterns = [
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
]
detail(request=, question_id=34)
question_id=34 由 int:question_id 匹配生成。使用尖括号“捕获”这部分 URL,且以关键字参数的形式发送给视图函数。上述字符串的 :question_id> 部分定义了将被用于区分匹配模式的变量名,而 int: 则是一个转换器决定了应该以什么变量类型匹配这部分的 URL 路径。
抛出404错误和快捷函数
views.py中的render()
- 可用来代替HttpResponse
- 使用字典context作为参数{key:value}
- context中的键值对对应HTML文件中{{key}}替换的值
from django.shortcuts import render
def runoob(request):
context = {}
context['hello'] = 'Hello World!'
return render(request, 'runoob.html', context)
views.py中的HttpRequest对象
HttpRequest即函数接收的request参数
常用属性:
属性 | 描述 |
---|---|
path | 请求页面的全路径,不包括域名—例如, “/hello/”,数据类型是字符串。 |
method | 请求中使用的HTTP方法的字符串表示。全大写表示。例如:if request.method == ‘GET’: |
GET | 包含所有HTTP GET参数的类字典对象。参见QueryDict 文档。 |
POST | 包含所有HTTP POST参数的类字典对象。参见QueryDict 文档。 |
REQUEST | 为了方便,该属性是POST和GET属性的集合体,但是有特殊性,先查找POST属性,然后再查找GET属性。不建议使用。 |
COOKIES | 包含所有cookies的标准Python字典对象。Keys和values都是字符串。 |
body | 数据类型是二进制字节流,是原生请求体里的参数内容,在 HTTP 中用于 POST,因为 GET 没有请求体。在 HTTP 中不常用,而在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML、Json 等。 |
还有:FILES、META、user、session、raw_post_data
常用方法:_getitem_(key)、has_key()、get_full_path()、is_secure()
QueryDict数据类型:一个类似于字典的对象。在HttpRequest对象中,
GET和POST属性是django.http.QueryDict类的实例。
get():(具体可以查询QueryDict的方法)返回字符串,如果该键对应有多个值,取出该键的最后一个值。
def runoob(request):
name = request.GET.get("name")
return HttpResponse('姓名:{}'.format(name))
def runoob(request):
name = request.POST.get("name")
return HttpResponse('姓名:{}'.format(name))
views.py中的HttpResponse对象
响应对象主要有三种形式:HttpResponse()、render()、redirect()。它们返回的本质上也都是HttpResponse对象
- HttpResponse(): 返回文本,参数为字符串,字符串中写文本内容。如果参数为字符串里含有 html 标签,也可以渲染。
- render(): 返回文本,第一个参数为 request,第二个参数为字符串(页面名称),第三个参数为字典(可选参数,向页面传递的参数:键为页面参数名,值为views参数名)。
- redirect():重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。
def runoob(request):
# return HttpResponse("菜鸟教程")
return HttpResponse("<a href='http://https://www.runoob.com/>菜鸟教程</a>")
def runoob(request):
name ="菜鸟教程"
return render(request,"runoob.html",{"name":name})
def runoob(request):
return redirect("/index/")
二、数据库
1.数据库配置
先设置TIME_ZONE为自己的时区
在mysite/settings.py中配置数据库
#该命令检查 INSTALLED_APPS 设置,为其中的每个应用创建需要的数据表
...\> py manage.py migrate
- 编辑 models.py 文件,改变模型。
- 运行 python manage.py makemigrations 为模型的改变生成迁移文件。
- 运行 python manage.py migrate 来应用数据库迁移。
每次新建新的models,都要重复2、3步,不能直接第3步
2.Django ORM
ORM,对象关系映射,是通过使用描述对象和数据库之间的映射的元数据,将程序中的对象自动持久化到数据库中。
可以这样理解,ORM 会将 Python 代码转成为 SQL 语句,SQL 语句通过桥梁传送到数据库服务端,在数据库中执行 SQL 语句并将结果返回。
3.模型models
Django规定,如果要使用模型,必须要创建一个app。
创造model/表结构
- 类需继承自models.Model
- 类名代表数据库表名
- 没有在models设置主键,但是 Django 会自动添加一个 id 作为主键。
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)
CharField
为字符串类型,必须有max_length
设置最大长度(max_length在postgreSQL下亲自实践,不是字节数,而是字符数,10个数字,10个汉字,10个英文占位一模一样,混合也一样,在网上查过mysql数据库也好像也是一样的),verbose_name
在django后台显示该英文字段的中文意思IntegerField
整型,在postgreSQL下亲自实践4个字节大小,也就是最大存储2^32-1的数,用来存秒数的话可以到68年的样子DecimalField
浮点型,max_digits=5表示整数部分和小数位数之和不大于5,decimal_places
表示小数的最大位数,null=True
表示字段可以为空,blank=True
表示在admin后台中该数据栏可以为空EmailField
该字段必须符合邮箱格式TextField
字段位文本类型,长度没有限制DateTimeField
为日期类型,auto_now_add=True
该条数据创建的时间,数据更新时,时间数值不变auto_now=True
该条数据创建的时间,数据更新时(即每次save时),时间数值也会改变。无法手动赋值,在admin管理器中是只读数据ForeignKey
多外键,第一个参数位你要关联的数据表,比如一个班级有多个学生,就属于一对多,外键要放到"多"的那张表,related_name是对外键取别名,常用在django的orm反向查询中
更多Field类型,官网在Documentation页面搜索“field”
Post’g’re’SQL特有的Field,官网
更多有关数据库构造时函数的使用
objects数据库查询常用语句,还挺全的
外键
外键的创建
多对一:在models中创建外键时需要使用到类class ForeignKey(to,on_delete,**options)
多对多:MangToManyField
- 第一个参数是引用的是哪个模型(如果是自己,直接写’self’,如果是别的包的模型,引入那个包写就行了)
- 第二个参数是在使用外键引用的模型数据被删除了,这个字段该如何处理,比如有CASCADE、SET_NULL等
CASCADE
:级联操作。如果外键对应的那条数据被删除了,那么这条数据也会被删除。PROTECT
:受保护。即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。如果我们强行删除,Django就会报错。SET_NULL
:设置为空。如果外键的那条数据被删除了,那么在本条数据上就将这个字段设置为空。如果设置这个选项,前提是要指定这个字段可以为空。SET_DEFAULT
:设置默认值。如果外键的那条数据被删除了,那么本条数据上就将这个字段设置为默认值。如果设置这个选项,== 前提是要指定这个字段一个默认值 ==。SET()
:如果外键的那条数据被删除了。那么将会获取SET函数中的值来作为这个外键的值。SET函数可以接收一个可以调用的对象(比如函数或者方法),如果是可以调用的对象,那么会将这个对象调用后的结果作为值返回回去。== 可以不用指定默认值 ==DO_NOTHING
:不采取任何行为。一切全看数据库级别的约束。
一对多ForeignKey
from django.db import models
#一个类下可以有多篇文章,但一篇文章只能对应一个类
class Category(models.Model):
name = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 是由Category影响Article
category = models.ForeignKey('Category',on_delete=models.CASCADE)
多对多MangToManyField
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
#一本书可以有多个作者,一个作者可以写多本书
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
外键的使用
一对一OneToOneField = ForeignKey(…,unique=True)
一对一的反向查询用小写类名即可,不用加_set
一对多ForeignKey
article = Article.objects.first()
#可以直接得到相关的数据模型对象
article.category
article.category.name
#通过Category对象反向追溯article对象
category = Category.objects.first()
#属性名称article_set是由模型名称的小写(如Article)加_set组成的。
#在ForeignKey函数中有一个参数related_name,如果设置了这个参数就可以不用_set的形式了,可以直接用related_name的值,这一点同样适用于ManyToManyField函数。
#article_set只是一个 QuerySet,所以它可以像QuerySet一样,能实现数据过滤和分切
category.article_set.all()
category.article_set.filter(title__icontains='django')
多对多ManyToManyField
虽然两者间关系是一样的,但根据定义是ManyToMany写在谁那里还是有正向和反向的关系的。
b = Book.objects.get(id=50)
b.authors.all()
#反向查询
a = Author.objects.get(first_name='Adrian',last_name='Holovaty')
a.book_set.all()
添加数据
添加数据需要先创建对象,然后再执行 save 函数
#方法一:模型类实例化对象
question = Question(question_text = '.....')
......
question.save()
#方法二(推荐):通过 ORM 提供的 objects 提供的方法 create 来实现
question = Question.objects.create(question_text = '.....')
获取/查询数据
def testdb(request):
# 初始化
response = ""
response1 = ""
# 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM
# 返回 QuerySet 类型数据
list = Test.objects.all()
# filter相当于SQL中的WHERE,可设置条件过滤结果
# 返回 QuerySet 类型数据
response2 = Test.objects.filter(id=1)
#response2 = Test.objects.filter(id=1,price = 300)
# exclude() 方法用于查询不符合条件的数据,用法跟filter一样
# 获取单个对象,如果符合筛选条件的对象超过了一个或者没有一个都会抛出错误。
response3 = Test.objects.get(id=1)
# 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
# 但不能用负数,没有逆序索引
Test.objects.order_by('name')[0:2]
#数据排序
Test.objects.order_by("id")
Test.objects.order_by("id").reverse() #对查询结果反转
# 上面的方法可以连锁使用
Test.objects.filter(name="runoob").order_by("id")
# count(),first(),last()方法都能直接加在末尾
# 输出所有数据
for var in list:
response1 += var.name + " "
response = response1
return HttpResponse("<p>" + response + "</p>")
数据库查询的函数语法
菜鸟教程——包含筛选下划线的使用,比上面的更全面
更新数据
修改数据可以使用 save() 或 update()
def testdb(request):
# 修改其中一个id=1的name字段,再save,相当于SQL中的UPDATE
# 对模型类的对象进行更新
test1 = Test.objects.get(id=1)
test1.name = 'Google'
test1.save()
# 另外一种方式,对QuerySet类型数据进行更新
# 返回值:受影响的行数(整数)
Test.objects.filter(id=1).update(name='Google')
# 修改所有的列
Test.objects.all().update(name='Google')
return HttpResponse("<p>修改成功</p>")
删除数据
删除数据库中的对象只需调用该对象的delete()方法即可
# 数据库操作
def testdb(request):
# 删除id=1的数据
test1 = Test.objects.get(id=1)
test1.delete()
# 另外一种方式
Test.objects.filter(id=1).delete()
# 删除所有数据
Test.objects.all().delete()
return HttpResponse("<p>删除成功</p>")