目录
创建正常项目简单流程梳理(mysql服务)
安装
创建项目(模型映射表)
创建应用(默认方式)
Django后台管理系统
服务器
管理操作
向admin注册模型
模型Model详细介绍
1.创建项目(mysql服务)
2.修改默认sqlite3为mysql
3.修改后在创建的项目中创建App
4.编写好Model
5.生成迁移
6.查看Mysql数据
7.类的属性objects
8.创建对象
查询集
比较运算符
聚合函数
F对象
Q对象
视图详细介绍
urlconf
解析流程
错误页面
Request对象
GET
POST
Reponse对象
子类HttpResponseRedirect(注意namespace的配置)
子类JsonResponse
简写函数render
Session
使用Redis缓存session
模板详细介绍
模板配置
模板定义
变量
标签
过滤器
注释
反向解析(动态生成跳转url)
模板继承
HTML转义
CSRF(跨站请求伪造)
验证码
高级特性
静态文件
中间件
上传图片
Admin站点
路径相关疑问
分页
Ajax请求和JsonReponse数据
Django官网
该备忘录是基于以下版本开发
Mac OS High sierra 10.13.6
mintoudeMacBook-Pro-2:~ mintou$ python3 -m django --version
2.2.1
mintoudeMacBook-Pro-2:~ mintou$ ipython3
Python 3.7.3 (default, Mar 27 2019, 09:23:39)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
pip3 install Django
pip3 install ipython3
FVFXGM44HV29:mysite mikejing191$ ipython3
Python 3.7.3 (default, Mar 27 2019, 09:23:39)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import django
In [2]: django.get_version()
Out[2]: '2.2.1'
In [3]:
cd到指定目录(例如桌面),mkdir一个文件夹放项目
然后执行
django-admin startproject test1
1.cd到manage.py下的目录,然后执行创建一个booktest的应用
mintoudeMacBook-Pro-2:DJangoProject mintou$ pwd
/Users/mintou/Desktop/DJangoProject
mintoudeMacBook-Pro-2:DJangoProject mintou$ ls
test1
mintoudeMacBook-Pro-2:DJangoProject mintou$ cd test1/
mintoudeMacBook-Pro-2:test1 mintou$ ls -a
. booktest manage.py
.. db.sqlite3 test1
mintoudeMacBook-Pro-2:test1 mintou$ python3 manage.py startapp booktest
从这也可以看到Django是默认用的sqlite3编写的
看下应用的目录
2.定义模型类
from django.db import models
class BookInfo(models.Model):
book_title = models.CharField(max_length=20)
book_publish_date = models.DateTimeField()
def __str__(self):
# 2.1最新版本不需要encode
return self.book_title
class HeroInfo(models.Model):
hero_name = models.CharField(max_length=20)
hero_gender = models.BooleanField()
hero_content = models.CharField(max_length=100)
# 这里和1.x版本不同的是需要增加on_delete CASCADE代表级联操作。主表删除之后和这个关联的都会跟随删除
hero_book = models.ForeignKey('BookInfo', on_delete=models.CASCADE)
def __str__(self):
return self.hero_name
3.生成数据表迁移
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'booktest',
]
python3 manage.py makemigrations
python3 manage.py migrate
这个 migrate
命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations
来跟踪执行过哪些迁移)并应用在数据库上 - 也就是将你对模型的更改同步到数据库结构上。
迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。我们会在后面的教程中更加深入的学习这部分内容,现在,你只需要记住,改变模型需要这三步:
models.py
文件,改变模型。python manage.py makemigrations
为模型的改变生成迁移文件。python manage.py migrate
来应用数据库迁移。数据库迁移被分解成生成和应用两个命令是为了让你能够在代码控制系统上提交迁移数据并使其能在多个应用里使用;这不仅仅会让开发更加简单,也给别的开发者和生产环境中的使用带来方便。
4.测试数据操作
回到manage.py的目录进入shell,导入包然后执行代码查看所有BookInfo的信息
mintoudeMacBook-Pro-2:test1 mintou$ pwd
/Users/mintou/Desktop/DJangoProject/test1
mintoudeMacBook-Pro-2:test1 mintou$ ls
booktest db.sqlite3 manage.py test1
mintoudeMacBook-Pro-2:test1 mintou$ python3 manage.py shell
Python 3.6.3 (v3.6.3:2c5fed86e0, Oct 3 2017, 00:32:08)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.3.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from booktest.models import BookInfo, HeroInfo
In [2]: from django.utils import timezone
In [3]: from datetime import *
In [4]: BookInfo.objects.all()
Out[4]: , ]>
In [5]: book1 = BookInfo()
In [6]: book1.book_title = "三国演义"
In [8]: book1.book_publish_date = datetime(year=2018,month=1,day=30)
In [9]: book1.save()
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1421: RuntimeWarning: DateTimeField BookInfo.book_publish_date received a naive datetime (2018-01-30 00:00:00) while time zone support is active.
RuntimeWarning)
In [10]: BookInfo.objects.all()
Out[10]: , , ]>
In [11]: BookInfo.objects.get(pk=3)
Out[11]:
In [12]: book2 = BookInfo.objects.get(pk=1)
In [13]: book2.book_title
Out[13]: '射雕银熊转'
In [14]: book2.book_title = "射雕转"
In [15]: book2.save()
book2.delete()
关联对象的操作
In [18]: HeroInfo.objects.all()
Out[18]: , , , , ]>
In [19]: h=HeroInfo()
In [20]: h.hero_name = "霹雳火"
In [21]: h.hero_gender = True
In [22]: h.hero_content = "晴明"
In [23]: BookInfo.objects.all()
Out[23]: , , ]>
In [24]: b2=BookInfo.objects.get(pk=2)
In [25]: h.hero_book=b2
In [26]: h.save()
In [27]: HeroInfo.objects.all()
Out[27]: , , , , , ]>
In [28]: b2.heroinfo_set.all()
Out[28]: , , , ]>
In [29]: h = b2.heroinfo_set.create(hero_name='豹子头',hero_gender=True,hero_con
...: tent="灵宠")
In [30]: HeroInfo.objects.all()
Out[30]: , , , , , , ]>
python3 manage.py runserver
mintoudeMacBook-Pro-2:test1 mintou$ python3 manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
August 16, 2018 - 10:07:53
Django version 2.1, using settings 'test1.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
python3 manage.py runserver 8080
站点分为“内容发布”和“公共访问”两部分
使用django的管理
python3 manage.py createsuperuser
按提示输入用户名、邮箱、密码
管理界面本地化
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'
from django.contrib import admin
# 这里Python3编写就不能和2一样直接写models,需要在写一个.
from .models import BookInfo, HeroInfo
admin.site.register(BookInfo)
admin.site.register(HeroInfo)
自定义管理页面
class QuestionAdmin(admin.ModelAdmin):
...
admin.site.register(Question, QuestionAdmin)
列表页属性
list_display = ['pk', 'book_title', 'book_publish_date']
list_filter = ['book_title']
search_fields = ['book_title']
list_per_page = 10
添加、修改页属性
fields = ['book_publish_date', 'book_title']
fieldsets = [
('basic',{'fields': ['book_title']}),
('more', {'fields': ['book_publish_date']}),
]
关联对象
对于HeroInfo模型类,有两种注册方式
按照BookInfor的注册方式完成HeroInfo的注册
from django.contrib import admin
# Register your models here.
from .models import BookInfo, HeroInfo
# 插入的Book的时候插入Hero 指定三条
class HeroInfoInline(admin.StackedInline):
model = HeroInfo
extra = 3
class BookAdmin(admin.ModelAdmin):
# 展示方式 UI
list_display = ['pk', 'book_title', 'book_publish_date']
# 搜索UI
search_fields = ['book_title']
# 插入BookInfo的时候可以带上Hero子类插入,具体信息看HeroInfoInline的属性
inlines = [HeroInfoInline]
class HeroAdmin(admin.ModelAdmin):
list_display = ['pk', 'hero_name', 'hero_gender', 'hero_content', 'hero_book']
search_fields = ['hero_name']
# 这里把需要的models注册进行 例如 BookInfo 和 HeroInfo 最基本的UI展示
# 如果需要自定义UI就需要写一个继承于admin.ModelAdmin来指定字段编写展示
admin.site.register(BookInfo, BookAdmin)
admin.site.register(HeroInfo, HeroAdmin)
插入BookInfo的时候可以看到底部会附带三个HeroInfo让我们填写
布尔值的显示
class HeroInfo(models.Model):
hero_name = models.CharField(max_length=20)
hero_gender = models.BooleanField()
hero_content = models.CharField(max_length=100)
# 这里和1.x版本不同的是需要增加on_delete CASCADE代表级联操作。主表删除之后和这个关联的都会跟随删除
hero_book = models.ForeignKey('BookInfo', on_delete=models.CASCADE)
def __str__(self):
return self.hero_name
def gender(self):
if self.hero_gender:
return "男"
else:
return "女"
gender.short_description = '性别'
class HeroAdmin(admin.ModelAdmin):
list_display = ['pk', 'hero_name', 'gender', 'hero_content', 'hero_book']
search_fields = ['hero_name']
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return HttpResponse('Hello Mikejing')
def detail(request,id):
return HttpResponse('detail page--->%s'%id)
URLconf(注意namespace的配置)
from django.contrib import admin
from django.urls import path, include, re_path
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^', include('booktest.urls'))
]
这里需要注意两点
1.这个是主入口,需要例如有个应用模块是booktest,我们需要另外再建一个urls,那么根部就需要把它include进来,需要把include包导入
2.2x版本之前是默认支持正则的,Django 2.x之后需要把path和re_path两个包都导入,后者对应正则匹配路径
from django.urls import path, re_path
from . import views
urlpatterns = [
re_path(r'^$', views.index, name='index'),
re_path(r'^book/([0-9]+)$', views.detail, name='detail'),
]
'DIRS': [os.path.join(BASE_DIR, 'templates')],
{{输出值,可以是变量,也可以是对象.属性}}
{%执行代码段%}
index.html模板
{%for book in lists%}
- {{book.book_title}}
{%endfor%}
注意:
这里a标签里面的href中写的路径有两个区别如下图,首先明确一点,无论哪种都是从头根部urls开启重新匹配
带/和不带的区别,一般写全路径都需要带上/
detail.html模板
{%for hero in lists%}
- {{hero.hero_name}} {{hero.hero_gender}} {{hero.hero_content}}
{%endfor%}
应用urls
from django.urls import path, re_path
from . import views
urlpatterns = [
re_path(r'^$', views.index, name='index'),
re_path(r'^book/([0-9]+)$', views.detail, name='detail'),
]
这里说的不是根目录下的urls,是每个创建的应用对应的urls,里面如果用了re_path正则来匹配,那么正则里面获取值的方式就是(),加了就能传递到views.detail方法里面作为第二个参数,没有默认还是一个
views里面的id就是正则()里面匹配到的id,根据id查找对应的heros进行模板渲染
from django.shortcuts import render
from django.http import HttpResponse
from .models import *
def index(request):
books = BookInfo.objects.all()
context = {'lists': books}
return render(request, 'booktest/index.html', context)
def detail(request, id):
book = BookInfo.objects.get(pk=id)
heros = book.heroinfo_set.all()
return render(request, 'booktest/detail.html', {'lists': heros})
定义属性
字段类型
字段选项
关系
bookinfo.heroinfo_set
heroinfo.bookinfo
heroinfo.book_id
元选项
_
class BookInfo(models.Model):
...
class Meta():
ordering = ['id']
class BookInfo(models.Model):
...
class Meta():
ordering = ['-id']
cd 到指定目录
django-admin startproject test2
打开配置文件默认用sqlite3引擎,name表示工程中sqlite3的路径
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
这里可以看到默认的数据库引擎是sqlite3,我们现在用Mysql,打开backends路径下的查看如下
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6
这是我Python 3.6的安装路径
到该路径下有很多python文件,这些都是默认的包,我们安装的第三方包都在site-packages里面
可以看到,除了sqlite3,还有mysql和oracle等,我们现在用mysql,因此就把上面的配置文件修改成mysql引擎
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test2',
'USER': 'root',
'PASSWORD': 'mikejing',
'HOST': 'localhost',
'PORT': '3306'
}
}
这里的修改是要到mysql服务器上创建一个名字为test2的database ,mysql没有test2数据库,因此后续需要生成迁移,如果已经有了,就不需要进行迁移,直接跑服务就好
mintou$ python3 manage.py startapp booktest
注意:当你修改成Mysql后之后,由于不在是2.7 python,我们用的是3.6 python针对Mysql的包名都不同,直接执行直接报错
Did you install mysqlclient or MySQL-python?
我们在booktest--->test2---->__init__.py根目录下的__init__中添加如下
import pymysql
pymysql.install_as_MySQLdb()
from django.db import models
class BookInfo(models.Model):
btitle = models.CharField(max_length=20)
bpub_data = models.DateTimeField(db_column= 'book_publish_data')
bread = models.IntegerField(default=0)
bcommet = 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=True)
isDelete = models.BooleanField(default=False)
hcontent = models.CharField(max_length=100)
# 注意2.1一定要加上on_delete
hbook = models.ForeignKey('BookInfo',on_delete=models.CASCADE)
把mode加入到setting文件中的INSTALLED_APPS选项中
python3 manage.py makemigrations
再次执行makemigrations即可
mintoudeMacBook-Pro-2:test2 mintou$ python3 manage.py makemigrations
Migrations for 'booktest':
booktest/migrations/0001_initial.py
- Create model BookInfo
- Create model HeroInfo
mintoudeMacBook-Pro-2:test2 mintou$ python3 manage.py migrate
最后执行migrate执行迁移,把我们做的Model数据类型全部迁移到数据库上生成对应的表信息
打开终端,链接数据库
mysql -uroot -p
输入密码
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test2 |
+--------------------+
5 rows in set (0.00 sec)
mysql> use test2
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+----------------------------+
| Tables_in_test2 |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| bookinfo |
| booktest_heroinfo |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
12 rows in set (0.00 sec)
mysql> desc bookinfo
-> ;
+-------------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| btitle | varchar(20) | NO | | NULL | |
| book_publish_data | datetime(6) | NO | | NULL | |
| bread | int(11) | NO | | NULL | |
| bcommet | int(11) | NO | | NULL | |
| isDelete | tinyint(1) | NO | | NULL | |
+-------------------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
mysql> desc booktest_heroinfo;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| hname | varchar(20) | NO | | NULL | |
| hgender | tinyint(1) | NO | | NULL | |
| isDelete | tinyint(1) | NO | | NULL | |
| hcontent | varchar(100) | NO | | NULL | |
| hbook_id | int(11) | NO | MUL | NULL | |
+----------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
可以看到我们刚才的model信息变成了mysql表,Django帮我们生成了很多其他的标,主要看
booktest_heroinfo 和 bookinfo 前者是默认表名,后者是通过Meta元类修改自定义的表名
class BookInfo(models.Model):
...
books = models.Manager()
管理器Manager
class BookInfoManager(models.Manager):
def get_queryset(self):
return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
class BookInfo(models.Model):
...
books = BookInfoManager()
该类继承了models.Manager,重写的get_queryset的方法,当我们重新进入
In [3]: BookInfo.objects.all()
Out[3]: , , , ]>
In [4]: exit
上半段,默认查出所有,没有重写之前
下半段是重写之后filter了,查出来只有三个了,而且不再是默认的objects,而是我们自己定义的类名books
mintoudeMacBook-Pro-2:test2 mintou$ python3 manage.py shell
Python 3.6.3 (v3.6.3:2c5fed86e0, Oct 3 2017, 00:32:08)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.3.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from booktest.models import BookInfo, HeroInfo
In [2]: BookInfo.objects.all()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 BookInfo.objects.all()
AttributeError: type object 'BookInfo' has no attribute 'objects'
In [3]: BookInfo.books.all()
Out[3]: , , ]>
class BookInfo(models.Model):
...
@classmethod
def create(cls, title, pub_date):
book = cls(btitle=title, bpub_date=pub_date)
book.bread=0
book.bcommet=0
book.isDelete = False
return book
引入时间包:from datetime import *
调用:book=BookInfo.create("hello",datetime(1980,10,11));
保存:book.save()
class BookInfoManager(models.Manager):
def create_book(self, title, pub_date):
book = self.model()
book.btitle = title
book.bpub_date = pub_date
book.bread=0
book.bcommet=0
book.isDelete = False
return book
class BookInfo(models.Model):
...
books = BookInfoManager()
调用:book=BookInfo.books.create_book("abc",datetime(1980,1,1))
保存:book.save()
class BookInfoManager(models.Manager):
def create_book(self, title, pub_date):
book = self.create(btitle = title,bpub_date = pub_date,bread=0,bcommet=0,isDelete = False)
return book
class BookInfo(models.Model):
...
books = BookInfoManager()
调用:book=Book.books.create_book("abc",datetime(1980,1,1))
查看:book.pk
实例的属性
实例的方法
filter(键1=值1,键2=值2)
等价于
filter(键1=值1).filter(键2=值2)
限制查询集
查询集的缓存
print([e.title for e in Entry.objects.all()])
print([e.title for e in Entry.objects.all()])
querylist=Entry.objects.all()
print([e.title for e in querylist])
print([e.title for e in querylist])
字段查询
filter(isDelete=False)
exclude(btitle__contains='传')
exclude(btitle__endswith='传')
In [13]: BookInfo.books.filter(heroinfo__isnull=False)
Out[13]: , , , , , , , , , , , , ]>
这里我们第四种书雪山飞狐由于isDelete字段是True,所以没出来,一般我们要查询一个book下面的所有hero,需要拿到book对象,也可以通过这方法来查询所有书的hero
filter(btitle__isnull=False)
filter(pk__in=[1, 2, 3, 4, 5])
filter(id__gt=3)
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
filter(heroinfo_ _hcontent_ _contains='八')
filter(pk__lt=6)
from django.db.models import Max
maxDate = list.aggregate(Max('bpub_date'))
count = list.count()
可以使用模型的字段A与字段B进行比较,如果A写在了等号的左边,则B出现在等号的右边,需要通过F对象构造
list.filter(bread__gte=F('bcommet'))
list.filter(bread__gte=F('bcommet') * 2)
list.filter(isDelete=F('heroinfo__isDelete'))
list.filter(bpub_date__lt=F('bpub_date') + timedelta(days=1))
from django.db.models import Q
list.filter(Q(pk_ _lt=6))
list.filter(pk_ _lt=6).filter(bcommet_ _gt=10)
list.filter(Q(pk_ _lt=6) | Q(bcommet_ _gt=10))
list.filter(~Q(pk__lt=6))
浏览器输入host+port+path---->DJango获取到地址除去host+port解析path---->匹配urls------>传递给views接收request返回response
http://www.google.com/python/1/?i=1&p=new,只匹配“/python/1/”部分
re_path(r'^([0-9]+)/$', views.detail, name='detail'),
re_path(r'^(?P[0-9]+)/$', views.detail, name='detail'),
新建的项目会有一个和项目同名的文件夹,下面有对应的urls文件,如下
根的urls
"""
from django.contrib import admin
from django.urls import path, re_path, include
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^booktest/', include('booktest.urls'))
]
app urls
from django.urls import path, re_path
from . import views
urlpatterns = [
re_path(r'^$', views.index, name='index'),
re_path(r'^book/([0-9]+)$', views.detail, name='detail'),
]
请求http://www.google.com/booktest/1/
在sesstings.py中的配置:
re_path(r'^booktest/', include('booktest.urls', namespace='booktest')),
在booktest应用urls.py中的配置
re_path(r'^([0-9]+)/$', views.detail, name='detail'),
匹配部分是:/booktest/1/
匹配过程:在settings.py中与“booktest/”成功,再用“1/”与booktest应用的urls匹配
根部
from django.contrib import admin
from django.urls import path, re_path, include
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^booktest/', include('booktest.urls', namespace='booktest'))
]
app
from django.urls import path, re_path
from . import views
app_name = 'booktest'
urlpatterns = [
]
这里和上面的配置区别就是需要加上namespace参数,然后在appurls加入app_name参数
404 (page not found) 视图
找不到了
{{request_path}}
DEBUG = False
ALLOWED_HOSTS = ['*', ]
http://127.0.0.1:8000/test/
500 (server error) 视图
400 (bad request) 视图
属性
方法
QueryDict对象
dict.get('键',default)
或简写为
dict['键']
dict.getlist('键',default)
def getTest1(request):
return render(request,'booktest/getTest1.html')
def getTest2(request):
return render(request,'booktest/getTest2.html')
def getTest3(request):
return render(request,'booktest/getTest3.html')
url(r'^getTest1/$', views.getTest1),
url(r'^getTest2/$', views.getTest2),
url(r'^getTest3/$', views.getTest3),
Title
链接1:一个键传递一个值
gettest2
链接2:一个键传递多个值
gettest3
def getTest2(request):
a=request.GET['a']
b=request.GET['b']
context={'a':a,'b':b}
return render(request,'booktest/getTest2.html',context)
Title
a:{{ a }}
b:{{ b }}
def getTest3(request):
a=request.GET.getlist('a')
b=request.GET['b']
context={'a':a,'b':b}
return render(request,'booktest/getTest3.html',context)
Title
a:{% for item in a %}
{{ item }}
{% endfor %}
b:{{ b }}
def postTest1(request):
return render(request,'booktest/postTest1.html')
url(r'^postTest1$',views.postTest1)
Title
def postTest2(request):
uname=request.POST['uname']
upwd=request.POST['upwd']
ugender=request.POST['ugender']
uhobby=request.POST.getlist('uhobby')
context={'uname':uname,'upwd':upwd,'ugender':ugender,'uhobby':uhobby}
return render(request,'booktest/postTest2.html',context)
url(r'^postTest2$',views.postTest2)
Title
{{ uname }}
{{ upwd }}
{{ ugender }}
{{ uhobby }}
#coding=utf-8
from django.http import HttpResponse
def index(request):
return HttpResponse('你好')
from django.http import HttpResponse
from django.template import RequestContext, loader
def index(request):
t1 = loader.get_template('polls/index.html')
context = RequestContext(request, {'h1': 'hello'})
return HttpResponse(t1.render(context))
属性
def getCookies(request):
# 后面再来的时候都会携带
response = HttpResponse()
if 'name' in request.COOKIES:
response.write(request.COOKIES['name'])
# 第一次的时候选择注入cookies
# response.set_cookie('name', 'mikjeing')
return response
分析:
http是无状态的,我们需要记录用户的信息,多次访问需要携带上一次的用户信息,有服务器发送cookies从resonse中给客户端保存在本地,但是服务器不存储这些信息,在有效时间内,同一域名下浏览器会默认带上cookies给服务器
简单的例子,当我们访问本地服务例如 127.0.0.7:8000/booktest/getCookies的时候是第一次,requestheader里面是找不到本地存储的cookies,因此不会有携带,但是服务器写了set_cookies,就会有respon里面带有cookies返回给客户端存储,刷新页面再次请求的时候,客户端带上cookies给服务端,就会在请求头带过去给服务器
上面的是最简单的设置例子,看下正常注册成功的时候把cookies的值回写
http://blog.51cto.com/suhaozhi/2063468
def login(request):
c_user = request.COOKIES.get('username')
if not c_user:
return redirect('/login/')
#如果没有从浏览器响应头中得到username对应的value,那么直接跳转回登录页面。
2.cookie回写。
if request.method == "GET":
return render(request,'login.html')
else:
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'admin' and pwd =='admin':
obj = redirect('/admin/')
obj.set_cookie('username','xxxx') ###为浏览器回写cookie!!key为username 对应的value为 xxx。
return obj
else:
return render(request,'login.html')
[28/Aug/2018 09:00:13] "GET /user/login/ HTTP/1.1" 200 6
{'csrftoken': 'fy3O8YWLMnlQwi7GfOR55aFvyhKJvzOBviQD4Phe3eMitk4Dd6OP5OYpUKIMPDM7', 'username': 'tiantian'}
[28/Aug/2018 09:00:22] "GET /user/login/ HTTP/1.1" 200 6
[28/Aug/2018 09:00:25] "GET /user/register/ HTTP/1.1" 200 3262
[28/Aug/2018 09:00:36] "GET /user/register/ HTTP/1.1" 200 3262
[28/Aug/2018 09:00:49] "GET /user/register_exit/?uname=wuliao HTTP/1.1" 200 12
[28/Aug/2018 09:00:56] "GET /user/register_exit/?uname=wuliao HTTP/1.1" 200 12
[28/Aug/2018 09:00:56] "POST /user/register_handle/ HTTP/1.1" 302 0
{'csrftoken': 'fy3O8YWLMnlQwi7GfOR55aFvyhKJvzOBviQD4Phe3eMitk4Dd6OP5OYpUKIMPDM7', 'username': 'wuliao'}
例如这两次注册的username的更改
1、设置cookie声明周期。
如果想在回写cookie时,可以给cookie加一个超时时间,就可以使用max_age参数。
例如:
obj.set_cookie('username','xxxx',max_age=10) ###为浏览器回写cookie!!key为username 对应的value为xxx,并且cookie的声明周期为10秒,10秒后自动消失。
2、设置cookie作用域。
如果需要设置cookie的作用域,可以通过response的set_cookie中的path参数去进行设置。
path='/' #代表对整个站点生效。
path='/p1' #代表对www.xxxx.com/p1/*站点生效。
还可以通过domain参数来设置,这个cookie对哪个域名生效。
默认为当前域名。
3、安全相关参数。
secure= False #默认值为False ,也就是关闭状态,当使用https时,需要开启。
httponly = False #默认值为False ,默认也是关闭状态,如果开启了httponly,那么这个cookie只能在http请求传输的时候可以被读取,
js是无法读取这个cookie的。
4、cookie的简单签名。
通过response回写cookie时。
obj.set_signed_cookie('kkk','vvv',salt='123456') #通过加盐的方式为cookie签名。
request.get_signed_cookie('kkk',salt='123456') #获取经过签名后的cookie值。
def redTest1(request):
return HttpResponseRedirect('/booktest/redTest2')
def redTest2(request):
return HttpResponse('我是转向后的页面资源')
app
from django.urls import path, re_path
from . import views
app_name = 'booktest'
urlpatterns = [
re_path(r'^redTest1$', views.redTest1, name='redTest1'),
re_path(r'^redTest2$', views.redTest2, name='redTest2'),
]
根
from django.contrib import admin
from django.urls import path, re_path, include
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^booktest/', include('booktest.urls', namespace='booktest'))
]
当我们输入以下的时候,会重定向
http://127.0.0.1:8000/booktest/redTest1
重定向
http://127.0.0.1:8000/booktest/redTest2
重定向推荐使用反向解析(需要namespace配置)
from django.urls import reverse
def redTest1(request):
# return HttpResponseRedirect('/booktest/redTest2')
# 重定向的两个方法
# return HttpResponseRedirect(reverse('booktest:redTest2'))
return redirect(reverse('booktest:redTest2'))
def redTest2(request):
return HttpResponse('我是转向后的页面资源')
from django.http import JsonResponse
def index2(requeset):
return JsonResponse({'list': 'abc'})
render
from django.shortcuts import render
def index(request):
return render(request, 'booktest/index.html', {'h1': 'hello'})
重定向(这里可以写全路径)
from django.shortcuts import redirect
from django.urls import reverse
def index(request):
return redirect(reverse('booktest:index2'))
def redTest1(request):
return redirect(reverse('booktest:redTest2'))
[21/Aug/2018 03:39:37] "GET /booktest/redTest1 HTTP/1.1" 302 0
[21/Aug/2018 03:39:37] "GET /booktest/redTest2 HTTP/1.1" 200 30
注:
任何简写的地方都可以写全路径例如 /booktest/redTest2
但是如果用reverse反向解析,需要用到命名空间namespace 两个都可以选择,后者更容易维护,不需要要在更改URL的情况下更改很多地方,直接自动反向解析url
经常看到网站有淘宝的商品cookies如何解释?
正常情况cookies是域名安全的,一开始我们用浏览器访问淘宝,打开很多商品,会被服务端包装到cookies传给客户端保存起来,那么如果继续访问淘宝,cookie会回传回去,就能知道对应的浏览记录然后进行分析和推荐,那为什么比如你访问博客网站的时候会有淘宝的广告,那其实是一个iframe,然后iframe里面嵌入了淘宝的域名下的链接,然后cookies就传过去了,拿出cookies信息解析展示推荐商品即可
启用session
项INSTALLED_APPS列表中添加:
'django.contrib.sessions',
项MIDDLEWARE_CLASSES列表中添加:
'django.contrib.sessions.middleware.SessionMiddleware',
使用session
urls.py
re_path(r'^mainTest$', views.mainTest, name='mainTest'),
re_path(r'^loginTest$', views.loginTest, name='login'),
re_path(r'^login_handle/$', views.login_handle, name='login_handle'),
re_path(r'^logoutTest/$', views.logoutTest, name='logoutTest'),
views.py
def mainTest(request):
username = request.session.get('name')
return render(request, 'booktest/mainTest.html', {'uname':username})
def loginTest(request):
return render(request, 'booktest/loginTest.html')
def login_handle(request):
request.session['name'] = request.POST['username']
request.session.set_expiry(0)
return redirect(reverse('booktest:mainTest'))
def logoutTest(request):
request.session.flush()
return redirect(reverse('booktest:mainTest'))
会话过期时间
set_expiry(value):设置会话的超时时间
如果没有指定,则两个星期后过期
如果value是一个整数,会话将在values秒没有活动后过期
若果value是一个imedelta对象,会话将在当前时间加上这个指定的日期/时间过期
如果value为0,那么用户会话的Cookie将在用户的浏览器关闭时过期
如果value为None,那么会话永不过期
修改视图中login_handle函数,查看效果
首先来撸一下逻辑
1.当我们一个页面请求的时候,服务端会通过request对象获取对应的session,如果没有,就会创建一个session,有的话就读,这就很好解释为什么通过request来获取,因为有可能带过来了,就不需要创建了,内部逻辑会判断
2.登录成功的时候一样直接获取session,这个就是OC里面的懒加载,每次都直接读,没有创建新的,这里的内部逻辑DJango做了,获取到之后赋值,然后返回登录页面,这个时候已经能从session获取到信息,就渲染登录之后的信息
3.第一次创建session后,服务器通过cookies返回给客户端,然后客户端再次访问的时候会携带cookie,服务端就能从cookies拿到对应的sessionid,进行资源查找
4.服务器退出登录的时候例如执行flush,会清楚session,在返回的cookies里面不返回sessionid,下次访问就需要重新创建分配了
退出登录的时候报文截图
session在DJango默认存储在配置的数据库中,我们配置的Mysql,看下具体表中的存储
mysql> show tables;
+----------------------------+
| Tables_in_test3 |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| bookinfo |
| booktest_heroinfo |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
12 rows in set (0.00 sec)
mysql> select *from django_session;
+----------------------------------+------------------------------------------------------------------------------------------------------+----------------------------+
| session_key | session_data | expire_date |
+----------------------------------+------------------------------------------------------------------------------------------------------+----------------------------+
| 1tj570h8vwlm7yynu0z0hwe2kz0r9pil | Y2ZmMzJlNzUzYjI4MjNkNmEwZTM2NTNhMzM4MzQyODRhMDJkYzZlYTp7Im5hbWUiOiIzMzMiLCJfc2Vzc2lvbl9leHBpcnkiOjB9 | 2018-09-04 07:07:14.852882 |
+----------------------------------+------------------------------------------------------------------------------------------------------+----------------------------+
1 row in set (0.00 sec)
配置Mysql情况下DJango是会把session数据存储到数据库里面,我们可以自己配置redis来提高性能
Mac安装redis服务
存储session
SESSION_ENGINE='django.contrib.sessions.backends.db'
SESSION_ENGINE='django.contrib.sessions.backends.cache'
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
redis存储
pip3 install django-redis-sessions
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 0
SESSION_REDIS_PASSWORD = ''
SESSION_REDIS_PREFIX = 'session'
Ubuntu
启动:sudo redis-server /etc/redis/redis.conf
停止:sudo redis-server stop
重启:sudo redis-server restart
Mac
启动:
brew services start redis
redis-server /usr/local/etc/redis.conf
停止:brew services stop redis
重启:brew services restart redis
redis-cli:使用客户端连接服务器
keys *:查看所有的键
get name:获取指定键的值
del name:删除指定名称的键
INFO keyspace 查看数据库
select index 选择数据库
CONFIG GET databases 查看数据库数量
配置好之后,我们还是执行上面的session登录操作,登录之后,报文中还是一样,可以下mysql和redis
可以看到session被存到的内存高性能redis里面去了,而Mysql不再存储。
DIRS=[os.path.join(BASE_DIR,"templates")]
模板处理
loader.get_template(template_name),返回一个Template对象
Template对象的render(RequestContext)方法,使用context渲染模板
from django.template import loader, RequestContext
from django.http import HttpResponse
def index(request):
tem = loader.get_template('temtest/index.html')
context = RequestContext(request, {})
return HttpResponse(tem.render(context))
快捷函数
from django.shortcuts import render
def index(request):
return render(request, 'temtest/index.html')
{{ variable }}
在模板中调用对象的方法
from django.db import models
class HeroInfo(models.Model):
...
def showName(self):
return self.hname
from django.shortcuts import render
from models import *
def index(request):
hero = HeroInfo(hname='abc')
context = {'hero': hero}
return render(request, 'temtest/detail.html', context)
{{hero.showName}}
{ %for ... in ...%}
循环逻辑
{{forloop.counter}}表示当前是第几次循环
{ %empty%}
给出的列表为或列表不存在时,执行此处
{ %endfor%}
{ %if ...%}
逻辑1
{ %elif ...%}
逻辑2
{ %else%}
逻辑3
{ %endif%}
{ % comment % }
多行注释
{ % endcomment % }
{ %include "foo/bar.html" % }
{ % url 'name' p1 p2 %}
{ % csrf_token %}
https://blog.csdn.net/xyp84/article/details/7945094
一、形式:小写
{{ name | lower }}
二、过滤器是可以嵌套的,字符串经过三个过滤器,第一个过滤器转换为小写,第二个过滤器输出首字母,第三个过滤器将首字母转换成大写
标签
{{ str|lower|first|upper }}
显示前30个字
{{ bio | truncatewords:"30" }}
格式化
{{ pub_date | date:"F j, Y" }}
过滤器列表
{{ 123|add:"5" }} 给value加上一个数值
{{ "AB'CD"|addslashes }} 单引号加上转义号,一般用于输出到javascript中
{{ "abcd"|capfirst }} 第一个字母大写
{{ "abcd"|center:"50" }} 输出指定长度的字符串,并把值对中
{{ "123spam456spam789"|cut:"spam" }} 查找删除指定字符串
{{ value|date:"F j, Y" }} 格式化日期
{{ value|default:"(N/A)" }} 值不存在,使用指定值
{{ value|default_if_none:"(N/A)" }} 值是None,使用指定值
{{ 列表变量|dictsort:"数字" }} 排序从小到大
{{ 列表变量|dictsortreversed:"数字" }} 排序从大到小
{% if 92|pisibleby:"2" %} 判断是否整除指定数字
{{ string|escape }} 转换为html实体
{{ 21984124|filesizeformat }} 以1024为基数,计算最大值,保留1位小数,增加可读性
{{ list|first }} 返回列表第一个元素
{{ "ik23hr&jqwh"|fix_ampersands }} &转为&
{{ 13.414121241|floatformat }} 保留1位小数,可为负数,几种形式
{{ 13.414121241|floatformat:"2" }} 保留2位小数
{{ 23456 |get_digit:"1" }} 从个位数开始截取指定位置的1个数字
{{ list|join:", " }} 用指定分隔符连接列表
{{ list|length }} 返回列表个数
{% if 列表|length_is:"3" %} 列表个数是否指定数值
{{ "ABCD"|linebreaks }} 用新行用
{% forloop.counter|divisibleby:"2" %} 表示是否被某个数整除
、
标记包裹
{{ "ABCD"|linebreaksbr }} 用新行用
标记包裹
{{ 变量|linenumbers }} 为变量中每一行加上行号
{{ "abcd"|ljust:"50" }} 把字符串在指定宽度中对左,其它用空格填充
{{ "ABCD"|lower }} 小写
{% for i in "1abc1"|make_list %}ABCDE,{% endfor %} 把字符串或数字的字符个数作为一个列表
{{ "abcdefghijklmnopqrstuvwxyz"|phone2numeric }} 把字符转为可以对应的数字??
{{ 列表或数字|pluralize }} 单词的复数形式,如列表字符串个数大于1,返回s,否则返回空串
{{ 列表或数字|pluralize:"es" }} 指定es
{{ 列表或数字|pluralize:"y,ies" }} 指定ies替换为y
{{ object|pprint }} 显示一个对象的值
{{ 列表|random }} 返回列表的随机一项
{{ string|removetags:"br p p" }} 删除字符串中指定html标记
{{ string|rjust:"50" }} 把字符串在指定宽度中对右,其它用空格填充
{{ 列表|slice:":2" }} 切片
{{ string|slugify }} 字符串中留下减号和下划线,其它符号删除,空格用减号替换
{{ 3|stringformat:"02i" }} 字符串格式,使用Python的字符串格式语法
{{ "EABCD"|striptags }} 剥去[X]HTML语法标记
{{ 时间变量|time:"P" }} 日期的时间部分格式
{{ datetime|timesince }} 给定日期到现在过去了多少时间
{{ datetime|timesince:"other_datetime" }} 两日期间过去了多少时间
{{ datetime|timeuntil }} 给定日期到现在过去了多少时间,与上面的区别在于2日期的前后位置。
{{ datetime|timeuntil:"other_datetime" }} 两日期间过去了多少时间
{{ "abdsadf"|title }} 首字母大写
{{ "A B C D E F"|truncatewords:"3" }} 截取指定个数的单词
{{ "111221"|truncatewords_html:"2" }} 截取指定个数的html标记,并补完整
{{ list|unordered_list }}
多重嵌套列表展现为html的无序列表
{{ string|upper }} 全部大写
linkageurl编码
{{ string|urlize }} 将URLs由纯文本变为可点击的链接。
{{ string|urlizetrunc:"30" }} 同上,多个截取字符数。
{{ "B C D E F"|wordcount }} 单词数
{{ "a b c d e f g h i j k"|wordwrap:"5" }} 每指定数量的字符就插入回车符
{{ boolean|yesno:"Yes,No,Perhaps" }} 对三种值的返回字符串,对应是 非空,空,None。
三、过滤器的参数
if list1|length > 1
name|lower|upper
list|join:", "
value|default:"什么也没有"
value|date:'Y-m-d'
{#...#}
{# { % if foo % }bar{ % else % } #}
DJango模板语言中
{ % url 'namespace:name' p1 p2 %}
重定向中
def login_handle(request):
request.session['name'] = request.POST['username']
request.session.set_expiry(0)
return redirect(reverse('booktest:mainTest'))
为什么要反向解析?
首先看个简单的例子
根url匹配
from django.contrib import admin
from django.urls import path, re_path, include
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^booktest/', include('booktest.urls', namespace='booktest'))
]
booktestapp里面url匹配
from django.urls import path, re_path
from . import views
app_name = 'booktest'
urlpatterns = [
re_path(r'^$', views.index, name='index'),
re_path(r'^(\d+)/$', views.show, name='show'),
]
views.py
index对应的文件
Index
展示