抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Django Admin 管理工具

Django 提供了基于 web 的管理工具。

Django 自动管理工具是 django.contrib 的一部分。你可以在项目的 settings.py 中的 INSTALLED_APPS 看到它:

1
2
3
4
5
6
7
8
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
)

django.contrib是一套庞大的功能集,它是Django基本代码的组成部分。

1. 激活管理工具

通常我们在生成项目时会在 urls.py 中自动设置好,我们只需去掉注释即可。

配置项如下所示:

1
2
3
4
5
6
7
# urls.py
from django.conf.urls import url
from django.contrib import admin

urlpatterns = [
url(r'^admin/', admin.site.urls),
]

启动开发服务器,然后在浏览器中访问 http://127.0.0.1:8000/admin/,得到如下界面:

Untitled

你可以通过命令 python manage.py createsuperuser 来创建超级用户,如下所示:

1
2
3
4
5
6
7
# python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@runoob.com
Password:
Password (again):
Superuser created successfully.
[root@solar HelloWorld]#

输入用户名密码后如图所示

Untitled

为了让 admin 界面管理某个数据模型,我们需要先注册该数据模型到 admin。比如,我们之前在 TestModel 中已经创建了模型 Test 。修改 TestModel/admin.py:

1
2
3
4
5
from django.contrib import admin
from TestModel.models import Test

# Register your models here.
admin.site.register(Test)

刷新后即可看到 Testmodel 数据表:

Untitled

2 复杂模型

管理页面的功能强大,完全有能力处理更加复杂的数据模型。

先在 TestModel/models.py 中增加一个更复杂的数据模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from django.db import models

# Create your models here.
class Test(models.Model):
name = models.CharField(max_length=20)

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

这里有两个表。Tag 以 Contact 为外部键。一个 Contact 可以对应多个 Tag。

我们还可以看到许多在之前没有见过的属性类型,比如 IntegerField 用于存储整数。

Untitled

在 TestModel/admin.py 注册多个模型并显示:

1
2
3
4
5
from django.contrib import admin
from TestModel.models import Test,Contact,Tag

# Register your models here.
admin.site.register([Test, Contact, Tag])

刷新管理页面,显示结果如下:

Untitled

3 自定义表单

我们可以自定义管理页面,来取代默认的页面。比如上面的 “add” 页面。我们想只显示 name 和 email 部分。修改 TestModel/admin.py:

1
2
3
4
5
6
7
8
9
from django.contrib import admin
from TestModel.models import Test,Contact,Tag

# Register your models here.
class ContactAdmin(admin.ModelAdmin):
fields = ('name', 'email')

admin.site.register(Contact, ContactAdmin)
admin.site.register([Test, Tag])

以上代码定义了一个 ContactAdmin 类,用以说明管理页面的显示格式。

里面的 fields 属性定义了要显示的字段。

由于该类对应的是 Contact 数据模型,我们在注册的时候,需要将它们一起注册。显示效果如下:

Untitled

我们还可以将输入栏分块,每个栏也可以定义自己的格式。修改 TestModel/admin.py为:

Untitled

4 内联(Inline)显示

上面的 Contact 是 Tag 的外部键,所以有外部参考的关系。

而在默认的页面显示中,将两者分离开来,无法体现出两者的从属关系。我们可以使用内联显示,让 Tag 附加在 Contact 的编辑页面上显示。

修改TestModel/admin.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.contrib import admin
from TestModel.models import Test,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([Test])

Untitled

二、Django ORM - 单表实例

阅读本章节前你需要先阅读了 Django 模型 进行基础配置及了解常见问题的解决方案。

接下来我们重新创建一个项目 app01(如果之前已创建过,忽略以下操作):

1
django-admin startapp app03

接下来在 settings.py 中找到 INSTALLED_APPS 这一项,如下:

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app03', # 添加此项
)

接下来,告诉 Django 使用 pymysql 模块连接 mysql 数据库:

1
2
3
# 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()

1 创建模型

在项目中的 models.py 中添加以下类:

app03/moudels.py

1
2
3
4
5
6
class Book(models.Model):
id = models.AutoField(primary_key=True) # id 会自动创建,可以手动写入
title = models.CharField(max_length=32) # 书籍名称
price = models.DecimalField(max_digits=5, decimal_places=2) # 书籍价格
publish = models.CharField(max_length=32) # 出版社名称
pub_date = models.DateField() # 出版时间

然后在命令行执行以下命令:

1
2
3
#$ python3 manage.py migrate   # 创建表结构 如果前面有过就不用运行这句话了
$ python3 manage.py makemigrations app03 # 让 Django 知道我们在我们的模型有一些变更
$ python3 manage.py migrate app03 # 创建表结构

2 数据库添加

规则配置:

apo03/urls.py

1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import path
from . import views

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

views.py

1
2
3
4
5
6
from django.shortcuts import render,HttpResponse
from app01 import models
def add_book(request):
books = models.Book.objects.create(title="如来神掌",price=200,publish="功夫出版社",pub_date="2010-10-10")
print(books, type(books)) # Book object (18)
return HttpResponse("<p>数据添加成功!</p>")
创建 creat models.Book.objects.create(title=”如来神掌”,price=200,publish=”功夫出版社”,pub_date=”2010-10-10”)
查找all全部 all books = models.Book.objects.all()
filter符合条件 models.Book.objects.filter(pk=5)
models.Book.objects.filter(publish=’菜鸟出版社’, price=300)
exclude不符合条件 books = models.Book.objects.exclude(pk=5)
排序 order_by() books = models.Book.objects.order_by(“price”) # 查询所有,按照价格升序排列
books = models.Book.objects.order_by(“-price”) # 查询所有,按照价格降序排
反转 reverse() books = models.Book.objects.order_by(“-price”).reverse()
计数 count() books = models.Book.objects.count() # 查询所有数据的数量
books = models.Book.objects.filter(price=200).count() # 查询符合条件数据
是否存在 exists() books = models.Book.objects.exists()
# 报错,判断的数据类型只能为QuerySet类型数据,不能为整型
books = models.Book.objects.count().exists()
# 报错,判断的数据类型只能为QuerySet类型数据,不能为模型类对象
books = models.Book.objects.first().exists()   |

| 查询 | values() | # 查询所有的id字段和price字段的数据
books = models.Book.objects.values(“pk”,”price”) |
| 对数据进行去重 | distinct() | books = models.Book.objects.values_list(“publish”).distinct() # 对模型类的对象去重没有意义,因为每个对象都是一个不一样的存在。
books = models.Book.objects.distinct() |
| 符号 | __gt 大于号 | books = models.Book.objects.filter(price__gt=200) |
| | __gte 大于等于 | books = models.Book.objects.filter(price__gte=200) |
| | __lt 小于 | books=models.Book.objects.filter(price__lt=300) |
| | __lte 小于等于 | books=models.Book.objects.filter(price__lte=300) |
| | __range | models.Book.objects.filter(price__range=[200,300]) |
| | __contains | models.Book.objects.filter(title__contains=”菜”) |
| | __icontains | models.Book.objects.filter(title__icontains=”python”) |
| | __startswith | models.Book.objects.filter(title__startswith=”菜”) |
| | __endswith | models.Book.objects.filter(title__endswith=”教程”) |
| | __year | models.Book.objects.filter(pub_date__year=2008) |
| 删除 | 类型数据.delete() | models.Book.objects.filter(pk__in=[1,2]).delete() |
| 修改 | 类型数据.update(字段名=更改的数据) | books = models.Book.objects.filter(pk__in=[7,8]).update(price=888) |

三、Django ORM – 多表实例

https://www.runoob.com/django/django-orm-2.html

四、Django ORM – 多表实例(聚合与分组查询)

https://www.runoob.com/django/django-orm-3.html

五、Django Form 组件

https://www.runoob.com/django/django-form-component.html

六、Django 用户认证(Auth)组件

Django 用户认证(Auth)组件一般用在用户的登录注册上,用于判断当前的用户是否合法,并跳转到登陆成功或失败页面。

Django 用户认证(Auth)组件需要导入 auth 模块:

1
2
3
4
5
# 认证模块
from django.contrib import auth

# 对应数据库
from django.contrib.auth.models import User

返回值是用户对象。

创建用户对象的三种方法:

  • **create()**:创建一个普通用户,密码是明文的。
  • **create_user()**:创建一个普通用户,密码是密文的。
  • **create_superuser()**:创建一个超级用户,密码是密文的,要多传一个邮箱 email 参数。

参数:

  • username: 用户名。
  • password:密码。
  • email:邮箱 (create_superuser 方法要多加一个 email)。

注意一定要使用create_user,如果使用create会导致密码是以明文的形式存储在数据库里,从而导致后面authenticate认证出错

1
2
from django.contrib.auth.models import User 
User.objects.create_user(username='runbooo',password='123')

登录

原文里面没有加最后一行导致一直在报错,一定要注意请求一定要返回一个可处理的对象。

1
2
3
4
5
6
7
8
9
10
11
12
def login(request):
if request.method == "GET":
return render(request, "login.html")
else:
username = request.POST.get("username")
password = request.POST.get("pwd")
#valid_num = request.POST.get("valid_num")
#keep_str = request.session.get("keep_str")
#if keep_str.upper() == valid_num.upper():
user = User.objects.get(username=username)
user_obj = auth.authenticate(request,username=username, password=password)
return HttpResponse("登录成功")

login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sn0wm1an</title>
</head>
<body>
<h3>用户登录</h3>
<form method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="pwd"></p>
<input type="submit">
</form>
</body>
</html>

Django cookie 与 session

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。

识别返回用户包括三个步骤:

  • 服务器脚本向浏览器发送一组 Cookie。例如:姓名、年龄或识别号码等。
  • 浏览器将这些信息存储在本地计算机上,以备将来使用。
  • 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息发送到服务器,服务器将使用这些信息来识别用户。

HTTP 是一种”无状态”协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。

但是仍然有以下三种方式来维持 Web 客户端和 Web 服务器之间的 session 会话

1 Cookies

一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。

在Web开发中,使用 session 来完成会话跟踪,session 底层依赖 Cookie 技术。

Untitled

设置 cookie:

1
2
rep.set_cookie(key,value,...) 
rep.set_signed_cookie(key,value,salt='加密盐',...)

获取 cookie:

1
request.COOKIES.get(key)

删除 cookie:

1
2
rep =HttpResponse || render || redirect 
rep.delete_cookie(key)

b 创建应用和模型

models.py

1
2
3
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)

urls.py

1
2
3
4
5
6
7
8
9
from django.contrib import admin
from django.urls import path
from cookie import views
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('logout/', views.logout),
path('order/', views.order)

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    username = request.POST.get("username")
    password = request.POST.get("pwd")

    user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
    print(user_obj.username)

    if not user_obj:
        return redirect("/login/")
    else:
        rep = redirect("/index/")
        rep.set_cookie("is_login", True)
        return rep
       
def index(request):
    print(request.COOKIES.get('is_login'))
    status = request.COOKIES.get('is_login') # 收到浏览器的再次请求,判断浏览器携带的cookie是不是登录成功的时候响应的 cookie
    if not status:
        return redirect('/login/')
    return render(request, "index.html")

def logout(request):
    rep = redirect('/login/')
    rep.delete_cookie("is_login")
    return rep # 点击注销后执行,删除cookie,不再保存用户状态,并弹到登录页面
   
def order(request):
    print(request.COOKIES.get('is_login'))
    status = request.COOKIES.get('is_login')
    if not status:
        return redirect('/login/')
    return render(request, "order.html")

以下创建三个模板文件:login.html、index.html、order.html

login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<h3>用户登录</h3>
<form action="" method="post">
{% csrf_token %}
<p>用户名: <input type="text" name="username"></p>
<p>密码: <input type="password" name="pwd"></p>
<input type="submit">
</form>


</body>
</html>

index.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>index 页面。。。</h2>
<a href="/logout/">注销</a>
</body>
</html>

order.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>order 页面。。。</h2>
<a href="/logout/">注销</a>

</body>
</html>

2 Session(保存在服务端的键值对)

服务器在运行时可以为每一个用户的浏览器创建一个其独享的 session 对象,由于 session 为用户浏览器独享,所以用户在访问服务器的 web 资源时,可以把各自的数据放在各自的 session 中,当用户再去访问该服务器中的其它 web 资源时,其它 web 资源再从用户各自的 session 中取出数据为用户服务。

Untitled

工作原理

  • a. 浏览器第一次请求获取登录页面 login。

  • b. 浏览器输入账号密码第二次请求,若输入正确,服务器响应浏览器一个 index 页面和一个键为 sessionid,值为随机字符串的 cookie,即 set_cookie (“sessionid”,随机字符串)。

  • c. 服务器内部在 django.session 表中记录一条数据。

    django.session 表中有三个字段。

    • session_key:存的是随机字符串,即响应给浏览器的 cookie 的 sessionid 键对应的值。
    • session_data:存的是用户的信息,即多个 request.session[“key”]=value,且是密文。
    • expire_date:存的是该条记录的过期时间(默认14天)
  • d. 浏览器第三次请求其他资源时,携带 cookie :{sessionid:随机字符串},服务器从 django.session 表中根据该随机字符串取出该用户的数据,供其使用(即保存状态)。

注意: django.session 表中保存的是浏览器的信息,而不是每一个用户的信息。 因此, 同一浏览器多个用户请求只保存一条记录(后面覆盖前面),多个浏览器请求才保存多条记录。

cookie 弥补了 http 无状态的不足,让服务器知道来的人是”谁”,但是 cookie 以文本的形式保存在浏览器端,安全性较差,且最大只支持 4096 字节,所以只通过 cookie 识别不同的用户,然后,在对应的 session 里保存私密的信息以及超过 4096 字节的文本。

session 设置:

1
request.session["key"] = value

执行步骤:

  • a. 生成随机字符串
  • b. 把随机字符串和设置的键值对保存到 django_session 表的 session_key 和 session_data 里
  • c. 设置 cookie:set_cookie(“sessionid”,随机字符串) 响应给浏览器

session 获取:

1
request.session.get('key')

执行步骤:

  • a. 从 cookie 中获取 sessionid 键的值,即随机字符串。
  • b. 根据随机字符串从 django_session 表过滤出记录。
  • c. 取出 session_data 字段的数据。

session 删除,删除整条记录(包括 session_key、session_data、expire_date 三个字段):

1
request.session.flush()

删除 session_data 里的其中一组键值对:

1
del request.session["key"]

执行步骤:

  • a. 从 cookie 中获取 sessionid 键的值,即随机字符串
  • b. 根据随机字符串从 django_session 表过滤出记录
  • c. 删除过滤出来的记录

Django 中间件

Django 中间件是修改 Django request 或者 response 对象的钩子,可以理解为是介于 HttpRequest 与 HttpResponse 处理之间的一道处理过程。

浏览器从请求到响应的过程中,Django 需要通过很多中间件来处理,可以看如下图所示:

Untitled

Django 中间件作用:

  • 修改请求,即传送到 view 中的 HttpRequest 对象。
  • 修改响应,即 view 返回的 HttpResponse 对象。

中间件组件配置在 settings.py 文件的 MIDDLEWARE 选项列表中。

配置中的每个字符串选项都是一个类,也就是一个中间件。

Django 默认的中间件配置:

1
2
3
4
5
6
7
8
9
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

1 自定义中间件

中间件可以定义四个方法,分别是:

1
2
3
4
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_exception(self, request, exception)
process_response(self, request, response)

自定义中间件的步骤:

在 app 目录下新建一个 py 文件,名字自定义,并在该 py 文件中导入 MiddlewareMixin:

1
**from django.utils.deprecation import MiddlewareMixin**

自定义的中间件类,要继承父类 MiddlewareMixin:

1
2
class MD1(MiddlewareMixin): 
pass

settings.py 中的 MIDDLEWARE 里注册自定义的中间件类:

1
2
3
4
5
6
7
8
9
10
11
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
   
    'app01.middlewares.MD1',
]

2 自定义中间件类的方法

自定义中间件类的方法有:process_request 和 process_response。

Untitled

a process_request 方法

process_request 方法有一个参数 request,这个 request 和视图函数中的 request 是一样的。

process_request 方法的返回值可以是 None 也可以是 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。

process_request 方法是在视图函数之前执行的。

当配置多个中间件时,会按照 MIDDLEWARE中 的注册顺序,也就是列表的索引值,顺序执行。

不同中间件之间传递的 request 参数都是同一个请求对象。

1
2
3
4
5
6
7
from django.utils.deprecation import MiddlewareMixin

from django.shortcuts import render, HttpResponse

class MD1(MiddlewareMixin):
def process_request(self, request):
print("md1 process_request 方法。", id(request)) #在视图之前执行

b process_response

process_response 方法有两个参数,一个是 request,一个是 response,request 是请求对象,response 是视图函数返回的 HttpResponse 对象,该方法必须要有返回值,且必须是response。

process_response 方法是在视图函数之后执行的。

当配置多个中间件时,会按照 MIDDLEWARE 中的注册顺序,也就是列表的索引值,倒序执行。

1
2
3
4
5
6
7
class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("md1  process_request 方法。", id(request)) #在视图之前执行

    def process_response(self,request, response): :#基于请求响应
        print("md1  process_response 方法!", id(request)) #在视图之后
        return response

从下图看,正常的情况下按照绿色的路线进行执行,假设中间件1有返回值,则按照红色的路线走,直接执行该类下的 process_response 方法返回,后面的其他中间件就不会执行。

Untitled

c process_view

process_view 方法格式如下:

1
process_view(request, view_func, view_args, view_kwargs)

process_view 方法有四个参数:

  • request 是 HttpRequest 对象。
  • view_func 是 Django 即将使用的视图函数。
  • view_args 是将传递给视图的位置参数的列表。
  • view_kwargs 是将传递给视图的关键字参数的字典。

view_args 和 view_kwargs 都不包含第一个视图参数(request)。

process_view 方法是在视图函数之前,process_request 方法之后执行的。

返回值可以是 None、view_func(request) 或 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。
  • c.返回值是 view_func(request),Django 将不执行后续视图函数之前执行的方法,提前执行视图函数,然后再倒序执行视图函数之后执行的方法。
  • 当最后一个中间件的 process_request 到达路由关系映射之后,返回到第一个中间件 process_view,然后依次往下,到达视图函数。

评论