Django
一、Django
二、Django-ninja
------------------------------------------------------------------------------------------
Django是Python下的一款网络服务器框架。Python下有许多款不同的框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。
安装Django:pip install django。本例中所用为Django 3.1.2。
一、启动
1、新建项目:django-admin startproject mysite
在当前目录下,将生成mysite文件夹。其文件树结构如下:
mysite ├── manage.py └── mysite ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py
2、启动服务器
进入mysite启动服务器:python manage.py runserver 8000 #如果不说明那么端口号默认为8000。
3、访问
打开浏览器访问http://127.0.0.1:8000,可以看到服务器已经在运行:虽然有一个能跑的服务器,但什么内容都没有。
二、第一个网页(以上面的项目为例)
Django采用的MVC结构,我们需要将URL对应分配给某个对象处理,这需要通过修改mysite/mysite/urls.py来完成,如下,将根目录的URL分配给了mysite.views.hello对象进行处理。
此时用以处理HTTP请求的这一对象还不存在,我们需要在mysite/mysite下创建views.py,并在其中定义hello函数,hello函数的功能是返回http回复,即这里的<p><h1>世界你好</h1></p>。hello有一个参数request,该参数包含有请求的具体信息,比如请求的类型等,这里并没有用到。
# -*- coding: utf-8 -*- from django.http import HttpResponse def hello(request): return HttpResponse("<p><h1>世界你好</h1></p>")
页面效果如下:
三、增加app
一个网站可能有多个功能。我们可以在Django下以app为单位进行模块化的管理,而不是将所有的东西都丢到一个文件夹中。
1、在mysite项目下创建新的app:
python manage.py startapp west
我们的根目录下出现了一个新的叫做west的文件夹。
2、我们需要修改项目设置,说明我们要使用west。在mysite/mysite/setting.py的INSTALLED_APPS中,增加"west":
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'west', )
可以看到,除了新增加的west,Django已经默认加载了一些功能性的app,比如用户验证、会话管理、显示静态文件等。我们将在以后讲解它们的用途。
3、我们下面为APP增加首页。
首先,修改mysite/mysite/urls.py。最后一行是说明对于west/的访问要参考west/urls.py。
为了去耦合实现模块化,我们在mysite/west/目录下新建urls.py,我们在mysite/west/urls.py中添加如下内容,将URL对应到west下的views.py的mypage函数。
最后,在mysite/west下修改views.py为:
# -*- coding: utf-8 -*- from django.http import HttpResponse def mypage(request): return HttpResponse("<p>第一个应用页面</p>")
输入命令: python manage.py runserver 8000 。然后在浏览器访问http://127.0.0.1:8000/west,查看效果。
四、自带的admin应用
使用自带admin应用
1、创建一个django项目
django-admin startproject mytest
django中自带的就有一个数据库:db.sqlite3。首次运行后就会出现该文件。
2、创建一个子应用
进入到django项目的根目录(mytest)。执行命令:python manage.py startapp myapp
3、创建完app后在setting.py中把你的app添加在配置文件中
4、在命令行中迁移django自带的表
python manage.py migrate
5、创建一个admin管理后台
python manage.py createsuperuser
6、运行
输入命令: python manage.py runserver 8000 。然后在浏览器中输入http://127.0.0.1:8000/admin/
在自带admin中添加模块
1、打开app下面的models.py进行创建表
from django.db import models
#creat your models here
class The_user(models.Model):
username=models.CharField(max_length=30)
password=models.CharField(max_length=255)
class Meta:
berbose_name_plural = '测试用户'
def __str__(self):
return self.username
2、生成迁移文件:
python manage.py makemigrations
3、执行数据库迁移:
python manage.py migrate
4、如果想在admin页面上看到表
打开app下的admin.py 导入你的models.py文件
from 子项目名 import models
admin.site.register(models.表名)
5、运行
输入命令: python manage.py runserver 8000 。然后在浏览器中输入http://127.0.0.1:8000/admin/
五、连接数据库--sqlite3
Django默认采用了sqlite3,sqlite3是一种轻型的嵌入式数据库引擎,占用资源非常低,处理速度比mysql还快。它的缺点在并发大的时候,会有锁的风险。但小的网站项目使用sqlite3足以。
1、新建一个项目和一个app:
首先,新建一个项目:django-admin.py startproject my_test
然后,新建一个app:python manage.py startapp datas
2、设置my_test/my_test/setting.py文件
在INSTALLED_APPS中,增加"datas"
3、修改my_test/my_test/urls.py
4、设置models.py文件
设置my_test/datas/models.py文件,在文件里新建的类就是一张表:
表字段声明之类可参考博客:https://www.cnblogs.com/yangmv/p/5327477.html
如新建Character表:
5、将table同步到数据库
生成迁移文件:python manage.py makemigrations
同步到数据库:python manage.py migrate
datas库中有改动需更新时: python manage.py makegrations datas
6、插入数据
数据模型(表)已建立了,但还没有数据输入。我们打开sqlite3并切换到相应数据库,添加记录(需电脑安装了sqlite3):
INSERT INTO west_character (name) Values ('Vamei'); INSERT INTO west_character (name) Values ('Django'); INSERT INTO west_character (name) Values ('John');
7、从数据库中取数据
设置my_test/datas/views.py,对于对应的请求我们将从数据库中读取数据,然后返回给客户端,
我们从west.models中引入了Character类。通过操作该类,我们可以读取表格中的记录
8、增加url导航
为了让http请求能找到上面的程序,在my_test/datas//urls.py增加url导航:
9、运行服务器
输入命令: python manage.py runserver 8000 。然后在浏览器中输入URL:127.0.0.1:8000/west/staff
从数据库读出数据,显示在页面
六、连接数据库--MySQL
Django为多种数据库后台提供了统一的调用API。根据需求不同,Django可以选择不同的数据库后台。MySQL算是最常用的数据库。我们这里将Django和MySQL连接。
1、在MySQL中创立Django项目的数据库
在Linux终端下启动mysql:$mysql -u root -p
在MySQL中创立Django项目的数据库:mysql> CREATE DATABASE villa DEFAULT CHARSET=utf8;
2、在MySQL中为Django项目创立用户,并授予相关权限:
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX,
ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON
villa.* TO 'vamei'@'localhost' IDENTIFIED BY 'vameiisgood';
3、在settings.py中更改DATABASES对象
Django根据这一设置,与MySQL中相应的数据库和用户连接起来。此后Django就可以在数据库中读写了。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'villa', 'USER': 'vamei', 'PASSWORD': 'vameiisgood', 'HOST':'localhost', 'PORT':'3306', } }
4、创建数据模型(表)
在Django下我们不用直接编写SQL语句。在传统的MySQL中,数据模型是表。在Django下,一个表为一个类。表的每一列是该类的一个属性。我们可以使用基于对象的方法来操纵MySQL数据库。在models.py中,我们创建一个只有一列的表,即只有一个属性的类:
from django.db import models class Character(models.Model): name = models.CharField(max_length=200) #name属性是字符类型,最大长度为200 def __unicode__(self): return self.name
5、同步数据库
Django根据models.py中描述的数据模型在MySQL中真正的创建各个关系表,通过如下命令可以同步数据库,同步后Django将建立相关的MySQL表格,并要求你创建一个超级用户,按要求设置用户和密码即可:
$python manage.py migrate
6、查看数据模型
数据模型建立了,打开MySQL命令行(超级用户vamei),查看数据模型::
$mysql -u vamei -p
USE villa;
SHOW TABLES;
SHOW COLUMNS FROM west_character;
最后一个命令返回Character类的对应表格,可以看到,Django还自动增加了一个id列,作为记录的主键(Primary Key)。:
7、插入数据
数据模型(表)已建立了,但还没有数据输入。为了简便,我们手动添加记录。打开MySQL命令行,并切换到相应数据库。添加记录:
INSERT INTO west_character (name) Values ('Vamei'); INSERT INTO west_character (name) Values ('Django'); INSERT INTO west_character (name) Values ('John');
8、从数据库中取数据
在west/views.py中添加视图。对于对应的请求我们将从数据库中读取数据,然后返回给客户端,
我们从west.models中引入了Character类。通过操作该类,我们可以读取表格中的记录
# -*- coding: utf-8 -*- from django.http import HttpResponse from west.models import Character def staff(request): staff_list = Character.objects.all() staff_str = map(str, staff_list) return HttpResponse("<p>" + ' '.join(staff_str) + "</p>")
9、增加url导航
为了让http请求能找到上面的程序,在west/urls.py增加url导航:
from django.urls import path
from . import views
urlpatterns = [
path('staff/', views.staff),
]
10、运行服务器
输入命令: python manage.py runserver 8000 。然后在浏览器中输入URL:127.0.0.1:8000/west/staff
从数据库读出数据,显示在页面
七、使用模板
在之前的程序中,我们直接生成一个字符串,作为http回复,返回给客户端。这一过程中使用了django.http.HttpResponse()。这种回复实际上将数据和视图的格式混合了到上面的字符串中。看似方便却为我们的管理带来困难。如果可以把数据和视图格式分离就可以重复使用同一视图格式了。Django中自带的模板系统,可以将视图格式分离出来作为模板使用。
1、新建一个项目和一个app:
首先,新建一个项目:django-admin.py startproject my_test
然后,新建一个app:python manage.py startapp west
2、设置my_test/my_test/setting.py文件
在INSTALLED_APPS中,增加"west"
3、修改my_test/my_test/urls.py
为了去耦合实现模块化,我们在my_test/west/目录下新建了urls.py,否则对my_test/my_test/urls.py可进行如下配置(推荐使用去耦合的方式);
4、设置模板
在my_test目录下新建templates目录,并在其中建立templay.html文件作为模板(my_test/templates/templay.html)。templay.html文件的内容如下,这个文件中双括号包起来的就是我们未来数据要出现的地方。而相关的格式控制即<h1>标签。
(本例不需要)有时,我们需要向Django说明模板文件的搜索路径,在my_test/my_test/settings.py添加如下内容,如果还有其它的路径用于存放模板也可以增加该元组中的元素,以便Django可以找到需要的模板。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR, "/templates/west/",], # 需修改的位置
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
5、设置views
我们现在修改my_test/west/views.py,增加一个新的对象,用于向模板提交数据:
from django.shortcuts import render def templay(request): context = {} context['label'] = 'Hello World!' return render(request, 'templay.html', context)
这里我们使用render来替代之前使用的HttpResponse。render中context作为参数,这就是我们的数据。该context中的‘label’元素值会填上模板里,构成一个完整的http回复。
整个流程是:west/views.py中的templay()在返回时,将环境数据context传递给模板templay.html。这一模板系统可以与Django的其它功能相互合作。
6、增加url导航
为了让http请求能找到上面的程序,在my_test/west/urls.py增加url导航:
7、输入命令: python manage.py runserver 8000 。然后访问http://127.0.0.1:8000/west/templay,可以看到页面:
八、
循环与选择
Django实际上提供了丰富的模板语言,我们下面体验一下最常见的循环与选择。
在使用sqlite3的例子中,staff中的数据实际上是一个数据容器,有三个元素。刚才我们将三个元素连接成一个字符串传送。利用模板语言我们可以直接传送数据容器本身,再循环显示。修改staff()为:
def staff(request): staff_list = Character.objects.all() return render(request, 'templay.html', {'staffs': staff_list.values_list()})
从数据库中查询到的三个对象都在staff_list中,我们直接将staff_list传送给模板。(为sqlite3的例子中加入模板)将模板templay.html修改为:
{% for item in staffs %} <p>{{ item.id }}, {{item}}</p> {% endfor %}
我们以类似于Python中for循环的方式来定义模板中的for,以显示staffs中的每个元素。
输入命令: python manage.py runserver 8000 。然后访问http://127.0.0.1:8000/west/staff,可以看到页面:
模板继承
模板可以用继承的方式来实现复用。我们首先新建templates/base.html:
<html> <head> <title>templay</title> </head> <body> <h1>come from base.html</h1> {% block mainbody %} <p>original</p> {% endblock %} </body> </html>
我们在下面的templates/templay.html中继承base.html并替换只替换掉特定的部分(base.html页面中名为mainbody的block标签是可以被继承者们替换掉的部分):
{% extends "base.html" %} {% block mainbody %} {% for item in staffs %} <p>{{ item.id }},{{ item.name }}</p> {% endfor %} {% endblock %}
第一句说明templay.html继承自base.html。这里用相同名字的block标签替换base.html相应的block。
输入命令: python manage.py runserver 8000 。然后访问http://127.0.0.1:8000/west/staff,可以看到页面:
html表格
HTML文件中可以包含表格标签。HTML表格的目的是帮助用户构成HTTP请求,把数据用GET或者POST的方法,传递给某一URL地址。下面是一个表格的例子,这里的form标签有两个属性。action用于说明URL地址,method说明请求的方法。表格中还包含有两个input标签,即两个输入栏目。根据type的不同,第一个为一个文本框,第二个为一个提交按钮。name为输入栏的名字。服务器在解析数据时,将以name为索引。
<form action="/west/investigate/" method="get"> <input type="text" name="staff"> <input type="submit" value="Submit"> </form>
我们可以将上面的表格直接存入模板form.html,设置urls.py,让对/west/form/的访问指向该视图。
在west/views.py中定义一个视图form()来显示表格。我们再定义investigate()来处理该表格提交的数据。表格是通过GET方法提交的,我们可以通过request.GET['staff']来获得name为staff的输入字符串数据,investigate()将直接显示该字符串。
from django.shortcuts import render def form(request): return render(request, 'form.html')
def investigate(request): rlt = request.GET['staff'] return HttpResponse(rlt)
设置urls.py,让该处理函数对应action的URL([site]/west/investigate/)。
访问http://127.0.0.1:8000/west/form,提交表格后,字符串在页面上显示出来。
POST方法
上面我们使用了GET方法。视图显示和请求处理分成两个函数处理。其实提交数据时更常用POST方法。下面我们使用POST方法,并用一个URL和处理函数同时显示视图和处理请求。
先创建模板investigate.html。在模板的末尾我们增加一个rlt记号为表格处理结果预留位置。表格后面还有一个{% csrf_token %}的标签。csrf是Django提供的防止伪装提交请求的功能。POST方法提交的表格必须有此标签。
<form action="/west/investigate/" method="post"> {% csrf_token %} <input type="text" name="staff"> <input type="submit" value="Submit"> </form> <p>{{ rlt }}</p>
在west/views.py中用investigate()来处理表格。这里的csrf()是和上面的{% csrf_token %}对应。if语句,当是POST方法时,额外的处理,即提取表格中的数据到环境变量。
from django.shortcuts import render from django.template.context_processors import csrf
def investigate(request): ctx ={} ctx.update(csrf(request)) if request.POST: ctx['rlt'] = request.POST['staff'] return render(request, "investigate.html", ctx)
访问http://127.0.0.1:8000/west/investigate:
存储数据
我们还可以将客户提交的数据存入数据库。我们将客户提交的字符串存入前面的模型Character中。
修改west/views.py的investigate(),在POST的处理部分,我们调用Character类创建新的对象并让该对象的属性name等于用户提交的字符串,通过save()方法我们让该记录入库。随后我们从数据库中读出所有的对象,并传递给模板。
from django.shortcuts import render from django.core.context_processors import csrf from west.models import Character def investigate(request): if request.POST: submitted = request.POST['staff'] new_record = Character(name = submitted) new_record.save() ctx ={} ctx.update(csrf(request)) all_records = Character.objects.all() ctx['staff'] = all_records return render(request, "investigate.html", ctx)
修改模板investigate.html,我们使用模板语言的for,来显示所有的记录:
<form action="/west/investigate/" method="post"> {% csrf_token %} <input type="text" name="staff"> <input type="submit" value="Submit"> </form> {% for person in staff %} <p>{{ person }}</p> {% endfor %}
访问http://127.0.0.1:8000/west/investigate:
表格对象
客户提交数据后,服务器往往需要对数据做一些处理。比如检验数据看是否符合预期的长度和数据类型。在必要的时候还需要对数据进行转换,比如从字符串转换成整数。这些过程通常都相当的繁琐。Django提供的数据对象可以大大简化这一过程,这样Django在获得数据后,可以自动根据该表格对象的要求对数据进行处理。
修改west/views.py。我们定义了CharacterForm类,并通过属性name说明了输入栏name的类型为字符串最大长度为200。在investigate()函数中,我们根据POST直接创立form对象。该对象可以直接判断输入是否有效并对输入进行预处理。空白输入被视为无效。后面,我们再次创建一个空的form对象,并将它交给模板显示。
from django.shortcuts import render from django.core.context_processors import csrf from west.models import Character from django import forms class CharacterForm(forms.Form): name = forms.CharField(max_length = 200) def investigate(request): if request.POST: form = CharacterForm(request.POST) if form.is_valid(): submitted = form.cleaned_data['name'] new_record = Character(name = submitted) new_record.save() form = CharacterForm() ctx ={} ctx.update(csrf(request)) all_records = Character.objects.all() ctx['staff'] = all_records ctx['form'] = form return render(request, "investigate.html", ctx)
在模板investigate.html中,我们可以直接显示form对象。如果有多个输入栏,我们可以用相同的方式直接显示整个form,而不是加入许多个<input>标签。
<form action="/west/investigate/" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Submit"> </form> {% for person in staff %} <p>{{ person }}</p> {% endfor %}
访问http://127.0.0.1:8000/west/investigate:
九、综合
Django提供了一个管理数据库的app(django.contrib.admin)。通过该app我们可以直接在web页面来管理我们的数据库。这个app通常已经预装好,可以在mysite/settings.py中的INSTALLED_APPS看到它。
默认界面
admin界面位于mysite/admin这个URL,这通常在mysite/urls.py中已经设置好(mysite为工程名)。
为了让admin界面管理某个数据模型,我们需要先注册该数据模型到admin。比如,我们之前在west中创建的模型Character。修改west/admin.py:
from django.contrib import admin from west.models import Character # Register your models here. admin.site.register(Character)
访问http://127.0.0.1:8000/admin,登录后可以看到管理界面。这个页面除了west.characters外,还有用户和组信息。它们来自Django预装的Auth模块。我们将在以后处理用户管理的问题。
复杂模型
管理页面可以处理更加复杂的数据模型。我们先在west/models.py中增加一个复杂的数据模型,我们新增两个表,Tag以Contact为外部键,一个Contact可以对应多个Tag。
from django.db import models # Create your models here. class Contact(models.Model): name = models.CharField(max_length=200) age = models.IntegerField(default=0) email = models.EmailField() def __unicode__(self): return self.name
生成同步文件:python manage.py makemigrations
同步数据库:python manage.py migrate
然后在west/admin.py注册新增的模型:
from django.contrib import admin from west.models import Character,Contact,Tag # Register your models here. admin.site.register([Character, Contact, Tag])
自定义页面
取代默认的页面
我们可以自定义管理页面来取代默认的页面,比如上面的"add"页面,我们想只显示name和email部分。修改west/admin.py:
from django.contrib import admin from west.models import Character,Contact,Tag class ContactAdmin(admin.ModelAdmin): fields = ('name', 'email') admin.site.register(Contact, ContactAdmin) admin.site.register([Character, Tag])
上面定义了一个ContactAdmin类,用以说明管理页面的显示格式。里面的fields属性用以说明要显示的输入栏,我们没有让"age"显示。
ContactAdmin对应的是Contact数据模型,我们在注册的时候需要将ContactAdmin类也进行注册。
输入栏分块
我们还可以将输入栏分块,给每一块输入栏以自己的显示格式。修改west/admin.py为:
from django.contrib import admin from west.models import Character,Contact,Tag # Register your models here. class ContactAdmin(admin.ModelAdmin): fieldsets = ( ['Main',{ 'fields':('name','email'), }], ['Advance',{ 'classes': ('collapse',), # CSS 'fields': ('age',), }] ) admin.site.register(Contact, ContactAdmin) admin.site.register([Character, Tag])
上面的栏目分为了Main和Advance两部分。classes说明它所在的部分的CSS格式。这里让Advance部分收敛起来:
Inline显示
上面的Contact是Tag的外部键,所以有外部参考的关系。而在默认的页面显示中,将两者分离开来,无法体现出两者的从属关系。我们可以使用Inline显示,让Tag附加在Contact的编辑页面上显示。
修改west/admin.py:
from django.contrib import admin from west.models import Character,Contact,Tag # Register your models here. class TagInline(admin.TabularInline): model = Tag class ContactAdmin(admin.ModelAdmin): inlines = [TagInline] # Inline fieldsets = ( ['Main',{ 'fields':('name','email'), }], ['Advance',{ 'classes': ('collapse',), 'fields': ('age',), }] ) admin.site.register(Contact, ContactAdmin) admin.site.register([Character])
效果如下:
列表页的显示
在Contact输入数条记录后,Contact的列表页看起来如下:
我们也可以自定义该页面的显示,比如在列表中显示更多的栏目,只需要在ContactAdmin中增加list_display属性:
from django.contrib import admin from west.models import Character,Contact,Tag # Register your models here. class ContactAdmin(admin.ModelAdmin): list_display = ('name','age', 'email') # list admin.site.register(Contact, ContactAdmin) admin.site.register([Character, Tag])
列表页新的显示效果如下:
我们还可以为该列表页增加搜索栏。搜索功能在管理大量记录时非常有用。使用search_fields说明要搜索的属性:
from django.contrib import admin from west.models import Character,Contact,Tag # Register your models here. class ContactAdmin(admin.ModelAdmin): list_display = ('name','age', 'email') search_fields = ('name',) admin.site.register(Contact, ContactAdmin) admin.site.register([Character])
效果如下:
Django有管理用户的模块,即django.contrib.auth。你可以在mysite/settings.py里看到。利用该模块可以直接在逻辑层面管理用户,不需要为用户建立模型也不需要手工去实现会话。
创建用户
你可以在admin页面直接看到用户管理的对话框Users。你可以在这里创建、删除和修改用户。在admin页面下,我们还可以控制不同用户组对数据库的访问权限。我们可以在Groups中增加用户组,设置用户组对数据库的访问权限,并将用户加入到某个用户组中。
用户登录
我们建立一个简单的表格。用户通过该表格来提交登陆信息,并在Django服务器上验证。如果用户名和密码正确,那么登入用户。
<form role="form" action="/login" method="post"> <label>Username</label> <input type="text" name='username'> <label>Password</label> <input name="password" type="password"> <input type="submit" value="Submit"> </form>
我们在views.py中,定义处理函数user_login()来登入用户。authenticate()函数,可以根据用户名和密码,验证用户信息。而login()函数则将用户登入。它们来自于django.contrib.auth。
# -*- coding: utf-8 -*- from django.shortcuts import render, redirect from django.core.context_processors import csrf from django.contrib.auth import * def user_login(request): ''' login ''' if request.POST: username = password = '' username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username, password=password) if user is not None and user.is_active: login(request, user) return redirect('/') ctx = {} ctx.update(csrf(request)) return render(request, 'login.html',ctx)
登出
有时用户希望能销毁会话。我们可以提供一个登出的URL:users/logout。已登入用户的访问该URL即可登出。在views.py中增加该URL的处理函数:
# -*- coding: utf-8 -*- from django.shortcuts import redirect def user_logout(request): ''' logout URL: /users/logout ''' logout(request) return redirect('/')
我们修改urls.py,让url对应user_logout()。
# -*- coding: utf-8 -*- mm
访问http://127.0.0.1/users/logout,就可以登出用户。
views.py中的用户
用户登陆的最终目的就是为了让服务器可以区别对待不同的用户,有些内容只能让登陆用户看到,有些内容则只能让特定登陆用户看到。在Django中,对用户身份的检验主要是在views.py中进行。views.py是连接模型和视图的中间层。在views.py的某个处理函数准备HTTP回复的过程中,我们可以检验用户是否登陆。根据用户是否登陆,我们可以给出不同的回复。最原始的方式是使用if式的选择结构:
# -*- coding: utf-8 -*- from django.http import HttpResponse def diff_response(request): if request.user.is_authenticated(): content = "<p>my dear user</p>" else: content = "<p>you wired stranger</p>" return HttpResponse(content)
用户的登录信息包含在request.user中,is_authenticated()方法用于判断用户是否登录,如果用户没有登录那该方法将返回false。
user对象属于contrib.auth.user类型,还有其它属性可供使用:
属性 | 功能 |
get_username() | 返回用户名 |
set_password() | 设置密码 |
get_fullname() | 返回姓名 |
last_login | 上次登录时间 |
date_joined | 账户创建时间 |
Django中装饰器
在Django中,我们还可以利用装饰器,根据用户的登录状况,来决定views.py中处理函数的显示效果。相对于if结构装饰器使用起来更加方便,装饰器login_required是Django预设的装饰器。user_only()的回复结果只能被登录用户看到,而未登录用户将被引导到其他页面。下面的user_only()是views.py中的一个处理函数。
from django.contrib.auth.decorators import login_required from django.http import HttpResponse @login_required def user_only(request): return HttpResponse("<p>This message is for logged in user only.</p>")
Django中还有其它的装饰器,用于修饰处理函数。假如我们想让http回复只能被特殊的用户看到。views.py中增添:
from django.contrib.auth.decorators import user_passes_test
from django.http import HttpResponse
def name_check(user): return user.get_username() == 'vamei' @user_passes_test(name_check) def specific_user(request): return HttpResponse("<p>for Vamei only</p>")
装饰器带有一个参数,该参数是一个函数对象name_check。当name_check返回真值,即用户名为vamei时,specific_user的结果才能被用户看到。
模板中的用户
用户是否登陆的信息也可以直接用于模板,比较原始的方式是把用户信息直接作为环境数据提交给模板。Django为此提供了捷径:我们可以直接在模板中调用用户信息。比如下面的模板:
{% if user.is_authenticated %} <p>Welcome, my genuine user, my true love.</p> {% else %} <p>Sorry, not login, you are not yet my sweetheart. </p> {% endif %}
不需要在环境变量中定义我们就可以直接在模板中引用user。这模板中调用了user的is_authenticated方法,将根据用户的登录情况返回真假值。和正常的Python程序不同,在Django模板中调用方法并不需要后面的括号。
用户注册
我们上面利用了admin管理页面来增加和删除用户,但这并不能用于一般的用户注册,我们需要提供让用户自主注册的功能。用户注册的基本原理非常简单,即建立一个提交用户信息的表格,表格中至少包括用户名和密码。相应的处理函数提取到这些信息建立User对象并存入到数据库中。我们可以利用Django中的UserCreationForm生成表格,并在views.py中处理表格:
from django.contrib.auth.forms import UserCreationForm from django.shortcuts import render, redirect from django.core.context_processors import csrf def register(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): new_user = form.save() return redirect("/") else: form = UserCreationForm() ctx = {'form': form} ctx.update(csrf(request)) return render(request, "register.html", ctx)
相应的模板register.html如下:
<form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Register"> </form>
十、apache服务器
前面研究了Django最主要的几个方面:数据库,模板,动态生成页面等,但都是使用python manage.py runserver来运行服务器,这是一个实验性的web服务器,它负责监听http端口,将收到的请求交给Django处理,将Django的回复发还给客户端,不适用于正常的站点运行。
我们需要一个可以稳定而持续的服务器,这样的持续性服务器可以有很多选择,比如apache, Nginx, lighttpd等,最常见的就是apache服务器。apache服务器和Django之间通过Python的web服务接口WSGI连接,所以我们需要mod_wsgi模块。
首先需要安装apache2和mod_wsgi。在ubuntu下我们可以使用apt-get安装:
sudo apt-get install apache2 sudo apt-get install libapache2-mod-wsgi
在apache的配置文件/etc/apache2/apache2.conf中增加下面的配置。上面的配置中/home/ubuntu/mysite是Django项目所在的位置。利用WSGIScriptAlias我们将URL 对应了wsgi接口程序。这样我们访问根URL时,访问请求会经由WSGI接口传递给Django项目mysite。
# Django WSGIScriptAlias / /home/vamei/mysite/mysite/wsgi.py WSGIPythonPath /home/vamei/mysite <Directory /home/vamei/mysite/mysite> <Files wsgi.py> Order deny,allow Require all granted </Files> </Directory>
配置好后,重启apache2:sudo /etc/init.d/apache2 restart
然后,使用浏览器访问http://localhost可以检查效果:
静态文件
Django的主要功能是动态的生成HTTP回复。很多媒体文件是静态存储的,如.js文件,.css文件和图片文件,这些文件变动的频率较小,我们希望静态的提供这些文件而不是动态的生成。这样既可以减小服务器的负担,也便于在浏览器缓存提高用户体验。我们可以在apache2.conf中添加如下配置:
Alias /media/ /home/vamei/media/ Alias /static/ /home/vamei/static/ <Directory /home/vamei/static/> Order deny,allow Require all granted </Directory> <Directory /home/vamei/media/> Order deny,allow Require all granted </Directory> # Django WSGIScriptAlias / /home/vamei/mysite/mysite/wsgi.py WSGIPythonPath /home/vamei/mysite <Directory /home/vamei/mysite/mysite/ > <Files wsgi.py> Order deny,allow Require all granted </Files> </Directory>
这样,/static/和/media/这两个URL的访问将引导向存有静态文件的/home/vamei/static/和/home/vamei/media/,apache将直接向客户提供这两个文件夹中的静态文件。而剩下的URL访问,将导向WSGI接口,由Django动态处理。在/home/vamei/static/中放入文件revenge.jpg,访问http://localhost/static/revenge:
django REST框架- Django-ninja
优点
通过脚手架创建项目/应用
:不用考虑项目架构怎么设计。自带Admin后台
:在没有前端的情况下,可以比较方便通过Admin对数据进行操作。自带常用模块
:一个命令就能生成 group、user、session ...表,一般个系统都需要user表吧!你还在考虑user表设计,我已经把登录/注册功能写完了。自带ORM
:对于建表和操作数据库可太方便了。自带单元测试模块
:虽然不是必用,但写单元测试很方便的,尤其是运行测试数据隔离,不依赖数据库去构造测试数据。
缺点
模板语言
:在前后端分离的架构下,不用Django做页面了,这个东西可以抛弃了。Form 组件
:以后端的方式渲染页面上的表单, 这个东西也可以抛弃了。只有get/post方法
:这就很不符合 RESTful 风格了。没有参数校验
:这就会导致开发接口效率不高,要写不少代码来判断是否为空,类型对不对。性能差
:反正各种性能对比中都是被吊打的那位。异步支持的不好
:如果你很在意异步,还是不要用django了。
现在的当红web框架当然是fastapi了,我之前的文章也有做介绍。django的大部分缺点,刚好是fastapi的优势,有没有二者相结合的产物?有,那就是django-ninjia
django-ninja 体验
- 安装
> pip install django
> pip install django-ninja
- 创建项目
> django-admin startproject apidemo
- 修改
urls.py
from django.contrib import admin
from django.urls import path
from ninja import NinjaAPI
api = NinjaAPI()
@api.get("/add")
def add(request, a: int, b: int):
return {"result": a + b}
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", api.urls),
]
- 启动项目
> python manage.py runserver
- 自动带api文档
- http://127.0.0.1:8000/api/docs
实现登录接口
当然,只是对比实现一个简单的接口,django-ninja 确实没有比fastapi简单。我们就做一个实际的功能,实现用户登录接口。
一般框架实现过程:
- 需要准备一个数据库。
- 创建一个库和一张用户表。
- 框架需要与数据库连接。
- 通过接口实现用户登录。
django-ninja 有django加持,会把这个过程变得极其简单。
- 执行数据库迁移
> python manage.py migrate
这一个命令相关表已经建好了,django默认使用sqlite3,不需要考虑数据库连接。
- 创建一个管理员账号
> python manage.py createsuperuser
Username (leave blank to use 'user'): admin # 用户名
Email address: admin@mail.com # 邮箱
Password: # 密码
Password (again): # 确认密码
Superuser created successfully.
我们需要一个可以登录成功的账号。
- 实现登录接口
修改ursl.py
文件
from django.contrib import admin
from django.urls import path
from django.contrib import auth
from ninja import NinjaAPI
from ninja import Schema
api = NinjaAPI()
class LoginIn(Schema):
# 登录参数类型校验
username: str
password: str
@api.post("/login")
def user_login(request, payload: LoginIn):
"""
用户登录
"""
user = auth.authenticate(username=payload.username, password=payload.password)
if user is not None:
return {"success": True, "msg": "login success"}
else:
return {"success": False, "msg": "login fail"}
urlpatterns = [
path('admin/', admin.site.urls),
path("api/", api.urls),
]
注意:这是个真实可用的登录接口,有参数类型校验,有查询数据库校验。只有传入的是第2步创建的帐号才可以返回login success
。