赞
踩
目录
cookies知识点详解
子类HttpResponseRedirect(注意namespace的配置)
该备忘录是基于以下版本开发
- 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]: <QuerySet [<BookInfo: 射雕银熊转>, <BookInfo: 水浒传>]>

- 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]: <QuerySet [<BookInfo: 射雕银熊转>, <BookInfo: 水浒传>, <BookInfo: 三国演义>]>
- In [11]: BookInfo.objects.get(pk=3)
- Out[11]: <BookInfo: 三国演义>
- 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]: <QuerySet [<HeroInfo: 郭靖>, <HeroInfo: 黄蓉>, <HeroInfo: 浪里白条>, <HeroInfo: 小李广>, <HeroInfo: 花和尚>]>
-
- 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]: <QuerySet [<BookInfo: 射雕转>, <BookInfo: 水浒传>, <BookInfo: 三国演义>]>
-
- 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]: <QuerySet [<HeroInfo: 郭靖>, <HeroInfo: 黄蓉>, <HeroInfo: 浪里白条>, <HeroInfo: 小李广>, <HeroInfo: 花和尚>, <HeroInfo: 霹雳火>]>

- In [28]: b2.heroinfo_set.all()
- Out[28]: <QuerySet [<HeroInfo: 浪里白条>, <HeroInfo: 小李广>, <HeroInfo: 花和尚>, <HeroInfo: 霹雳火>]>
- In [29]: h = b2.heroinfo_set.create(hero_name='豹子头',hero_gender=True,hero_con
- ...: tent="灵宠")
-
- In [30]: HeroInfo.objects.all()
- Out[30]: <QuerySet [<HeroInfo: 郭靖>, <HeroInfo: 黄蓉>, <HeroInfo: 浪里白条>, <HeroInfo: 小李广>, <HeroInfo: 花和尚>, <HeroInfo: 霹雳火>, <HeroInfo: 豹子头>]>
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('<h1>Hello Mikejing<h1>')
-
-
- 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模板
- <body>
-
-
- <ul>
- {%for book in lists%}
- <li><a href="/book/{{book.id}}">{{book.book_title}}</a></li>
- {%endfor%}
- </ul>
-
- </body>
注意:
这里a标签里面的href中写的路径有两个区别如下图,首先明确一点,无论哪种都是从头根部urls开启重新匹配
带/和不带的区别,一般写全路径都需要带上/
detail.html模板
- <body>
-
- <ul>
- {%for hero in lists%}
- <li>{{hero.hero_name}} {{hero.hero_gender}} {{hero.hero_content}}</li>
- {%endfor%}
- </ul>
- </body>
应用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
元选项
<app_name>_<model_name>
- 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]: <QuerySet [<BookInfo: BookInfo object (1)>, <BookInfo: BookInfo object (2)>, <BookInfo: BookInfo object (3)>, <BookInfo: BookInfo object (4)>]>
-
- 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)
- <ipython-input-2-182abd3174ec> in <module>()
- ----> 1 BookInfo.objects.all()
-
- AttributeError: type object 'BookInfo' has no attribute 'objects'
-
- In [3]: BookInfo.books.all()
- Out[3]: <QuerySet [<BookInfo: BookInfo object (1)>, <BookInfo: BookInfo object (2)>, <BookInfo: BookInfo object (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]: <QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 射雕英雄传>, <BookInfo: 射雕英雄传>, <BookInfo: 射雕英雄传>, <BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 天龙八部>, <BookInfo: 天龙八部>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 笑傲江湖>, <BookInfo: 笑傲江湖>, <BookInfo: 笑傲江湖>]>
这里我们第四种书雪山飞狐由于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<id>[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) 视图
- <!DOCTYPE html>
- <html>
- <head>
- <title></title>
- </head>
- <body>
- 找不到了
- <hr/>
- {{request_path}}
- </body>
- </html>
- 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),
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- 链接1:一个键传递一个值
- <a href="/getTest2/?a=1&b=2">gettest2</a><br>
- 链接2:一个键传递多个值
- <a href="/getTest3/?a=1&a=2&b=3">gettest3</a>
- </body>
- </html>
- def getTest2(request):
- a=request.GET['a']
- b=request.GET['b']
- context={'a':a,'b':b}
- return render(request,'booktest/getTest2.html',context)
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- a:{{ a }}<br>
- b:{{ b }}
- </body>
- </html>
- def getTest3(request):
- a=request.GET.getlist('a')
- b=request.GET['b']
- context={'a':a,'b':b}
- return render(request,'booktest/getTest3.html',context)
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- a:{% for item in a %}
- {{ item }}
- {% endfor %}
- <br>
- b:{{ b }}
- </body>
- </html>
- def postTest1(request):
- return render(request,'booktest/postTest1.html')
url(r'^postTest1$',views.postTest1)
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- <form method="post" action="/postTest2/">
- 姓名:<input type="text" name="uname"/><br>
- 密码:<input type="password" name="upwd"/><br>
- 性别:<input type="radio" name="ugender" value="1"/>男
- <input type="radio" name="ugender" value="0"/>女<br>
- 爱好:<input type="checkbox" name="uhobby" value="胸口碎大石"/>胸口碎大石
- <input type="checkbox" name="uhobby" value="跳楼"/>跳楼
- <input type="checkbox" name="uhobby" value="喝酒"/>喝酒
- <input type="checkbox" name="uhobby" value="爬山"/>爬山<br>
- <input type="submit" value="提交"/>
- </form>
- </body>
- </html>

- 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)
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- {{ uname }}<br>
- {{ upwd }}<br>
- {{ ugender }}<br>
- {{ uhobby }}
- </body>
- </html>
- #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来提高性能
存储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对应的文件
- <body>
- <h1>Index</h1>
-
- <a href="/booktest/123">展示</a>
- </body>
- </html>

这里面两个页面index页面展示一个跳转a标签,a标签写的路径是根路径,会替换url里面的路径,这么看来简单的跳转就没问题。但是如果咱们按下面改一下
- urlpatterns = [
- path('admin/', admin.site.urls),
- re_path(r'^', include('booktest.urls', namespace='booktest'))
- ]
当我们进入index页面的时候再点击a标签,路径还是带有booktest前缀,因此直接404了,外面改了,我们里面还是需要跟着改,这就非常的浪费时间了,而且维护起来很麻烦,因此就有了反向解析。
我们在根的urls下面都会有定义namespace,在app目录下的urls里面会给匹配的name,除此之外还需要在app的urls下面指定app_name
- 根部urls
-
- urlpatterns = [
- path('admin/', admin.site.urls),
- re_path(r'^booktest/', include('booktest.urls', namespace='booktest'))
- ]
-
- app里面urls
-
- app_name = 'booktest'
-
- urlpatterns = [
-
- re_path(r'^$', views.index, name='index'),
- re_path(r'^(\d+)/$', views.show, name='show'),
-
- ]
-
-
- views
- def index(request):
- return render(request, 'booktest/index.html')
- def show(request, p1):
- return render(request,'booktest/show.html', {'p1':p1})
-
- index页面信息
- <h1>Index</h1>
- # 这里的第一个参数是namespace 第二个参数是show正则里面()匹配的参数
- <a href="{% url 'booktest:show' '100000' %}">展示</a>
- </body>
- </html>

这里可以看到通过上面a标签的写法来指定反向解析,前提是booktest和show要先指定namespace和name,然后就可以通过语法进行反向url解析,不需要我们写死跳转路径了,views里面的重定向也是一样可以反向解析出路径,这样外部更改url,我们内部也无需再改
- { %block block_name%}
- 这里可以定义默认值
- 如果不定义默认值,则表示空字符串
- { %endblock%}
{ % extends "base.html" %}
- { %block block_name%}
- 实际填充内容
- { %endblock%}
说明
- { % block block_name %}
- 区域内容
- { % endblock block_name %}
三层继承结构
1.创建根级模板
- <!DOCTYPE html>
- <html>
- <head>
- <title>{%block title%}{%endblock%} 水果超市</title>
- </head>
- <body>
- top--{{logo}}
- <hr/>
- {%block left%}{%endblock%}
- {%block content%}{%endblock%}
- <hr/>
- bottom
- </body>
- </html>
2.创建分支模版
- {%extends 'temtest/base.html'%}
- {%block title%}商品{%endblock%}
- {%block left%}
- <h1>goods left</h1>
- {%endblock%}
- {%extends 'temtest/base.html'%}
- {%block title%}用户中心{%endblock%}
- {%block left%}
- <font color='blue'>user left</font>
- {%endblock%}
- {%extends 'temtest/base.html'%}
- {%block content%}
- 首页内容
- {%endblock content%}
3.为具体页面创建模板,继承自分支模板
- {%extends 'temtest/base_goods.html'%}
- {%block content%}
- 商品正文列表
- {%endblock content%}
- {%extends 'temtest/base_user.html'%}
- {%block content%}
- 用户密码修改
- {%endblock content%}
4.视图调用具体页面,并传递模板中需要的数据
- logo='welcome to itcast'
- def index(request):
- return render(request, 'temtest/index.html', {'logo': logo})
- def goodslist(request):
- return render(request, 'temtest/goodslist.html', {'logo': logo})
- def userpwd(request):
- return render(request, 'temtest/userpwd.html', {'logo': logo})
5.配置url
- from django.conf.urls import url
- from . import views
- urlpatterns = [
- url(r'^$', views.index, name='index'),
- url(r'^list/$', views.goodslist, name='list'),
- url(r'^pwd/$', views.userpwd, name='pwd'),
- ]
- 视图代码:
- def index(request):
- return render(request, 'temtest/index2.html',
- {
- 't1': '<h1>hello</h1>'
- })
- 模板代码:
- {{t1}}
会被自动转义的字符
- < 会转换为<
-
- > 会转换为>
-
- ' (单引号) 会转换为'
-
- " (双引号)会转换为 "
-
- & 会转换为 &
- {{t1|escape}}
关闭转义
- {{ data|safe }}
- {% autoescape off %}
- {{ body }}
- {% endautoescape %}
字符串字面值
- {{ data|default:"<b>123</b>" }}
{{ data|default:"<b>123</b>" }}
- def csrf1(request):
- return render(request,'booktest/csrf1.html')
- def csrf2(request):
- uname=request.POST['uname']
- return render(request,'booktest/csrf2.html',{'uname':uname})
- url(r'^csrf1/$', views.csrf1),
- url(r'^csrf2/$', views.csrf2),
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- <form method="post" action="/crsf2/">
- <input name="uname"><br>
- <input type="submit" value="提交"/>
- </form>
- </body>
- </html>
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- {{ uname }}
- </body>
- </html>
防csrf的使用
- <form>
- {% csrf_token %}
- ...
- </form>
取消保护
- from django.views.decorators.csrf import csrf_exempt
-
- @csrf_exempt
- def csrf2(request):
- uname=request.POST['uname']
- return render(request,'booktest/csrf2.html',{'uname':uname})
保护原理
<input type='hidden' name='csrfmiddlewaretoken' value='nGjAB3Md9ZSb4NmG1sXDolPmh3bR2g59' />
自己的域名下自己访问加了{% csrf_token%}表单隐藏添加了cookies中的csrftoken信息,由于开启了middleware,服务器会做一个token让cookie带回来,如果表单没有开启token验证,表单不会携带csrftoken,那就会返回403,如果加了token验证,那么表单里面会把token带过去,服务器拿到表单和cookies进行token比较就可以了。但是有个问题,其他域名下访问的时候,也是伪造了表单,然后把cookies里面的token值抓下来带过去了,这个时候就能跨域访问了,感觉DJango没有判断cookies里面有没有值,直接拿服务器的和表单的比较了,这样cookies被扒下来就能伪造了,因此验证码模式就出来了,更完善的模式
ImageFont表示字体对象,Mac的字体路径为”/System/Library/Fonts/“
- from PIL import Image, ImageDraw, ImageFont
- from io import BytesIO
-
- def verify(request):
- # 引入绘图模块
- from PIL import Image, ImageDraw, ImageFont
- # 引入随机函数模块
- import random
- # 定义变量,用于画面的背景色、宽、高
- bgcolor = (random.randrange(20, 100), random.randrange(
- 20, 100), 255)
- width = 100
- height = 25
- # 创建画面对象
- im = Image.new('RGB', (width, height), bgcolor)
- # 创建画笔对象
- draw = ImageDraw.Draw(im)
- # 调用画笔的point()函数绘制噪点
- for i in range(0, 100):
- xy = (random.randrange(0, width), random.randrange(0, height))
- fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
- draw.point(xy, fill=fill)
- # 定义验证码的备选值
- str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
- # 随机选取4个值作为验证码
- rand_str = ''
- for i in range(0, 4):
- rand_str += str1[random.randrange(0, len(str1))]
- # 构造字体对象 这里的路径是Mac的
- font = ImageFont.truetype('/System/Library/Fonts/AquaKana.ttc', 23)
- # 构造字体颜色
- fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
- # 绘制4个字
- draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
- draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
- draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
- draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
- # 释放画笔
- del draw
- # 存入session,用于做进一步验证
- request.session['verifycode'] = rand_str
- # 内存文件操作 python2和3有很大区别,这里用的3
- buf = BytesIO()
- # 将图片保存在内存中,文件类型为png 二进制 所以用BytesIO
- im.save(buf, 'png')
- # 将内存中的图片数据返回给客户端,MIME类型为图片png
- return HttpResponse(buf.getvalue(), 'image/png')

1.注意Python3和2的区别,这里我们导入的BytesIO,而不在是StringsIO了
2.Mac的路径注意填写
配置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'^verify$', views.verify, name='verify'),
- re_path(r'^verifycode$', views.verifycode, name='verifycode'),
-
- ]
显示验证码
- <h1>Index</h1>
- <a href="">{{random}}</a>
-
- <a href="{% url 'booktest:show' '100000' %}">展示</a>
- <br>
- <br>
- <br>
- <tr>
- <form action="{% url 'booktest:verifycode' %}" method="post">
- <input type="text" name="name">
- <img src="{% url 'booktest:verify' %}" alt="验证码" id="verify">
- <span id='verifycodeChange' onclick="changecode()">看不清,换一个</span>
- <input type="submit" value="提交">
- </form>
-
- </body>
-
- <script type="text/javascript">
-
- function changecode() {
- var i = Math.floor(Math.random() * (1000 - 1) + 1)
- document.getElementById("verify").src = "{% url 'booktest:verify' %}".concat('?a=', i)
- }
-
-
- </script>
-
- </html>

验证
- def verifycode(request):
- poststr = request.POST['name']
- vcodestr = request.session.get('verifycode', '')
- if poststr.upper() == vcodestr:
- return HttpResponse('OK')
- else:
- return HttpResponse('NO')
配置静态文件
- 这个值是可变的,例如给别人起一个外号,如果path里面路径由这个开头,就会从下面路径查找
-
- 127.0.0.1:8000/static/booktest/a1.jpg
-
- 匹配到static了,因此后面的/booktest/a1.jpg就会去STATICFILES_DIRS路径下查找
-
-
- STATIC_URL = '/static/'
- STATICFILES_DIRS = [
- os.path.join(BASE_DIR, 'static'),
- ]
-
-
- 如果改为
- STATIC_URL = '/mkj/'
- STATICFILES_DIRS = [
- os.path.join(BASE_DIR, 'static'),
- ]
-
- src=127.0.0.1:8000/static/booktest/a1.jpg这个时候,匹配不上mkj路径,因此显示不了
-
- 我们要写成src=127.0.0.1:8000/mkj/booktest/a1.jpg就好了,避免通过查看源代码知道物理结构,就弄了一个别名

test3(project名称)/static/booktest/
- 结构
- /static/my_app/myexample.jpg
-
-
- src下面写相对路径,第一个路径标识STATIC_URL,和setting中匹配上,说明读的静态文件,然后再去
- STATICFILES_DIRS中查找后面路径的值/booktest/a1.jpg 找到就显示
- <img src="/static/booktest/a1.jpg" alt="美女" width="100" height="100">
- 导入static标签
- { % load static from staticfiles %}
- <img src="{ % static "my_app/myexample.jpg" %}" alt="My image"/>
-
- 固定static写法,然后导入配置路径下的具体路径即可
- <img src="{ % static "booktest/a1.jpg" %}" alt="My image"/>
- from django.http import HttpResponse
- class MyException():
- def process_exception(request,response, exception):
- return HttpResponse(exception.message)
- MIDDLEWARE_CLASSES = (
- 'test1.myexception.MyException',
- ...
- )
pic=models.ImageField(upload_to='cars/')
pip3 install Pillow
MEDIA_ROOT=os.path.join(BASE_DIR,"static/media")
- <body>
- <h1>呵呵</h1>
- <form action="{% url 'booktest:uploadfile2' %}" method="post" enctype="multipart/form-data">
- {% csrf_token %}
- <input type="file" name="icon">
- <input type="submit" value="提交">
- </form>
- </body>
- </html>
视图代码
- def uploadFile1(request):
- return render(request, 'booktest/upload.html')
-
-
- def uploadFile2(request):
- # 注意是FILES
- pic = request.FILES['icon']
- # 全路径
- storepath = os.path.join(settings.MEDIA_ROOT, pic.name)
- # with是不需要手动释放内存 ft指针
- with open(storepath, 'wb') as ft:
- # 内存中图片读取
- for ct in pic.chunks():
- # 写入
- ft.write(ct)
- # 这里的staitc是标识setting文件下的路径,会自动拼接那个路径,你然后后面跟media/xxx.jpg即可
- return HttpResponse('<img src="/static/media/%s" alt="">'%pic.name)

- python manage.py createsuperuser
- 然后按提示填写用户名、邮箱、密码
- from django.contrib import admin
- from models import *
-
- admin.site.register(HeroInfo)
ModelAdmin对象
- class HeroAdmin(admin.ModelAdmin):
- ...
admin.site.register(HeroInfo,HeroAdmin)
- @admin.register(HeroInfo)
- class HeroAdmin(admin.ModelAdmin):
列表页选项
“操作选项”的位置
- class HeroAdmin(admin.ModelAdmin):
- actions_on_top = True
- actions_on_bottom = True
list_display
- 在models.py文件中
- from django.db import models
- from tinymce.models import HTMLField
- from django.utils.html import format_html
-
- class HeroInfo(models.Model):
- hname = models.CharField(max_length=10)
- hcontent = HTMLField()
- isDelete = models.BooleanField()
- def hContent(self):
- return format_html(self.hcontent)
-
- 在admin.py文件中
- class HeroAdmin(admin.ModelAdmin):
- list_display = ['hname', 'hContent']
- 在models.py中HeroInfo类的代码改为如下:
- def hContent(self):
- return format_html(self.hcontent)
- hContent.admin_order_field = 'hname'
- 在models.py中为HeroInfo类增加方法hName:
- def hName(self):
- return self.hname
- hName.short_description = '姓名'
- hContent.short_description = '内容'
-
- 在admin.py页中注册
- class HeroAdmin(admin.ModelAdmin):
- list_display = ['hName', 'hContent']
list_filter
- class HeroAdmin(admin.ModelAdmin):
- ...
- list_filter = ['hname', 'hcontent']
list_per_page
- class HeroAdmin(admin.ModelAdmin):
- ...
- list_per_page = 10
search_fields
- class HeroAdmin(admin.ModelAdmin):
- ...
- search_fields = ['hname']
增加与修改页选项
- class HeroAdmin(admin.ModelAdmin):
- ...
- fields = [('hname', 'hcontent')]
- class HeroAdmin(admin.ModelAdmin):
- ...
- fieldsets = (
- ('base', {'fields': ('hname')}),
- ('other', {'fields': ('hcontent')})
- )
InlineModelAdmin对象
- class HeroInline(admin.TabularInline):
- model = HeroInfo
- class BookAdmin(admin.ModelAdmin):
- inlines = [
- HeroInline,
- ]
1、路径前面的/问题
首先我们看下/开头的路径,以一个 127.0.0.1:8000/booktest/path1路径为例,当我们进入前面这个路径的views后,点击a标签,这个时候a标签如果不开启命名空间,正常情况下如果用href='path2',那么这个路径会变成
127.0.0.1:8000/booktest/path1/path2
如果href='/path2',路径会变成
127.0.0.1:8000/path2
如果是img标签,路径是指定静态资源的,首先看下配置
- STATIC_URL = '/static/'
- STATICFILES_DIRS = [
- os.path.join(BASE_DIR, 'static'),
- ]
如果这个时候img标签里面的src是 /static/booktest/a1.jpg的意思是,第一个路径的参数是匹配上面的STATIC_URL的,如果匹配上了,就去STATICFILES_DIRS下查找,查找内容就是booktest/a1.jpg,这就是静态资源查找的规则
2、路径后面的/问题
Django中的处理方法是提供了一个设置项,APPEND_SLASH
,这个设置的默认值是True。当APPEND_SLASH
为True的时候,如果一个URL没有匹配成功,那么Django会在这个URL尾部加上一个'/'并重新redirect(301)回来。如果设置为False,那没有匹配成功的时候就什么都不会做。
需要注意的是,由于采用了redirect的方式,所以POST数据可能会丢失。
例如两个请求
- 127.0.0.1:8000/booktest/hello
-
- 127.0.0.1:8000/booktest/hello/
在APPEND_SLASH默认为True的情况下,如果
如果设置了'hello',那么只有'hello'会匹配,'hello/'不会匹配。
如果设置了'hello/',那么'hello'和'hello/'都会匹配。hello没匹配到,自动加上尾斜杠,然后301继续匹配,这也是DJango的推荐方式
在APPEND_SLASH默认为False的情况下,写了上面就匹配什么,匹配不到也不会继续301重定向,
可以看到如果按着DJango的写法,一般匹配都会写上尾斜杠来匹配用户输入写或者没写的url
https://www.bbsmax.com/A/kjdwABD6JN/
Paginator对象
属性
方法
异常exception
Page对象
创建对象
属性
方法
视图
- def herolist(request,index):
- if index == '':
- index = 1
- list1 = HeroInfo.objects.all()
- paginator = Paginator(list1, 5)
- page = paginator.page(index)
-
-
- return render(request, 'booktest/hero.html', {'page': page,'lists':list1})
url
- urlpatterns = [
- re_path(r'^$', views.index, name='index'),
- re_path(r'^hero/(\d*)$', views.herolist, name='hero'),
- ]
模板
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <ul>
- {% for hero in page.object_list %}
- <li>{{hero.hname}}</li>
- {% endfor %}
- <br>
- {% for index in page.paginator.page_range %}
- {% if page.number == index%}
- {{index}}
- {% else %}
- <a href="/booktest/hero/{{index}}">{{index}}</a>
- {% endif %}
- {% endfor %}
- <!--总个数 {{page.count}}-->
- <br>
- <!--分了几页 {{page.num_pages}}-->
- <br>
- <!--页数数组 {{page.page_range}}-->
- </ul>
-
- </body>
- </html>

引入js文件
修改settings.py关于静态文件的设置
- # Static files (CSS, JavaScript, Images)
- # https://docs.djangoproject.com/en/2.1/howto/static-files/
-
- STATIC_URL = '/static/'
- STATICFILES_DIRS = [
- os.path.join(BASE_DIR, 'static'),
- ]
在models.py中定义模型
- class AreaInfo(models.Model):
- title = models.CharField(max_length=20)
- parentid = models.ForeignKey('self', null=True, blank=True,on_delete=models.CASCADE)
生成迁移
- python manage.py makemigrations
- python manage.py migrate
网上找一个sql脚本,然后用Navicat连接,然后把脚本跑入对应的数据库即可
views.py中编写视图
- # 选择首页
- def area(request):
- return render(request,'booktest/area.html')
-
- # 省请求json信息
- def areainfo(request, pid):
- data = AreaInfo.objects.filter(parentid__isnull=True).values('id', 'title')
-
- return JsonResponse({'result': list(data)})
- # 市和区请求json信息
- def cityInfo(request, cid):
-
- data = AreaInfo.objects.filter(parentid=cid).values('id', 'title')
-
- return JsonResponse({'result': list(data)})
如果配置了mysql,就是mysql,配置了redis就从redis,获取到的fiter数据是QuerySet的,不能直接给JsonResponse转换,下面是StackoverFlow的答案
https://stackoverflow.com/questions/30243101/return-queryset-as-json
Simplest solution without any additional framework:
- results = PCT.objects.filter(code__startswith='a').values('id', 'name')
- return JsonResponse({'results': list(results)})
returns
{'results': [{'id': 1, 'name': 'foo'}, ...]}
or if you only need the values:
- results = PCT.objects.filter(code__startswith='a').values_list('id', 'name')
- return JsonResponse({'results': list(results)})
returns
{'results': [[1, 'foo'], ...]}
在urls.py中配置urlconf
- re_path(r'^area/$', views.area, name='area'),
- re_path(r'^area/(\d+)/$', views.areainfo, name='areainfo'),
- re_path(r'^city/(\d+)/$', views.cityInfo, name='cityinfo'),
主urls.py中包含此应用的url
- urlpatterns = [
- path('admin/', admin.site.urls),
- re_path(r'^booktest/', include('booktest.urls', namespace='booktest'))
- ]
定义模板index.html
'DIRS': [os.path.join(BASE_DIR, 'templates')],
定义模板文件:包含三个select标签,分别存放省市区的信息
- <body>
- <!--选择器-->
- <select name="" id="pro">
- <option value="">请选择省</option>
- </select>
- <select name="" id="city">
- <option value="">请选择市</option>
- </select>
- <select name="" id="dis">
- <option value="">请选择区</option>
- </select>
-
- </body>
js代码
1.一进来请求省信息,get,直接执行
2.点击之后触发请求下一级
- <script src="/static/booktest/jquery-3.3.1.min.js"></script>
- <script>
- $(function () {
- //获取省
- pro = $('#pro')
-
- city = $('#city')
-
- dis = $('#dis')
- // get请求
- $.get('/booktest/area/0/', function (data) {
- $.each(data.result, function (index, item) {
- // console.log(index)
- pro.append('<option value="' + item.id + '">' + item.title + '</option>')
- })
- })
-
- // 获取市
- $('#pro').change(function () {
-
- // 选择当前城市的id
- console.log('/booktest/city/' + ($(this).val() || "0") + '/')
- $.get('/booktest/city/' + ($(this).val() || "0") + '/', function (data) {
- city.empty().append('<option value="">请选择市</option>')
- dis.empty().append('<option value="">请选择市</option>')
- $.each(data.result, function (index, item) {
- city.append('<option value="' + item.id + '">' + item.title + '</option>')
- })
- })
-
- })
-
- $('#city').change(function () {
- $.get('/booktest/city/' + ($(this).val() || "0") + '/', function (data) {
- dis.empty().append('<option value="">请选择区</option>')
- $.each(data.result, function (index, item) {
- dis.append('<option value="' + item.id + '">' + item.title + '</option>')
- })
- })
- })
-
-
- })
- </script>

1.找个目录新建个文件夹放项目(例如桌面)
2.打开终端,cd到目录下,执行
- django-admin startproject taobao
-
-
- cd taobao/
-
- .
- ├── manage.py
- └── taobao
- ├── __init__.py
- ├── settings.py
- ├── urls.py
- └── wsgi.py
-
- 1 directory, 5 files
3.配置静态文件
先创建静态文件夹,static和templates,都在根级,和manage.py同级,在static中放静态文件
添加templates路径和static路径在根级的setting.py里面
- TEMPLATES = [
- {
- 'DIRS': [os.path.join(BASE_DIR,'templates')], // 添加路径
- ......
- },
- ]
-
- # Static files (CSS, JavaScript, Images)
- # https://docs.djangoproject.com/en/2.1/howto/static-files/
-
- STATIC_URL = '/static/'
-
- STATICFILES_DIRS=[
- os.path.join(BASE_DIR, 'static')
- ]
修改默认的sqllite为mysql
- # Database
- # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
-
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'taobao',
- 'USER': 'root',
- 'PASSWORD': 'mikejing',
- 'HOST': 'localhost',
- 'PORT': '3306'
-
- }
- }
我们指定名字NAME为taobao的databases没有的话就需要自己先创建好,不然后面生成的models找不到数据库无法生成表
- mysql> show databases;
- +--------------------+
- | Database |
- +--------------------+
- | information_schema |
- | mysql |
- | performance_schema |
- | sys |
- | test2 |
- | test3 |
- +--------------------+
- 6 rows in set (0.00 sec)
-
- mysql> create database taobao charset=utf8;
- Query OK, 1 row affected, 1 warning (0.05 sec)
-
- mysql> show databases;
- +--------------------+
- | Database |
- +--------------------+
- | information_schema |
- | mysql |
- | performance_schema |
- | sys |
- | taobao |
- | test2 |
- | test3 |
- +--------------------+
- 7 rows in set (0.00 sec)

4.在taobao项目中创建app模块(用户模块为例)以及迁移
python3 manage.py startapp userinfo
在models.py里面创建模型类
- from django.db import models
-
-
- class UserInfo(models.Model):
- username = models.CharField(max_length=20)
- userpwd = models.CharField(max_length=40)
- useremail = models.CharField(max_length=30)
- usershow = models.CharField(max_length=20, default='')
- useraddress = models.CharField(max_length=100, default='')
- useryoubian = models.CharField(max_length=6, default='')
- userphone = models.CharField(max_length=11, default='')
在根级settings里面添加apps
- INSTALLED_APPS = [
- ......
- 'userinfo',
- ]
注意:当你修改成Mysql为数据库之后,由于不在是2.7 python,我们用的是3.6 python针对Mysql的包名都不同,直接执行直接报错,因此需要在根级taobao__init__文件中加入
- import pymysql
- pymysql.install_as_MySQLdb()
然后生成迁移脚本(sql语句而已 userinfo/migrations/0001_initial.py文件中),执行迁移 (执行)
- mintoudeMacBook-Pro-2:taobao mintou$ python3 manage.py makemigrations
- Migrations for 'userinfo':
- userinfo/migrations/0001_initial.py
- - Create model UserInfo
- mintoudeMacBook-Pro-2:taobao mintou$ python3 manage.py migrate
成功之后打开mysql查看即可
- mysql> desc userinfo_userinfo;
- +-------------+--------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +-------------+--------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | username | varchar(20) | NO | | NULL | |
- | userpwd | varchar(40) | NO | | NULL | |
- | useremail | varchar(30) | NO | | NULL | |
- | usershow | varchar(20) | NO | | NULL | |
- | useraddress | varchar(100) | NO | | NULL | |
- | useryoubian | varchar(6) | NO | | NULL | |
- | userphone | varchar(11) | NO | | NULL | |
- +-------------+--------------+------+-----+---------+----------------+
- 8 rows in set (0.00 sec)
-
- mysql>

5.根据模块导入templates模板文件
首先根据模块在templates里面新建app对应的目录,然后html文件导入
6.编写views.py
- from django.shortcuts import render
-
-
- # Create your views here.
-
- def register(request):
- return render(request, 'userinfo/register.html')
7.配置urls即可访问
根级
- from django.contrib import admin
- from django.urls import path, re_path, include
-
- urlpatterns = [
- path('admin/', admin.site.urls),
- re_path(r'^user/', include('userinfo.urls', namespace='userinfo'))
- ]
模块级
- from django.urls import path, re_path
- from . import views
-
- app_name = 'userinfo'
-
-
- urlpatterns = [
- re_path(r'^register/', views.register, name='register')
- ]
Python的WSGI
最近在学习使用Python进行WebServer的编程,发现WSGI(Web Server Gateway Interface)的概念。PythonWeb服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是Python应用程序或框架和Web服务器之间的一种接口,已经被广泛接受,它已基本达成它的可移植性方面的目标。WSGI 没有官方的实现,因为WSGI更像一个协议。只要遵照这些协议,WSGI应用(Application)都可以在任何服务器(Server)上运行,反之亦然。
如果没有WSGI,你选择的Python网络框架将会限制所能够使用的 Web 服务器。
这就意味着,你基本上只能使用能够正常运行的服务器与框架组合,而不能选择你希望使用的服务器或框架。
那么,你怎样确保可以在不修改 Web 服务器代码或网络框架代码的前提下,使用自己选择的服务器,并且匹配多个不同的网络框架呢?为了解决这个问题,就出现了PythonWeb 服务器网关接口(Web Server Gateway Interface,WSGI)。
WSGI的出现,让开发者可以将网络框架与 Web 服务器的选择分隔开来,不再相互限制。现在,你可以真正地将不同的 Web 服务器与网络开发框架进行混合搭配,选择满足自己需求的组合。例如,你可以使用Gunicorn或Nginx/uWSGI或Waitress服务器来运行Django、Flask或Pyramid应用。正是由于服务器和框架均支持WSGI,才真正得以实现二者之间的自由混合搭配。
Java的Servlet API
下面将类比Java来说明一下:
如果没有Java Servlet API,你选择的Java Web容器(Java Socket编程框架实现)将会限制所能够使用的Java Web框架(因为没有Java Servlet API,那么SpringMVC可能会实现一套SpringMVCHttpRequest和SpringMVCHttpResponse标准,Struts2可能会实现一套Struts2HttpRequest和Struts2HttpResponse标准,如果Tomcat只支持SpringMVC的API,那么选择Tomcat服务器就只能使用SpringMVC的Web框架来写服务端代码)。
这就意味着,你基本上只能使用能够正常运行的服务器(Tomcat)与框架(SpringMVC)组合,而不能选择你希望使用的服务器或框架(比如:我要换成Tomcat + Struts2的组合)。
注意:这里假设没有Java Servlet API,这样就相当于SpringMVC和Struts2可能都要自己实现一套Servlet封装HttpRequest和HttpResponse,这样从SpringMVC更换成Struts2就几乎需要重写服务器端的代码。为了解决这个问题,Java提出了Java Servlet API协议,让所有的Web服务框架都实现此Java Servlet API协议来和Java Web服务器(例如:Tomcat)交互,而复杂的网络连接控制等等都交由Java Web服务器来控制,Java Web服务器用Java Socket编程实现了复杂的网络连接管理。
详细说说Python的WSGI
Python Web 开发中,服务端程序可以分为两个部分,一是服务器程序,二是应用程序。前者负责把客户端请求接收,整理,后者负责具体的逻辑处理。为了方便应用程序的开发,我们把常用的功能封装起来,成为各种Web开发框架,例如 Django, Flask, Tornado。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
- 服务器端:
服务器必须将可迭代对象的内容传递给客户端,可迭代对象会产生bytestrings,必须完全完成每个bytestring后才能请求下一个。
- 应用程序:
服务器程序会在每次客户端的请求传来时,调用我们写好的应用程序,并将处理好的结果返回给客户端。
总结:
- Web Server Gateway Interface是Python编写Web业务统一接口。
- 无论多么复杂的Web应用程序,入口都是一个WSGI处理函数。
- Web应用程序就是写一个WSGI的处理函数,主要功能在于交互式地浏览和修改数据,生成动态Web内容,针对每个HTTP请求进行响应。
实现Python的Web应用程序能被访问的方式
要使 Python 写的程序能在 Web 上被访问,还需要搭建一个支持 Python 的 HTTP 服务器(也就是实现了WSGI server(WSGI协议)的Http服务器)。有如下几种方式:
- 可以自己使用Python Socket编程实现一个Http服务器
- 使用支持Python的开源的Http服务器(如:uWSGI,wsgiref,Mod_WSGI等等)。如果是使用Nginx,Apache,Lighttpd等Http服务器需要单独安装支持WSGI server的模块插件。
- 使用Python开源Web框架(如:Flask,Django等等)内置的Http服务器(Django自带的WSGI Server,一般测试使用)
Python标准库对WSGI的实现
wsgiref 是Python标准库给出的 WSGI 的参考实现。simple_server 这一模块实现了一个简单的 HTTP 服务器。
Python源码中的wsgiref的simple_server.py正好说明上面的分工情况,server的主要作用是接受client的请求,並把的收到的请求交給RequestHandlerClass处理,RequestHandlerClass处理完成后回传结果给client
uWSGI服务器
uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。注意uwsgi是一种通信协议,而uWSGI是实现uwsgi协议和WSGI协议的Web服务器。
Django框架内置的WSGI Server服务器
Django的WSGIServer继承自wsgiref.simple_server.WSGIServer,而WSGIRequestHandler继承自wsgiref.simple_server.WSGIRequestHandler
之前说到的application,在Django中一般是django.core.handlers.wsgi.WSGIHandler对象,WSGIHandler继承自django.core.handlers.base.BaseHandler,这个是Django处理request的核心逻辑,它会创建一个WSGIRequest实例,而WSGIRequest是从http.HttpRequest继承而来
Python和Java的类比
Python和Java的服务器结构
- 独立WSGI server(实现了Http服务器功能) + Python Web应用程序
- 例如:Gunicorn,uWSGI + Django,Flask
- 独立Servlet引擎(Java应用服务器)(实现了Http服务器功能) + Java Web应用程序
- 例如:Jetty,Tomcat + SpringMVC,Struts2
Python和Java服务器共同点
WSGI server(例如Gunicorn和uWSGI)
- WSGI server服务器内部都有组建来实现Socket连接的创建和管理。
- WSGI server服务器都实现了Http服务器功能,能接受Http请求,并且通过Python Web应用程序处理之后返回动态Web内容。
Java应用服务器(Jetty和Tomcat)
- Java应用服务器内部都有Connector组件来实现Socket连接的创建和管理。
- Java应用服务器都实现了Http服务器功能,能接受Http请求,并且通过Java Web应用程序处理之后返回动态Web内容。
以上这段参考链接
另外写了一篇部署到阿里云
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。