0%

python django实践

说明

  • 本次系列教程来自于这里

  • 本地环境

1
2
C:\Users\Administrator>python --version
Python 3.7.9

创建项目

  • 安装django
1
C:\Users\Administrator>python -m pip install Django
  • 查看版本
1
2
3
4
5
python

>>> import django
>>> django.get_version()
'3.1.3'
  • 使用django-admin 创建项目
1
E:\proj>django-admin startproject StudyDjango
  • 查看创建好的目录
1
2
3
4
5
6
7
8
9
E:\proj\StudyDjango>tree /f
│ manage.py

└─StudyDjango
asgi.py
settings.py
urls.py
wsgi.py
__init__.py
  • 启动服务器
1
E:\proj\StudyDjango>python manage.py runserver 0.0.0.0:8000
  • 浏览器打开127.0.0.1:8000

image-20230801144936413

视图和 URL 配置

在先前创建的 StudyDjango目录下的 StudyDjango目录新建一个 views.py 文件,并输入代码:

1
2
3
4
5
6
//StudyDjango/StudyDjango/views.py 文件代码:

from django.http import HttpResponse

def hello(request):
return HttpResponse("Hello world ! ")

接着,绑定 URL 与视图函数。打开 urls.py 文件,删除原来代码,将以下代码复制粘贴到 urls.py 文件中:

1
2
3
4
5
6
7
8
// StudyDjango/StudyDjango/urls.py
from django.conf.urls import url

from . import views

urlpatterns = [
url(r'^$', views.hello),
]

完成后,启动 Django 开发服务器,并在浏览器访问打开浏览器并访问:

image-20230801150043592

我们也可以修改以下规则:

1
2
3
4
5
6
7
8
9
10
11
// StudyDjango/StudyDjango/urls.py
from django.conf.urls import url
from django.urls import path

from . import views

urlpatterns = [
# url(r'^$', views.hello),
path('hello/', views.hello),

]

通过浏览器打开 http://127.0.0.1:8000/hello,输出结果如下:

image-20230801150312911

模板

  • 我们使用 django.http.HttpResponse() 来输出 “Hello World!”。该方式将数据与视图混合在一起,不符合 Django 的 MVC 思想。

  • 将在 StudyDjango目录底下创建 templates 目录并建立 runoob.html文件,runoob.html 文件代码如下:

1
2
3
// StudyDjango/templates/runoob.html 

<h1>{{ hello }}</h1>
  • 接下来我们需要向Django说明模板文件的路径,修改HelloWorld/settings.py,修改 TEMPLATES 中的 DIRS 为 [os.path.join(BASE_DIR, 'templates')],如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// StudyDjango/StudyDjango/settings.py 文件代码:

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 修改位置
'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',
],
},
},
]
  • 我们现在修改 views.py,增加一个新的对象,用于向模板提交数据:
1
2
3
4
5
6
7
8
// StudyDjango/StudyDjango/views.py

from django.shortcuts import render

def runoob(request):
context = {}
context['hello'] = 'Hello World!'
return render(request, 'runoob.html', context)
  • 修改urls中的路由
1
2
3
4
5
6
7
8
9
10
11
12
13
// StudyDjango/StudyDjango/urls.py

from django.urls import path

from . import views

urlpatterns = [
# url(r'^$', views.hello),
# path('hello/', views.hello),
path('runoob/', views.runoob),

]

  • 访问最新地址

image-20230801152821208

  • 更多模板语法参考这里

模型

  • Django 对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。

  • Django 为这些数据库提供了统一的调用API。 我们可以根据自己业务需求选择不同的数据库

  • 本次模型使用mysql,需要安装mysq驱动

1
E:\proj\StudyDjango>pip3 install pymysql

本地需要搭建好Mysql环境

Django ORM

  • 本地数据库中需要提前创建数据库,因为ORM 无法操作到数据库级别,只能到表。

  • 使用heidisql连接本地mysql数据库后,新建一个数据库,名称为:runoob

  • 我们在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// StudyDjango/StudyDjango/settings.py

DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
'default':
{
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'runoob', # 数据库名称
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123456', # 数据库密码
}
}
  • 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
1
2
3
4
// StudyDjango/StudyDjango/__init__.py

import pymysql
pymysql.install_as_MySQLdb()

定义模型

创建APP

  • Django 规定,如果要使用模型,必须要创建一个 app。我们使用以下命令创建一个 TestModel 的 app:
1
E:\proj\StudyDjango>django-admin startapp TestModel

image-20230801154926128

  • 我们修改 TestModel/models.py 文件,代码如下:
1
2
3
4
5
6
from django.db import models


class Test(models.Model):
name = models.CharField(max_length=20)

以上的类名代表了数据库表名(test),且继承了models.Model,类里面的字段代表数据表中的字段(name),数据类型则由CharField(相当于varchar)、DateField(相当于datetime), max_length 参数限定长度。

  • 接下来在 settings.py 中找到INSTALLED_APPS这一项,如下:
1
2
3
4
5
6
7
8
9
10
11
// StudyDjango\StudyDjango\settings.py

INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'TestModel', # 添加此项
)
  • 在命令行中运行, 创建表结构,发现报错了
1
2
3
4
5
E:\proj\StudyDjango>python manage.py migrate

File "D:\app\Python37\lib\site-packages\django\db\backends\mysql\base.py", line 36, in <module>
raise ImproperlyConfigured('mysqlclient 1.4.0 or newer is required; you have %s.' % Database.__version__)
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required; you have 0.10.1.
  • 原因是 MySQLclient 目前只支持到 Python3.4,因此如果使用的更高版本的 python,把D:\app\Python37\Lib\site-packages\django\db\backends\mysql\base.py 对应报错代码注释

  • 再次执行

1
2
3
4
5
6

E:\proj\StudyDjango> python manage.py migrate # 创建表结构

E:\proj\StudyDjango> python manage.py makemigrations TestModel # 让 Django 知道我们在我们的模型有一些变更
E:\proj\StudyDjango> python manage.py migrate TestModel # 创建表结构

  • 查看表已经生成成功

image-20230801163314750

数据库操作

操作模型

新增数据

1
2
3
4
5
6
7
8
9
10
11
// StudyDjango/StudyDjango/testdb.py
from django.http import HttpResponse

from TestModel.models import Test


# 数据库操作
def testdb(request):
test1 = Test(name='runoob')
test1.save()
return HttpResponse("<p>数据添加成功!</p>")
  • 下来我们在 StudyDjango目录中添加 testdb.py 文件到urls.py中
1
2
3
4
5
6
7
8
9
10
11
from django.urls import path

from . import views,testdb

urlpatterns = [
# url(r'^$', views.hello),
# path('hello/', views.hello),
path('runoob/', views.runoob),
path('testdb/', testdb.testdb),

]
  • 访问 http://127.0.0.1:8000/testdb 就可以看到数据添加成功的提示。

image-20230801170113854

查询数据

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
from django.http import HttpResponse

from TestModel.models import Test

# 数据库操作
def testdb(request):
# 初始化
response = ""
response1 = ""


# 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM
list = Test.objects.all()

# filter相当于SQL中的WHERE,可设置条件过滤结果
response2 = Test.objects.filter(id=1)

# 获取单个对象
response3 = Test.objects.get(id=1)

# 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
Test.objects.order_by('name')[0:2]

#数据排序
Test.objects.order_by("id")

# 上面的方法可以连锁使用
Test.objects.filter(name="runoob").order_by("id")

# 输出所有数据
for var in list:
response1 += var.name + " "
response = response1
return HttpResponse("<p>" + response + "</p>")

更新

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

from TestModel.models import Test

# 数据库操作
def testdb(request):
# 修改其中一个id=1的name字段,再save,相当于SQL中的UPDATE
test1 = Test.objects.get(id=1)
test1.name = 'Google'
test1.save()

# 另外一种方式
#Test.objects.filter(id=1).update(name='Google')

# 修改所有的列
# Test.objects.all().update(name='Google')

return HttpResponse("<p>修改成功</p>")

删除

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

from TestModel.models import Test

# 数据库操作
def testdb(request):
# 删除id=1的数据
test1 = Test.objects.get(id=1)
test1.delete()

# 另外一种方式
# Test.objects.filter(id=1).delete()

# 删除所有数据
# Test.objects.all().delete()

return HttpResponse("<p>删除成功</p>")

路由

  • Django 项目里多个app目录共用一个 urls 容易造成混淆,后期维护也不方便
  • 在项目名(TestModel)中新增一个 TestModel/urls.py
1
2
3
4
5
6
7
8
from django.urls import path,re_path
from django.conf.urls import url

from TestModel import views # 从自己的 app 目录引入 views
urlpatterns = [
url(r'^userAdd/', views.userAdd),
path('userQuery/', views.userQuery),
]
  • TestModel/view.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
35
36
37
38
39
40
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
from TestModel.models import Test


def userAdd(request):
test1 = Test(name='runoob')
test1.save()
return HttpResponse("<p>数据添加成功!</p>")

def userQuery(request):
# 初始化
response = ""
response1 = ""

# 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM
list = Test.objects.all()

# filter相当于SQL中的WHERE,可设置条件过滤结果
response2 = Test.objects.filter(id=1)

# 获取单个对象
response3 = Test.objects.get(id=1)

# 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
Test.objects.order_by('name')[0:2]

# 数据排序
Test.objects.order_by("id")

# 上面的方法可以连锁使用
Test.objects.filter(name="runoob").order_by("id")

# 输出所有数据
for var in list:
response1 += var.name + " "
response = response1
return HttpResponse("<p>" + response + "</p>")
  • StudyDiango/StudyDiango/urls.py 中引用具体项目的url
1
2
3
4
5
6
7
8
9
10
11
12
13
from django.conf.urls import url
from django.urls import path, include

from . import views,testdb

urlpatterns = [
# url(r'^$', views.hello),
# path('hello/', views.hello),
path('runoob/', views.runoob),
path('testdb/', testdb.testdb),
path('testModel/', include("TestModel.urls")),

]
  • 浏览器打开http://127.0.0.1:8000/testModel/userQuery/

image-20230802105812991

ORM多表实践

表结构

书籍表 Book:title 、 price 、 pub_date 、 publish(外键,多对一) 、 authors(多对多)

出版社表 Publish:name 、 city 、 email

作者表 Author:name 、 age 、 au_detail(一对一)

作者详情表 AuthorDetail:gender 、 tel 、 addr 、 birthday

  • 关系图

image-20230802104212169

创建模型

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
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
# 关联出版社一对多,意思就是一个出版社可以有印刷多本书
publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
# 多对多,意思就是多个作者可以编写多本书
authors = models.ManyToManyField("Author")


class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
email = models.EmailField()


class Author(models.Model):
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
# 一对一,意思为一个作者对应一个作者详情
au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)


class AuthorDetail(models.Model):
gender_choices = (
(0, "女"),
(1, "男"),
(2, "保密"),
)
gender = models.SmallIntegerField(choices=gender_choices)
tel = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
birthday = models.DateField()
  • 生成表结构
1
2
E:\proj\StudyDjango> python manage.py makemigrations TestModel  # 让 Django 知道我们在我们的模型有一些变更
E:\proj\StudyDjango> python manage.py migrate TestModel # 创建表

image-20230802110229182

注意testmode_book_authors 为book多对多authors的表

插入数据

1
2
3
4
5
6
7
8
# 插入出版社
insert into testmodel_publish(name,city,email) values ("华山出版社", "华山", "hs@163.com"), ("明教出版社", "黑木崖", "mj@163.com")

# 先插入 authordetail 表中多数据
insert into testmodel_authordetail(gender,tel,addr,birthday) values (1,13432335433,"华山","1994-5-23"), (1,13943454554,"黑木崖","1961-8-13"), (0,13878934322,"黑木崖","1996-5-20")

# 再将数据插入 author,这样 author 才能找到 authordetail
insert into testmodel_author(name,age,au_detail_id) values ("令狐冲",25,1), ("任我行",58,2), ("任盈盈",23,3)

ORM添加数据

一对多(外键 ForeignKey)

方式一: 传对象的形式,返回值的数据类型是对象,书籍对象。

步骤:

  • a. 获取出版社对象
  • b. 给书籍的出版社属性 pulish 传出版社对象
1
2
3
4
5
6
7
8
9
// testmode/views.py

def add_book(request):
# 获取出版社对象
pub_obj = models.Publish.objects.filter(pk=1).first()
# 给书籍的出版社属性publish传出版社对象
book = models.Book.objects.create(title="菜鸟教程", price=200, pub_date="2010-10-10", publish=pub_obj)
print(book, type(book))
return HttpResponse(book)

方式二: 传对象 id 的形式(由于传过来的数据一般是 id,所以传对象 id 是常用的)。

一对多中,设置外键属性的类(多的表)中,MySQL 中显示的字段名是:外键属性名_id

返回值的数据类型是对象,书籍对象。

步骤:

  • a. 获取出版社对象的 id
  • b. 给书籍的关联出版社字段 pulish_id 传出版社对象的 id
1
2
3
4
5
6
7
8
9
def add_book(request):
# 获取出版社对象
pub_obj = models.Publish.objects.filter(pk=1).first()
# 获取出版社对象的id
pk = pub_obj.pk
# 给书籍的关联出版社字段 publish_id 传出版社对象的id
book = models.Book.objects.create(title="冲灵剑法", price=100, pub_date="2004-04-04", publish_id=pk)
print(book, type(book))
return HttpResponse(book)
  • testmodel/urls.py 引用views.py代码
1
2
3
4
5
6
7
8
9
from django.urls import path,re_path
from django.conf.urls import url

from TestModel import views # 从自己的 app 目录引入 views
urlpatterns = [
url(r'^userAdd/', views.userAdd),
path('userQuery/', views.userQuery),
path('add_book/', views.add_book),
]

多对多(ManyToManyField):

  • 在第三张关系表中新增数据

方式一: 传对象形式,无返回值。

步骤:

  • a. 获取作者对象
  • b. 获取书籍对象
  • c. 给书籍对象的 authors 属性用 add 方法传作者对象
1
2
3
4
5
6
7
8
9
10
def add_books(request):
# 获取作者对象
chong = models.Author.objects.filter(name="令狐冲").first()
ying = models.Author.objects.filter(name="任盈盈").first()
# 获取书籍对象
book = models.Book.objects.filter(title="菜鸟教程").first()
# 给书籍对象的 authors 属性用 add 方法传作者对象
# 菜鸟教程这本书有两个作者
book.authors.add(chong, ying)
return HttpResponse(book)

方式二: 传对象id形式,无返回值。

步骤:

  • a. 获取作者对象的 id
  • b. 获取书籍对象
  • c. 给书籍对象的 authors 属性用 add 方法传作者对象的 id
1
2
3
4
5
6
7
8
9
def add_books_1(request):
# 获取作者对象
chong = models.Author.objects.filter(name="令狐冲").first()
# 获取作者对象的id
pk = chong.pk
# 获取书籍对象
book = models.Book.objects.filter(title="冲灵剑法").first()
# 给书籍对象的 authors 属性用 add 方法传作者对象的id
book.authors.add(pk)

关联管理器(对象调用)

前提:

  • 多对多(双向均有关联管理器)
  • 一对多(只有多的那个类的对象有关联管理器,即反向才有)

语法格式:

1
2
正向:属性名,如add,create等
反向:小写类名加 _set

一对多只能反向

  • add。用于多对多,把指定的模型对象添加到关联对象集(关系表)中。
1
2
3
4
5
6
7
# 方式一:传对象
book_obj = models.Book.objects.get(id=10)
author_list = models.Author.objects.filter(id__gt=2)
book_obj.authors.add(*author_list) # 将 id 大于2的作者对象添加到这本书的作者集合中
# 方式二:传对象 id
book_obj.authors.add(*[1,3]) # 将 id=1 和 id=3 的作者对象添加到这本书的作者集合中
return HttpResponse("ok")
  • 反向:小写表名_set
1
2
3
4
ying = models.Author.objects.filter(name="任盈盈").first()
book = models.Book.objects.filter(title="冲灵剑法").first()
ying.book_set.add(book)
return HttpResponse("ok")

create:创建一个新的对象,并同时将它添加到关联对象集之中。

1
2
3
4
5
pub = models.Publish.objects.filter(name="明教出版社").first()
wo = models.Author.objects.filter(name="任我行").first()
book = wo.book_set.create(title="吸星大法", price=300, pub_date="1999-9-19", publish=pub)
print(book, type(book))
return HttpResponse("ok")

remove:从关联对象集中移除执行的模型对象。

1
2
3
4
author_obj =models.Author.objects.get(id=1)
book_obj = models.Book.objects.get(id=11)
author_obj.book_set.remove(book_obj)
return HttpResponse("ok")

clear:从关联对象集中移除一切对象,删除关联,不会删除对象。

1
2
3
#  清空独孤九剑关联的所有作者
book = models.Book.objects.filter(title="菜鸟教程").first()
book.authors.clear()

对于 ForeignKey 对象,这个方法仅在 null=True(可以为空)时存在。

ORM 查询

一对多

查询主键为 10 的书籍的出版社所在的城市(正向)。

1
2
3
4
book = models.Book.objects.filter(pk=10).first()
res = book.publish.city
print(res, type(res))
return HttpResponse("ok")
  • 查询明教出版社出版的书籍名(反向)。
1
2
3
4
5
6
pub = models.Publish.objects.filter(name="明教出版社").first()
# pub.book_set.all():取出书籍表的所有书籍对象,在一个 QuerySet 里,遍历取出一个个书籍对象。
res = pub.book_set.all()
for i in res:
print(i.title)
return HttpResponse("ok")

一对一

查询令狐冲的电话(正向

正向:对象.属性 (author.au_detail) 可以跳转到关联的表(作者详情表)

1
2
3
4
author = models.Author.objects.filter(name="令狐冲").first()
res = author.au_detail.tel
print(res, type(res))
return HttpResponse("ok")

查询所有住址在黑木崖的作者的姓名(反向)。

1
2
3
4
addr = models.AuthorDetail.objects.filter(addr="黑木崖").first()
res = addr.author.name
print(res, type(res))
return HttpResponse("ok")

多对多

  • 菜鸟教程所有作者的名字以及手机号(正向)。

  • 正向:**对象.属性(book.authors)**可以跳转到关联的表(作者表)。

  • 作者表里没有作者电话,因此再次通过**对象.属性(i.au_detail)**跳转到关联的表(作者详情表)

1
2
3
4
5
book = models.Book.objects.filter(title="菜鸟教程").first()
res = book.authors.all()
for i in res:
print(i.name, i.au_detail.tel)
return HttpResponse("ok")
  • 查询任我行出过的所有书籍的名字(反向)。
1
2
3
4
5
author = models.Author.objects.filter(name="任我行").first()
res = author.book_set.all()
for i in res:
print(i.title)
return HttpResponse("ok")

基于双下划线的跨表查询

正向:属性名称__跨表的属性名称

反向:小写类名__跨表的属性名称

一对多

正向:查询菜鸟出版社出版过的所有书籍的名字与价格。

1
2
# publish__name 跨表中的name
res = models.Book.objects.filter(publish__name="菜鸟出版社").values_list("title", "price")

反向:通过 小写类名__跨表的属性名称(book__title,book__price) 跨表获取数据。

1
2
res = models.Publish.objects.filter(name="菜鸟出版社").values_list("book__title","book__price")
return HttpResponse("ok")

多对多

查询任我行出过的所有书籍的名字。

正向:通过 属性名称__跨表的属性名称(authors__name) 跨表获取数据:

1
res = models.Book.objects.filter(authors__name="任我行").values_list("title")

反向:通过 小写类名__跨表的属性名称(book__title) 跨表获取数据:

1
res = models.Author.objects.filter(name="任我行").values_list("book__title")

一对一

查询任我行的手机号。

正向:通过 属性名称__跨表的属性名称(au_detail__tel) 跨表获取数据。

1
res = models.Author.objects.filter(name="任我行").values_list("au_detail__tel")

反向:通过 小写类名__跨表的属性名称(author__name) 跨表获取数据。

1
res = models.AuthorDetail.objects.filter(author__name="任我行").values_list("tel")

总结

  • 主要介绍了新建项目,应用,路由,连接数据库,对model进行增删改查等操作