Django一页通
官网:https://www.djangoproject.com/,中文官方教程:https://docs.djangoproject.com/zh-hans/3.1/
安装: pip install django
查看版本: django-admin --version 或: 1 import django 2 django.get_version()
创建工程: django-admin startproject 工程名
启动服务: python manage.py runserver
服务启动成功后浏览器访问:http://127.0.0.1:8000/
启动服务指定端口: python manage.py runserver 8001
启动服务指定IP:端口: python manage.py runserver 127.0.0.1:8002 (不能只指定IP不指定端口)
让所有人都可访问: python manage.py runserver 0.0.0.0:8000 同时需要设置settings.py中的代码 ALLOWED_HOSTS = ['*']
创建应用: python manage.py startapp myapp 或 django-admin startapp appname
访问后台管理:http://localhost:8000/admin
生成迁移: python manage.py makemigrations ,指定应用生成迁移 python manage.py makemigrations myapp
执行迁移: python manage.py migrate
第一个演示Demo:
myapp/views.py
from django.http import HttpResponse def hello(request): return HttpResponse('Hello')
也能识别HTML代码: return HttpResponse('<h1>Hello</h1>')
mypro/urls.py
from django.contrib import admin from django.urls import path from myapp import views urlpatterns = [ path('admin/', admin.site.urls), path('hello/',views.hello) ]
启动服务后浏览器访问:http://localhost:8000/hello/
优化上面的Demo:
myapp/views.py不变。
新增:myapp/urls.py:
from django.urls import path from myapp import views urlpatterns = [ path('hello/',views.hello) ]
修改:mypro/urls.py:
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('myapp/',include('myapp.urls')) ]
根路由已在mypro/settings.py中默认配置好: ROOT_URLCONF = 'mypro.urls'
浏览器访问:http://localhost:8000/myapp/hello/
创建模型:表名自动为应用名_模型名,数据库中生成的表名为 myapp_person
from django.db import models class Person(models.Model): name=models.CharField(max_length=32) # CharField必须指定max_length,否则生成迁移文件时就会报错 age=models.IntegerField(default=18) # IntegerField必须指定default,否则,新增模型正常,添加数据时报错;修改模型迁移时会阻塞并提示两种指定default值的方式
创建模型:指定表名,数据库中生成的表名为person
查询排序,方式一
from django.db import models class Person(models.Model): name = models.CharField(max_length=32) age=models.IntegerField(default=18) class Meta: db_table = 'person' # 指定表名 ordering='age','-name' # 指定查询时的排序规则,默认使用id排序,倒序在字段前加-,用元组或列表指定1到多个排序字段
生成迁移文件:
(venv) D:\桌面\mypro>python manage.py makemigrations
No changes detected
原因:未注册应用,需要在mypro/mypro/settings.py中注册 INSTALLED_APPS = ['myapp'] 或 INSTALLED_APPS = ['myapp.apps.MyappConfig']
再次生成迁移文件:
(venv) D:\桌面\mypro>python manage.py makemigrations Migrations for 'myapp': myapp\migrations\0001_initial.py - Create model Person
执行迁移:完成后会在数据库迁移记录表中产品相应的迁移记录,并创建对应模型的表。
(venv) D:\桌面\mypro>python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, myapp, sessions Running migrations: Applying myapp.0001_initial... OK
若对模型有修改,可再次执行生成迁移文件和执行迁移。
若删除迁移文件,则一定要同时删除表中迁移记录,及删除对应模型的表。
migrations包勿删!
使用模板,在myapp目录中建立模板文件夹templates,注:不是template
myapp/views.py
渲染方式一
from django.shortcuts import render def hello(request): return render(request,'hello.html')
渲染方式二
from django.http import HttpResponse from django.template import loader def hello(request): template=loader.get_template('hello.html') return HttpResponse(template.render())
myapp/templates/hello.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>标题一</h1> </body> </html>
使用模板,在工程目录mypro中建立template或templates文件夹,并在settings.py中注册。(推荐方式)
视图函数myapp/views.py同上不用改变。
mypro/template/hello.html或mypro/templates/hello.html文件内容同上。
mypro/mypro/settings.py中注册:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'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', ], }, }, ]
数据库的增删改查
增,方式一
mypro/myapp/views.py
from django.http import HttpResponse from myapp.models import Person def add(request): person=Person() person.name='李四' person.save() return HttpResponse(f'新增成功,{person.name}')
增,方式二
def addperson(request): Person.objects.create(name='李四',age=22) # 关键代码 return HttpResponse('添加成功')
删
mypro/myapp/views.py
from django.http import HttpResponse from myapp.models import Person def delete(request): person=Person.objects.first() person.delete() return HttpResponse(f'删除成功,{person.name}')
改
mypro/myapp/views.py
from django.http import HttpResponse from myapp.models import Person def update(request): person=Person.objects.first() person.name='王五' person.save() return HttpResponse(f'更新成功,{person.name}')
查
mypro/myapp/views.py
from django.shortcuts import render from myapp.models import Person def query(request): persons=Person.objects.all() context={ 'persons':persons } return render(request,'myapp/query.html',context=context)
mypro/templates/myapp/query.html,用到了模板语法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for person in persons %} <li>{{ person.name }}</li> {% endfor %} </ul> </body> </html>
查询过滤:filter和exclude可任意多个级联
过滤条件:__gt=、__gte=、__lt=、__lte=、__year=,其他month、day、week_day、hour、minute、second
def getperson(request): persons=Person.objects.filter(age__gt=20).exclude(age__gt=30) context={ 'persons':persons } return render(request,'hello.html',context=context)
= 同 __exact= 大小写敏感 persons=Person.objects.filter(name='Tom') 同 persons=Person.objects.filter(name__exact='Tom')
__iexact 忽略大小写 persons=Person.objects.filter(name__iexact='Tom') i为ignore在sqlite3中以下3个有无i都一样:
__contains同 __icontains忽略大小写 persons=Person.objects.filter(name__contains='t') 同 persons=Person.objects.filter(name__icontains='Tom')
__startswith同 __istartswith忽略大小写 persons=Person.objects.filter(name__startswith='t')同 persons=Person.objects.filter(name__istartswith='t')
__endswith同__iendswith忽略大小写persons=Person.objects.filter(name__endswith='m') 同 persons=Person.objects.filter(name__iendswith='m')
__isnull persons=Person.objects.filter(name__isnull=False) __isnull=True未试验出效果。无__isnotnull和__notisnull
__in大小写敏感 persons=Person.objects.filter(name__in=['无名氏','Tom'])
__range persons=Person.objects.filter(age__range=(30,50))
查询排序,方式二
def getperson(request): persons=Person.objects.order_by('name') context={ 'persons':persons } return render(request,'hello.html',context=context)
默认管理页面为英文,改为中文:mypro/mypro/settings.py LANGUAGE_CODE = 'zh-hans' ,再访问:http://127.0.0.1:8000/admin就显示中文。
默认时区为 TIME_ZONE = 'UTC' 改为 TIME_ZONE = 'Asia/Shanghai'
自定义创建对象的类方法:
from django.db import models class Person(models.Model): name = models.CharField(max_length=32) age=models.IntegerField(default=18) @classmethod def create(cls,name='无名氏',age=28): person=cls() person.name=name person.age=age person.save() class Meta: db_table = 'person' ordering='age','-name'
创建对象
增,方式三
def addperson(request): Person.create() Person.create(name='小猪') Person.create(age=29) Person.create(name='小狗',age=21) return HttpResponse('添加成功')
将查询结果集中的对象转化为字典,values()
def getperson(request): persons=Person.objects.order_by('name') print('-----------------------') print(type(persons)) print('-----------------------') print(persons) print('-----------------------') print(persons.values()) print('-----------------------') print(list(persons.values())) # 关键代码 print('-----------------------') context={ 'persons':persons } return render(request,'hello.html',context=context)
查询单个数据
get()
def getperson(request): # 多个会报MultipleObjectsReturned # 未查询到数据会报DoesNotExist person=Person.objects.get(name='李四') print('-----------------------') print(type(person)) # <class 'myapp.models.Person'> print('-----------------------') print(person) # Person object (301) print('-----------------------') return HttpResponse(f'查询成功,姓名={person.name}')
first()
def getperson(request): person=Person.objects.first() # first()无参数 print('-----------------------') print(type(person)) # <class 'myapp.models.Person'> print('-----------------------') print(person) # Person object (239) print('-----------------------') return HttpResponse(f'查询成功,姓名={person.name}')
last()
def getperson(request): person=Person.objects.last() return HttpResponse(f'查询成功,姓名={person.name}')
查询计数,统计查询结果集,count()
def getperson(request): persons=Person.objects.all() return HttpResponse(f'查询成功,{persons.count()}个人。')
判断查询结果集是否有数据,exists()
def getperson(request): persons=Person.objects.filter(name='李四') if persons.exists(): return HttpResponse('查询成功') return HttpResponse('查询失败')
通过主键查询,pk=xxx,id=xxx
persons=Person.objects.filter(pk=308) 或 persons=Person.objects.filter(id=306)
约束:唯一约束,unique=True,默认False
class User(models.Model): name=models.CharField(max_length=16,unique=True)
添加重复记录会报IntegrityError完整性错误,UNIQUE constraint failed: myapp_user.name唯一约束失败
限制查询结果集,类似字符串或列表的切片,但下标不能为负
def getperson(request): persons=Person.objects.all()[2:4] # 跳过第0、1个,取第2、3个,左闭右开 context={ 'persons':persons } return render(request,'hello.html',context=context)
分页查看,get请求传参数,方式一
def getperson(request): page=int(request.GET.get('page')) # get请求传参,关键代码 per_page=5 persons=Person.objects.all()[(page-1)*per_page:page*per_page] # 分页,关键代码 context={ 'persons':persons } return render(request,'hello.html',context=context)
浏览器访问:http://127.0.0.1:8000/myapp/getperson/?page=2
分页器
views.py
def getinfo(request): p=int(request.GET.get('p',1)) notices=Notice.objects.all() paginator=Paginator(notices,10) # 每页10条数据 print(f'总记录数={paginator.count}') print(f'总页数={paginator.num_pages}') print(f'页码列表={paginator.page_range}') page=paginator.page(p) print(f'当前页数据对象={page.object_list}') print(f'当前页码值={page.number}') print(f'当前页的Paginator对象={page.paginator}') print(f'是否有下一页:{page.has_next()}') print(f'是否有上一页:{page.has_previous()}') print(f'是否有上一页或下一页:{page.has_other_pages()}') print(f'下一页页码:{page.next_page_number()}') print(f'上一页页码:{page.previous_page_number()}') print(f'当前页数据数:{len(page)}') return render(request,'info.html',locals())
urls.py path('getinfo/',views.getinfo),
models.py
class Notice(models.Model): info=models.CharField(max_length=16)
info.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>分页信息</title> </head> <body> <ul> {% for notice in page %} <li>{{ notice.info }}</li> {% endfor %} </ul> </body> </html>
get请求传参数,方式二
urls.py path('hi/<int:num>/<str:name>/',views.hi)
views.py
def hi(request,num,name): print(type(num)) # <class 'int'> print(num) # 666 print(type(name)) # <class 'str'> print(name) # good return HttpResponse('hi')
浏览器访问:http://127.0.0.1:8000/myapp/hi/666/good/
数据级联
一对多
models.py
class User(models.Model): name=models.CharField(max_length=16,unique=True) class Good(models.Model): name=models.CharField(max_length=16) user=models.ForeignKey(User,on_delete=models.CASCADE) # 一定要设置on_delete,否则生成迁移文件时会报错
views.py
def adduser(request): user=User() user.name='黄牛' user.save() return HttpResponse(f'添加User成功,{user.name}') def addgood(request): user=User.objects.last() good=Good() good.name='泉水' good.user=user good.save() return HttpResponse(f'添加货物成功:{good.name},{good.user.name}') def getgood(request): user=User.objects.last() # goods=Good.objects.filter(user=user) goods=user.good_set.all() return render(request,'goods.html',context=locals())
goods.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>货物清单</title> </head> <body> <ul> {% for good in goods %} <li>{{ good.name }} - {{ good.user.name }}</li> {% endfor %} </ul> </body> </html>
跨关系查询,数据去重
models.py
class Author(models.Model): name=models.CharField(max_length=16) class Article(models.Model): content=models.TextField() # 可不带参数 author=models.ForeignKey(Author,on_delete=models.CASCADE)
views.py
def getauthor(request): # 不能写成Article__content__contains,即模型名在此必须全小写 # 规则:模型类名__属性名__比较运算符= authors=Author.objects.filter(article__content__contains='黄山') # 关键代码,跨关系查询 # 不支持distinct操作,下面这行会报错: # authors=Author.objects.filter(article__content__contains='黄山').distinct('name') author_list=[] for author in authors: # 关键代码,数据去重 if author not in author_list: author_list.append(author) return render(request,'authors.html',context=locals())
authors.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>作者清单</title> </head> <body> <ul> {% for author in author_list %} <li>{{ author.name }}</li> {% endfor %} </ul> </body> </html>
聚合、自相关查询、F/Q对象、显隐属性
models.py
class Student(models.Model): # 主键未指定会自动产生id,为隐式属性;若指定,则不会自动产生,为显式属性 sno=models.AutoField(primary_key=True) # 关键代码 name=models.CharField(max_length=16) python=models.IntegerField(default=0) linux=models.IntegerField(default=0)
views.py
def getstudent(request):
# 聚合 # result=Student.objects.aggregate(Avg('python')) # 平均值={'python__avg': 84.2} # result=Student.objects.aggregate(Count('python')) # 计数={'python__count': 5} # result=Student.objects.aggregate(Max('python')) # 最大值={'python__max': 99} # result=Student.objects.aggregate(Min('python')) # 最小值={'python__min': 70} # result=Student.objects.aggregate(Sum('python')) # 最小值={'python__sum': 418} # return HttpResponse(f'查询成功:{result}') # F对象的应用:行(记录)自相关查询 # students=Student.objects.filter(python__gt=F('linux')) # students=Student.objects.filter(python__gt=F('linux')+5) # Q对象的应用:组合条件查询,与&、或|、非~ # students=Student.objects.filter(Q(python__gt=80) & Q(linux__gt=80)) # students=Student.objects.filter(Q(python__gt=80) | Q(linux__gt=80)) students=Student.objects.filter(~Q(Q(python__gt=80) | Q(linux__gt=80))) return render(request,'students.html',locals())
students.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>学生成绩清单</title> </head> <body> <ul> {% for student in students %} <li>学号={{ student.sno }},姓名={{ student.name }},Python={{ student.python }},Linux={{ student.linux }}</li> {% endfor %} </ul> </body> </html>
自定义模型管理器
models.py
class TeacherManager(models.Manager): pass class Teacher(models.Model): name=models.CharField(max_length=16) # teacherManager=models.Manager() # 自定义模型管理器,方式一 teacherManager=TeacherManager() # 自定义模型管理器,方式二
views.py
def getteacher(request): # 模型管理器objects为隐性属性,未指定时会自动生成。 # teacher=Teacher.objects.first() # 若自定义模型管理器teacherManager,则objects就不存在了 teacher=Teacher.teacherManager.first() return HttpResponse(f'获取老师成功:{teacher.name}')
逻辑删除
models.py
class TeacherManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(is_delete=False) class Teacher(models.Model): name=models.CharField(max_length=16) is_delete=models.BooleanField(default=False) # 逻辑删除字段 teacherManager=TeacherManager() # 实际逻辑删除后的查询集,方式二
views.py
def getteachers(request): # 实际逻辑删除后的查询集,方式一 # teachers=Teacher.objects.filter(is_delete=False) # 实际逻辑删除后的查询集,方式二 teachers=Teacher.teacherManager.all() return render(request,'teachers.html',locals())
批量添加数据,此法不适合多表级联
models.py
class Teacher(models.Model): name=models.CharField(max_length=16)
views.py
def addteacher(request): teachers=[] for i in range(5): teacher=Teacher() teacher.name='李四{}'.format(i) # teacher.save() # 批量添加时不建议用save(),因为save()会频繁的建立/关闭数据库连接 teachers.append(teacher) Teacher.objects.bulk_create(teachers) # 批量添加时的建议方法 return HttpResponse(f'添加老师成功:{teachers}')
一对一,1:1,多表数据级联操作
models.py
class IDCard(models.Model): idno=models.CharField(max_length=32,unique=True) class Phone(models.Model): phoneno=models.CharField(max_length=16) # CASCADE:删除IDCard会自动删除Phone,删除Phone不会删除IDCard # idcard=models.OneToOneField(IDCard,on_delete=models.CASCADE) # 使用外键+唯一约束实现一对一关系 # PROTECT:删IDCard前必须先删Phone idcard=models.OneToOneField(IDCard,on_delete=models.PROTECT) # SET_NULL:前提是此字段可为空,否则无法设置。 # idcard=models.OneToOneField(IDCard,null=True,on_delete=models.SET_NULL) # SET_DEFAULT:前提是此字段必须设置默认值,且默认值的数据类型要正确,且未被其他对象关联。 # idcard=models.OneToOneField(IDCard,default=4,on_delete=models.SET_DEFAULT) # 前提是设置的值(如666)要存在,且未被其他对象关联。 # idcard=models.OneToOneField(IDCard,default=4,on_delete=models.SET(666))
views.py
def addidcard(request): idcard=IDCard() idcard.idno='x002' idcard.save() return HttpResponse(f'添加身份证成功:{idcard.idno}') def addphone(request): idcard=IDCard.objects.last() phone=Phone() phone.phoneno='132' phone.idcard=idcard phone.save() return HttpResponse(f'添加手机号成功:{phone.phoneno}') def delidcard(request): idcard=IDCard.objects.first() idcard.delete() return HttpResponse(f'删除身份证成功:{idcard.idno}') def delphone(request): phone=Phone.objects.first() phone.delete() return HttpResponse(f'删除手机号成功:{phone.phoneno}')
主从表数据获取:
views.py
def get_idcard_by_phone(request): phone=Phone.objects.first() # idcard为Phone的显性属性 idcard=phone.idcard return HttpResponse(f'通过手机号:{phone.phoneno},获取身份证:{idcard.idno}成功。') def get_phone_by_idcard(request): idcard=IDCard.objects.last() # 主表获取从表通过隐性属性,即从表名小写 # phone为IDCard的隐性属性 phone=idcard.phone return HttpResponse(f'通过身份证:{idcard.idno},获取手机号:{phone.phoneno}成功。')
一对多,1:N
models.py
class Province(models.Model): name=models.CharField(max_length=16) class City(models.Model): name=models.CharField(max_length=16) province=models.ForeignKey(Province,on_delete=models.PROTECT) # 一对多通过外键实现
views.py
def get_province_by_city(request): city=City.objects.first() province=city.province # 显性属性 return HttpResponse(f'通过城市:{city.name},获取省:{province.name}成功。') def get_citys_by_province(request): province=Province.objects.last() cities=province.city_set.all() # city_set为Province的隐性属性 return render(request,'cities.html',locals())
添加数据:
def add_city(request): province=Province.objects.last() city=City() city.name='常州' # city.province=province # 方式一 city.save() province.city_set.add(city) # 方式二,前提是city的外键省字段province可为空,即province=models.ForeignKey(Province,null=True,on_delete=models.PROTECT)
return HttpResponse(f'给省:{province.name}添加城市:{city.name},成功!')
一对多数据删除同一对一
多对多,M:N,由于会创建额外的关系表,所以哪个表申明关系不像一对一和一对多那样很介意了。
models.py
class Reader(models.Model): name=models.CharField(max_length=16) # 更重要的设为主表 class News(models.Model): content=models.TextField()
# 次重要的申明关系,设为从表 reader=models.ManyToManyField(Reader) # 会自动创建额外的关系表维护多对多关系
views.py
def add_reader(request): reader=Reader() reader.name='王五' reader.save() return HttpResponse(f'添加读者成功,{reader.name}') def add_news(request): news=News() news.content='劳动法将严格执行' news.save() return HttpResponse(f'添加新闻成功,{news.content}') def add_reader_news(request): reader=Reader.objects.last() news=News.objects.last() # news.reader.add(reader) # 多对多关系绑定,方式一,从表的显性属性绑定 reader.news_set.add(news) # 多对多关系绽定,方式二,主表的隐性属性绑定。是否有remove、clear等方法? return HttpResponse(f'添加读者-新闻关系成功,{reader.name},{news.content}')
模型继承
models.py
class Animal(models.Model): name=models.CharField(max_length=16) # 这种写法更好,只创建Dog一张表,单表的性能更好。否则会创建Animal和Dog两张表,多表级联会降低性能。 class Meta: abstract=True class Dog(Animal): color=models.CharField(max_length=16)
T 模板语法
countrys.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>幸福国信息</title> </head> <body> <h6>{{ countrys.0.name }}</h6> {# 索引、属性 #} <h6>{{ countrys.1.develop }}</h6> {# 方法,不能传参 #} <h6>{{ provinces.安徽 }}</h6> {# 字典的键 #} {# 单行注释 #} {% comment %} 多行注释 {% endcomment %} <!-- 不建议用HTML的注释,能在浏览器查看源码中显示。 --> <ul> {% for country in countrys %} {% if forloop.first %} <li style="color:#ff0000">{{forloop.counter0}} {{ country.name }}</li> {% elif forloop.last %} <li style="color:#0f0">{{forloop.counter}} {{ country.name }}</li> {% else %} <li>{{ forloop.revcounter0 }} {{ country.name }} {{ forloop.revcounter }}</li> {% endif %} {% empty %} <h2>for循环为空</h2> {% endfor %} </ul> </body> </html>
models.py
class Country(models.Model): name=models.CharField(max_length=16) def develop(self): return '杀贪官、斩奸商!人民幸福!人人爱国敬业诚信友善!'
views.py
def getcountrys(request): countrys=Country.objects.all() provinces={ '安徽':'合肥', '江苏':'南京', } return render(request,'countrys.html',locals())
T 模板语法 继续
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>模板语法</title> <script type="text/javascript"> alert('网站崩溃了!') </script> </head> <body> {% widthratio 5 1 2 %} {# 数 分母 分子 #} <hr> <ul> {% for name in names %} {% if forloop.counter|divisibleby:2 %} {# 整除 #} <li style="color:red">{{name}}</li> {% else %} <li style="color:blue">{{name}}</li> {% endif %} {% endfor %} </ul> <hr> {% for name in names %} {% ifequal name 'Tom' %} <li>{{name}}</li> {% endifequal %} {% ifnotequal name "Tom" %} <li>{{name}}</li> {% endifnotequal %} {% endfor %} <hr> {{5|add:10}} {{10|add:-5}} {{'Tom'|lower}} {{"Tom"|upper}} <hr> {{names|join:'-'}} <hr> {{var|default:'中国'}} <hr> {{dateVal|date:'y-m-d'}} <hr> {{code|safe}} <hr> {% autoescape off %} {{code}} {% endautoescape %} <hr> {% autoescape on %} {{code}} {% endautoescape %} </body> </html>
views.py
def index(request): names=['Tom','Jerry'] dateVal=datetime.now() # code='<h1>好好学习</h1>' # code=''' # <script type="text/javascript"> # alert('网站崩溃了!') # </script> # ''' code=''' <script type="text/javascript"> window.onload=function(){ var lis=document.getElementsByTagName("li"); for(var i=0;i<lis.length;i++){ console.log(i); lis[i].innerHTML="你的网站崩溃了!"; } } </script> ''' return render(request,'index.html',locals())
结构标签,block+extends,化整为零,推荐使用
base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{Title}}</title> {% block extJS %} {% endblock %} {% block extCSS%} {% endblock %} </head> <body> {% block header %} {# 首次出现为定义 #} {% endblock %} {% block banner %} {% endblock %} {% block content %} {% endblock %} {% block footer %} {% endblock %} </body> </html>
loginBase.html
{% extends 'base.html' %} {% block header %} <h1>再次出现为重写</h1> {% endblock %} {% block logininfo %} {# 继承之后再次挖坑,后面的继承此坑不生效 #} {% endblock %}
login.html
{% extends 'loginBase.html' %} {% block header %} {{ block.super }} {# 调用父类后面就为追加 #} <h2>三次及以上出现为覆盖之前的重写</h2> {% endblock %} {% block logininfo %} {# 未生效 #} <h3>这是登录信息</h3> {% endblock %} {% block banner %} <h4>这是轮播</h4> {% endblock %} {% block content %} <h5>这是内容</h5> {% endblock %} {% block footer %} <h6>这是底部信息</h6> {% endblock %}
views.py
def login(request): title='结构标签' return render(request,'login.html',locals())
结构标签,include,聚零为整,不推荐使用,性能差
first.html
<h1>这是first.html的内容</h1>
second.html
<h2>这是second.html的内容</h2>
all.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{title}}</title> </head> <body> {% include 'first.html' %} {% include 'second.html' %} </body> </html>
views.py
def all(request): title='结构标签include' return render(request,'all.html',locals())
响应json数据
views.py
def getJson(request): data={ 'status':0, 'msg':'ok' } return JsonResponse(data)
urls.py path('getJson',views.getJson),
页面跳转
hi_info.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>页面跳转</title> </head> <body> <a href="/myapp/hi">跳转到hi页</a> </body> </html>
views.py
def hi_info(request): return render(request,'hi_info.html') def hi(request): return HttpResponse('hi')
urls.py path('hi_info',views.hi_info), 、 path('hi',views.hi) 和 path('hi/',views.hi) 有区别,注:路径后是否以/结尾有区别
自定义404页面未找到,Page not found (404)
直接在templates目录中创建404.html
404.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>页面未找到</title> </head> <body> 页面未找到 </body> </html>
设置settings.py DEBUG = False 和 ALLOWED_HOSTS = ['*'] ,ALLOWED_HOSTS不为空就好。
HttpRequest对象
views.py
def hi(request,num,name): print('----------------') print(request) # <WSGIRequest: GET '/myapp/hi/666/good/?key=%27%E4%B8%AD%E5%9B%BD%27'> print(request.path) # /myapp/hi/666/good/ print(request.method) # GET print(request.encoding) # None print(request.GET) # <QueryDict: {'key': ["'中国'"]}> print(request.POST) # <QueryDict: {}> print(request.FILES) # <MultiValueDict: {}> print(request.COOKIES) # {'csrftoken': 'QybCRghQbKBAaZ7FHsiYWVWTukk6h940GssD4Tt8YRLk3XAfJZ77mWGrZp8Vz9gh'} print(request.session) # <django.contrib.sessions.backends.db.SessionStore object at 0x000002E36FCFE3A0> print(request.is_ajax()) # False print('################') print(request.META) print('----------------') return HttpResponse('hi')
urls.py path('hi/<int:num>/<str:name>/',views.hi)
浏览器访问:http://127.0.0.1:8000/myapp/hi/666/good/?key='中国'
request.META 好多重要信息都在这里面!
{'PUBLIC': 'C:\\Users\\Public', 'LOCALAPPDATA': 'C:\\Users\\xiongjiawei\\AppData\\Local', 'PROGRAMFILES': 'C:\\Program Files', 'VIRTUAL_ENV': 'D:\\妗岄 潰\\mypro\\venv', 'USERNAME': 'xiongjiawei', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'LG_PATH': 'D:\\Program Files (x86)\\HP\\LoadRunne r\\', 'IDEA_INITIAL_DIRECTORY': 'C:\\Program Files\\JetBrains\\PyCharm 2020.1\\bin', 'NUMBER_OF_PROCESSORS': '4', 'PROCESSOR_ARCHITECTURE': 'AMD64', 'PR OMPT': '(venv) $P$G', '__INTELLIJ_COMMAND_HISTFILE__': 'C:\\Users\\xiongjiawei\\AppData\\Roaming\\JetBrains\\PyCharm2020.1\\terminal\\history\\history-1 ', 'SYSTEMDRIVE': 'C:', 'CONFIGSETROOT': 'C:\\WINDOWS\\ConfigSetRoot', 'CLASSPATH': 'C:\\Program Files\\Java\\jdk1.8.0_131\\lib\\dt.jar;C:\\Program File s\\Java\\jdk1.8.0_131\\lib\\tools.jar', 'PROGRAMDATA': 'C:\\ProgramData', 'PROCESSOR_REVISION': '8e09', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x 86)\\Common Files', 'USERPROFILE': 'C:\\Users\\xiongjiawei', 'ANALYSIS_PATH': 'D:\\Program Files (x86)\\HP\\LoadRunner\\', '_OLD_VIRTUAL_PATH': 'D:\\app \\xiongjiawei\\product\\11.2.0\\dbhome_1\\bin;C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program Files (x86)\\Intel\\iCLS Client\\;C:\\Program Files\\I ntel\\iCLS Client\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x8 6)\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files (x86)\\Inte l\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files (x86)\\NVIDIA Corpo ration\\PhysX\\Common;D:\\Program Files\\TortoiseSVN\\bin;D:\\Users\\xiongjiawei\\AppData\\Local\\Android\\sdk\\platform-tools;D:\\Program Files\\VanDyk e Software\\Clients\\;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;D:\\Program Files ( x86)\\Appium\\node_modules\\.bin;D:\\Program Files\\nodejs\\;D:\\Program Files\\android-sdk-windows\\tools;C:\\Program Files\\Java\\jdk1.8.0_131\\bin;D: \\Program Files\\android-sdk-windows\\platform-tools;C:\\Program Files\\Java\\jdk1.8.0_131\\jre\\bin;C:\\WINDOWS\\System32\\OpenSSH\\;d:\\Program Files (x86)\\Tesseract-OCR;d:\\Program Files\\Git\\cmd;D:\\Program Files\\Python\\Python38\\Scripts\\;D:\\Program Files\\Python\\Python38\\;C:\\Users\\xiongji awei\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\xiongjiawei\\AppData\\Roaming\\npm;%PyCharm Community Edition%;D:\\Program Files (x86)\\Tesserac t-OCR;', 'USERDOMAIN': 'LAPTOP-BAIQPBL2', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\ \Modules', 'PROCESSOR_LEVEL': '6', 'TEMP': 'C:\\Users\\XIONGJ~1\\AppData\\Local\\Temp', 'PROGRAMW6432': 'C:\\Program Files', 'TESSDATA_PREFIX': 'd:\\Pro gram Files (x86)\\Tesseract-OCR\\', 'APPDATA': 'C:\\Users\\xiongjiawei\\AppData\\Roaming', 'LOGONSERVER': '\\\\LAPTOP-BAIQPBL2', 'JAVA_HOME': 'C:\\Progr am Files\\Java\\jdk1.8.0_131', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'TERMINAL_EMULATOR': 'JetBrains-JediTerm', 'LR_PATH': 'D:\\Program Files (x86)\\HP\\LoadRunner\\', 'WINDIR': 'C:\\WINDOWS', '_OLD_VIRTUAL_PROMPT': '$P$G', 'SYSTEMROOT': 'C:\\WINDOWS', 'COMPUTERNAME': 'LAPT OP-BAIQPBL2', 'SESSIONNAME': 'Console', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 142 Stepping 9, GenuineIntel', 'HOMEPATH': '\\Users\\xiongjiawei', 'DRIVERDATA': 'C:\\Windows\\System32\\Drivers\\DriverData', 'VUGEN_PATH': 'D:\\Program Files (x86)\\H P\\LoadRunner\\', 'TMP': 'C:\\Users\\XIONGJ~1\\AppData\\Local\\Temp', 'COMSPEC': 'C:\\WINDOWS\\system32\\cmd.exe', 'ALLUSERSPROFILE': 'C:\\ProgramData', 'PATH': 'D:\\妗岄潰\\mypro\\venv\\Scripts;D:\\app\\xiongjiawei\\product\\11.2.0\\dbhome_1\\bin;C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program File s (x86)\\Intel\\iCLS Client\\;C:\\Program Files\\Intel\\iCLS Client\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System3 2\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files\\Intel\\Intel(R) Management En gine Components\\DAL;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\Intel(R) Management Engine Com ponents\\IPT;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;D:\\Program Files\\TortoiseSVN\\bin;D:\\Users\\xiongjiawei\\AppData\\Local\\Andr oid\\sdk\\platform-tools;D:\\Program Files\\VanDyke Software\\Clients\\;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\Syste m32\\WindowsPowerShell\\v1.0\\;D:\\Program Files (x86)\\Appium\\node_modules\\.bin;D:\\Program Files\\nodejs\\;D:\\Program Files\\android-sdk-windows\\t ools;C:\\Program Files\\Java\\jdk1.8.0_131\\bin;D:\\Program Files\\android-sdk-windows\\platform-tools;C:\\Program Files\\Java\\jdk1.8.0_131\\jre\\bin;C :\\WINDOWS\\System32\\OpenSSH\\;d:\\Program Files (x86)\\Tesseract-OCR;d:\\Program Files\\Git\\cmd;D:\\Program Files\\Python\\Python38\\Scripts\\;D:\\Pr ogram Files\\Python\\Python38\\;C:\\Users\\xiongjiawei\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\xiongjiawei\\AppData\\Roaming\\npm;%PyCharm Co mmunity Edition%;D:\\Program Files (x86)\\Tesseract-OCR;', 'ANDROID_HOME': 'D:\\Program Files\\android-sdk-windows', 'HOMEDRIVE': 'C:', 'COMMONPROGRAMFI LES': 'C:\\Program Files\\Common Files', 'ONEDRIVE': 'C:\\Users\\xiongjiawei\\OneDrive', 'USERDOMAIN_ROAMINGPROFILE': 'LAPTOP-BAIQPBL2', 'OS': 'Windows_ NT', 'LOG_FILE': 'C:\\Users\\xiongjiawei\\AppData\\Local\\Temp\\ihp_custom_batches.log', 'DJANGO_SETTINGS_MODULE': 'mypro.settings', 'RUN_MAIN': 'true', 'SERVER_NAME': 'LAPTOP-BAIQPBL2', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'S ERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/myapp/hi/666/good/', 'QUERY_STRING': 'key=%27% E4%B8%AD%E5%9B%BD%27', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_C ACHE_CONTROL': 'max-age=0', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHT ML, like Gecko) Chrome/85.0.4183.121 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/a png,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'HTTP_SEC_FETCH_SITE': 'none', 'HTTP_SEC_FETCH_MODE': 'navigate', 'HTTP_SEC_FETCH_USER': '?1', 'H TTP_SEC_FETCH_DEST': 'document', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'csrftoken=QybCRg hQbKBAaZ7FHsiYWVWTukk6h940GssD4Tt8YRLk3XAfJZ77mWGrZp8Vz9gh', 'wsgi.input': <django.core.handlers.wsgi.LimitedStream object at 0x000002B0A8BD3E20>, 'wsgi .errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsg i.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>, 'CSRF_COOKIE': 'QybCRghQbKBAaZ7FHsiYWVWTukk6h 940GssD4Tt8YRLk3XAfJZ77mWGrZp8Vz9gh'}
get请求传递多个同名参数获取方式:
views.py
def hi(request,num,name): print('----------------') print(request.GET.get('name')) # xyz print(request.GET['name']) # xyz print(request.GET.getlist('name')) # ['abc', 'xyz'] print(request.GET.get('notexist')) # None print(request.GET.getlist('notexist')) # [] print('----------------') return HttpResponse('hi')
浏览器访问:http://127.0.0.1:8000/myapp/hi/666/good/?name=abc&name=xyz
POST请求
CSRF验证失败,解决方式一:注释掉settiings中的相应代码 'django.middleware.csrf.CsrfViewMiddleware'
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="/myapp/login/" method="post"> {% csrf_token %} {# CSRF验证失败,解决方式二 #} <!-- <input type="hidden" name="csrfmiddlewaretoken" value="DFXxHTfWPbUlw37XwtuJAj4wh7EXGLSKtzeyUwreCi45p1Axy0jS0kO4McsMYL41">--> {# 若不写name,则request.POST为空 #} <input type="text" placeholder="请输入账号" name="username"> <input type="password" placeholder="请输入密码" name="password"> <!-- <button>登录</button>--> {# 登录按钮写法一 #} <input type="submit" value="登录"> {# 登录按钮写法二 #} </form> </body> </html>
views.py
def login(request): if request.method=='GET': return render(request,'login.html') elif request.method=='POST': # login.html中账号和密码未写name则为<QueryDict: {}> # 写了name为<QueryDict: {'username': ['111'], 'password': ['222']}> print(request.POST) # 获取其中具体的值同request.GET return HttpResponse('登录成功')
HttpResponse和JsonResponse
views.py
def getresponse(request): # Content-Type: text/html response=HttpResponse() response.content='中' response.content='国' # 覆盖 response.write('家') # 追加 response.write('庭') # 还是追加 response.flush() # Content-Type: application/json # response=JsonResponse({'country':'中国'}) return response
重定向
views.py
def first(request): # return HttpResponse('first') return HttpResponseRedirect('/myapp/second/') # 重定向,方式一 # return redirect('/myapp/second') # 重定向,方式二 def second(request): return HttpResponse('second')
urls.py
path('first/',views.first), path('second/',views.second),
会话技术
Cookie,客户端会话技术
views.py
def login(request): if request.method=='GET': return render(request,'login.html') elif request.method=='POST': username=request.POST.get('username') password=request.POST.get('password') response=redirect('/myapp/mine/') # Cookie是服务器设置给客户端的,通过response实现 # Cookie不支持中文,可通过存时编码,取时解码实现。 username=base64.standard_b64encode(username.encode('utf8')).decode('utf8') password=base64.standard_b64encode(password.encode('utf8')).decode('utf8') # response.set_cookie('username',username) # response.set_cookie('password',password) # response.set_signed_cookie('username',username,'salt') # response.set_signed_cookie('password',password,'salt') # response.set_signed_cookie('username',username,SECRET_KEY) # response.set_signed_cookie('password',password,SECRET_KEY) # 设置过期时间max_age单位秒,0表示浏览器关闭失效。 # expires单位秒,还支持timedelta(days=1),1天后过期 response.set_signed_cookie('username',username,SECRET_KEY,max_age=60) response.set_signed_cookie('password',password,SECRET_KEY,expires=timedelta(days=1)) return response def mine(request): # 在请求时Cookie自动被携带,Cookie不能跨浏览器,且IP(域名)隔离 # username=request.COOKIES.get('username') # password=request.COOKIES.get('password') try: # username=request.get_signed_cookie('username',salt='salt') # password=request.get_signed_cookie('password',salt='salt') username=request.get_signed_cookie('username',salt=SECRET_KEY) password=request.get_signed_cookie('password',salt=SECRET_KEY) username=base64.standard_b64decode(username.encode('utf8')).decode('utf8') password=base64.standard_b64decode(password.encode('utf8')).decode('utf8') except: username=password=None print('username=',username) # 写法一 # if username and password: # return HttpResponse('个人中心') # return HttpResponse('游客') # 写法二 return render(request,'mine.html',locals()) def logout(request): response=HttpResponse('退出登录') response.delete_cookie('username') response.delete_cookie('password') return response
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="/myapp/login/" method="post"> {% csrf_token %} <input type="text" placeholder="请输入账号" name="username"> <input type="password" placeholder="请输入密码" name="password"> <button>登录</button> </form> </body> </html>
mine.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>个人中心</title> </head> <body> {% if username %} <h1>欢迎:{{ username }}</h1> <a href="/myapp/logout/">退出</a> {% else %} <h2>游客</h2> <a href="/myapp/login/">登录</a> {% endif %} </body> </html>
urls.py
path('login/',views.login),
path('mine/',views.mine),
path('logout/',views.logout),
session,服务端会话技术
views.py
def login(request): if request.method=='GET': return render(request,'login.html') elif request.method=='POST': username=request.POST.get('username') password=request.POST.get('password') response=redirect('/myapp/mine/') # 支持中文,默认用base64编码/解码 request.session['username']=username request.session['password']=password return response def mine(request): username=request.session.get('username') password=request.session.get('password') # 写法一 # if username and password: # return HttpResponse('个人中心') # return HttpResponse('游客') # 写法二 return render(request,'mine.html',locals()) def logout(request): response=HttpResponse('退出登录') # 退出会话,方式一,不会同步删除django_session表中的session会话记录 # response.delete_cookie('sessionid') # 退出会话,方式二,本质是将django_session表中的session会话记录的session_data字段中关于用户信息的数据置空{} # del request.session['username'] # del request.session['password'] # 退出会话,方式三,推荐方式,django_session表不会产生垃圾数据 request.session.flush() return response
Token,自定义服务端会话技术。Cookie依赖于浏览器,Session要利用Cookie的Set-Cookie:sessionid=xxx,所以还是依赖浏览器。对于非BS架构的,如CS架构,就不能使用Cookie和Session会话技术。
models.py
class Staff(models.Model): name=models.CharField(max_length=16) token=models.CharField(max_length=16,null=True)
views.py
def login(request): name=request.POST.get('name') print('name=',name) try: staff=Staff.objects.get(name=name) token=uuid.uuid4().hex staff.token=token staff.save() data={ 'msg':'ok', 'token':token } return JsonResponse(data) except: return JsonResponse({'msg': '用户不存在,或不唯一'}) def mine(request): token=request.GET.get('token') try: staff=Staff.objects.get(token=token) data={ 'msg':'ok', 'name':staff.name } return JsonResponse(data) except: return JsonResponse({'msg':'用户不存在,或不唯一'})
settings.py # 'django.middleware.csrf.CsrfViewMiddleware',
类似手机应用服务端接口,所以使用 Postman配合验证。