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对象进行处理。

from django.urls import path
from . import views

urlpatterns = [
    path('', 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。

from django.urls import path
from . import views
from django.conf.urls import include

urlpatterns = [
    path('', views.hello),
    path('west/', include('west.urls')),
]

为了去耦合实现模块化,我们在mysite/west/目录下新建urls.py,我们在mysite/west/urls.py中添加如下内容,将URL对应到west下的views.py的mypage函数。

from django.urls import path
from . import views

urlpatterns = [
    path('', views.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

from django.contrib import admin
from django.urls import path

from django.conf.urls import include

urlpatterns = [
    path(r'admin/', admin.site.urls),
    path(r'datas/', include('datas.urls')),  #需要添加的
]

 

4、设置models.py文件

设置my_test/datas/models.py文件,在文件里新建的类就是一张表:
表字段声明之类可参考博客:https://www.cnblogs.com/yangmv/p/5327477.html
如新建Character表:

class Character(models.Model):
    #可以解决views.py中调用时没有objects属性的问题
    objects = models.Manager()  

    #name属性是字符类型,最大长度为200
    name = models.CharField(max_length=200)
    
    def __unicode__(self):
        return self.name

 

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类。通过操作该类,我们可以读取表格中的记录

from django.shortcuts import render
from django.http import HttpResponse
from aaa.models import Character


def staff(request):
    staff_list = Character.objects.all()
    staff_str  = map(str, staff_list.values_list()) #不加values/values_list参数获取不到数据库的值
    return HttpResponse("<p>" + ' '.join(staff_str) + "</p>")

 

8、增加url导航

为了让http请求能找到上面的程序,在my_test/datas//urls.py增加url导航:

from django.urls import path
from . import views

urlpatterns = [
    path('staff/', views.staff),
]

 

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

from django.contrib import admin
from django.urls import path

from django.conf.urls import include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('west/', include('west.urls')),  #需要添加的
]

 为了去耦合实现模块化,我们在my_test/west/目录下新建了urls.py,否则对my_test/my_test/urls.py可进行如下配置(推荐使用去耦合的方式);

from django.conf.urls import include
from bbb import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('bbb/', views.templay),
]

 

4、设置模板

在my_test目录下新建templates目录,并在其中建立templay.html文件作为模板(my_test/templates/templay.html)。templay.html文件的内容如下,这个文件中双括号包起来的就是我们未来数据要出现的地方。而相关的格式控制即<h1>标签。

<h1><center>{{ label }}</center> </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导航:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.templay),
]


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/的访问指向该视图。

from django.urls import path
from . import views

urlpatterns = [
    path('form/', views.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/)。

from django.urls import path
from . import views

urlpatterns = [
    path('form/', views.form),
    path('investigate/', views.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
class Tag(models.Model):
    contact = models.ForeignKey(Contact,on_delete=models.CASCADE)
    name    = models.CharField(max_length=50)
    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

 Django 他是一个入门有门槛,学会了做项目非常方便的Web框架。

优点

  1. 通过脚手架创建项目/应用:不用考虑项目架构怎么设计。
  2. 自带Admin后台:在没有前端的情况下,可以比较方便通过Admin对数据进行操作。
  3. 自带常用模块:一个命令就能生成 group、user、session ...表,一般个系统都需要user表吧!你还在考虑user表设计,我已经把登录/注册功能写完了。
  4. 自带ORM:对于建表和操作数据库可太方便了。
  5. 自带单元测试模块:虽然不是必用,但写单元测试很方便的,尤其是运行测试数据隔离,不依赖数据库去构造测试数据。

缺点

  1. 模板语言:在前后端分离的架构下,不用Django做页面了,这个东西可以抛弃了。
  2. Form 组件:以后端的方式渲染页面上的表单, 这个东西也可以抛弃了。
  3. 只有get/post方法:这就很不符合 RESTful 风格了。
  4. 没有参数校验:这就会导致开发接口效率不高,要写不少代码来判断是否为空,类型对不对。
  5. 性能差:反正各种性能对比中都是被吊打的那位。
  6. 异步支持的不好:如果你很在意异步,还是不要用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简单。我们就做一个实际的功能,实现用户登录接口。

一般框架实现过程:

  1. 需要准备一个数据库。
  2. 创建一个库和一张用户表。
  3. 框架需要与数据库连接。
  4. 通过接口实现用户登录。

django-ninja 有django加持,会把这个过程变得极其简单。

  1. 执行数据库迁移
> python manage.py migrate

这一个命令相关表已经建好了,django默认使用sqlite3,不需要考虑数据库连接。

  1. 创建一个管理员账号
> python manage.py createsuperuser
Username (leave blank to use 'user'): admin  # 用户名
Email address: admin@mail.com   # 邮箱
Password:                       # 密码
Password (again):               # 确认密码
Superuser created successfully.

我们需要一个可以登录成功的账号。

  1. 实现登录接口

修改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

 

 

posted @ 2018-01-04 16:08  liangww  阅读(399)  评论(0编辑  收藏  举报