创建Django项目

配置数据库

  • settings.py里的DATABASES里配置数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 定义数据库配置
    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql', # 使用MySQL作为数据库引擎
    'NAME': 'movie', # 数据库名称为movie
    'USER': 'root', # 数据库用户名为root
    'PASSWORD': '123456' # 数据库密码为123456
    }
    }
  • 使用PyMySQL来替代MySQL,并在配置文件中movie/init.py文件中设置

    1
    2
    3
    4
    5
    # 导入PyMySQL模块
    import pymysql

    # 将PyMySQL模块安装为MySQLdb的别名,以便在Django项目中使用MySQL数据库
    pymysql.install_as_MySQLdb()
  • 在vscode终端输入命令来安装PyMySQL

    1
    pip install pymysql
  • 安装完PyMySQL后,再次执行迁移文件命令,将数据库应用到数据表中
    执行完,django项目所应用到的数据表会出现在navicat中

    1
    2
    python manage.py makemigrations
    python manage.py migrate

启动django项目

  • 执行命令启动服务
    1
    python manage.py runserver
  • 在浏览器地址中输入:http://127.0.0.1:8000/查看项目效果

django后台

  • 输入:127.0.0.1:8000/admin入到后台的登录页面
  • 通过命令来创建管理员用户
    1
    python manage.py createsuperuser

创建电影应用

创建应用

使用命令来创建movie应用

1
python manage.py startapp movie

编写模型

在movie/models.py文件中来编写模型

  • 项目中需要用到电影的分类,这些分类主要包含了电影、综艺、纪录片、短片,所以要创建一个category类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from django.db import models

    class Category(models.Model):
    # 分类信息
    id = models.AutoField(primary_key=True) # 自增主键字段
    category_name = models.CharField(max_length=100, verbose_name='分类名') # 分类名字段,最大长度为100

    class Meta:
    db_table = u'category' # 数据库表名
    verbose_name = '分类管理' # 在Admin后台显示的单个对象的名称
    verbose_name_plural = '分类管理' # 在Admin后台显示的对象列表的名称

    def __str__(self):
    return self.category_name # 返回对象的字符串表示形式,即分类名
  • 还需要电影的信息,所以在movie/models.py文件中来继续创建一个movie类

    这个电影下面的字段比较多,包括id、电影名称、上映年份、导演、编剧、主演等等。

    • 地区是写死的,用一个region表示。
    • 另外,还有一个字段是Hot,表示是否为热门,是否为置顶,它们都是布尔值。
    • 还有一个可选项是quality,表示视频质量。
    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
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    from django.db import models

    # 地区选项
    Region = [
    (1, '中国大陆'),
    (2, '中国香港'),
    (3, '中国台湾'),
    (4, '美国'),
    (5, '韩国'),
    (6, '日本'),
    (7, '其他')
    ]
    # 清晰度选项
    Quality = [
    (1, '720P'),
    (2, '1080P'),
    (3, '4K')
    ]

    # 热门精选选项
    Hot = [
    (False, '否'),
    (True, '是')
    ]
    # 置顶选项
    Top = [
    (False, '否'),
    (True, '是')
    ]
    # 是否限制选项
    SHOW = [
    (False, '否'),
    (True, '是')
    ]
    # 是否免费选项
    FREE = [
    (False, '否'),
    (True, '是')
    ]

    class Category(models.Model):
    # 分类信息

    class Movie(models.Model):
    # 电影信息
    id = models.AutoField(primary_key=True) # 自增主键
    movie_name = models.CharField(max_length=100, verbose_name='电影名') # 电影名
    release_year = models.IntegerField(verbose_name='上映年份') # 上映年份
    director = models.CharField(max_length=100, verbose_name='导演') # 导演
    scriptwriter = models.CharField(max_length=100, verbose_name='编剧') # 编剧
    actors = models.CharField(max_length=200, verbose_name='主演') # 主演
    region = models.SmallIntegerField(choices=Region, verbose_name='地区') # 地区
    types = models.CharField(max_length=50, verbose_name='类型') # 类型
    language = models.CharField(max_length=100, verbose_name='语言') # 语言
    release_date = models.DateField(verbose_name='上映日期') # 上映日期
    duration = models.CharField(max_length=50, verbose_name='时长(或集数)') # 时长(或集数)
    alternate_name = models.CharField(max_length=100, blank=True, verbose_name='又名') # 又名,可选
    image_url = models.CharField(max_length=300, blank=True, verbose_name='图片链接') # 图片链接,可选
    rate = models.FloatField(blank=True, verbose_name='豆瓣评分') # 豆瓣评分,可选
    review = models.TextField(max_length=500, blank=True, verbose_name='简介') # 简介,可选
    is_hot = models.BooleanField(choices=Hot, default=False, verbose_name='是否热门') # 是否热门
    is_top = models.BooleanField(choices=Top, default=False, verbose_name='是否置顶') # 是否置顶
    quality = models.SmallIntegerField(choices=Quality, blank=False, verbose_name='清晰度') # 清晰度
    subtitle = models.CharField(max_length=100, blank=True, verbose_name='字幕') # 字幕,可选
    update_info = models.CharField(max_length=100, blank=True, verbose_name='更新信息') # 更新信息,可选
    update_progress = models.CharField(max_length=100, blank=True, verbose_name='更新进度') # 更新进度,可选
    download_info = models.TextField(max_length=500, blank=True, verbose_name='网盘信息',
    help_text="每个网盘信息占一行,每个字段用网盘名:网址 提取码:字符 组成(无提取码不写)。 如 百度网盘:http://www.baidu.com 提取码:8888 ") # 网盘信息,可选
    is_show = models.BooleanField(choices=SHOW, default=True, verbose_name='是否显示') # 是否显示
    is_free = models.BooleanField(choices=FREE, default=False, verbose_name='是否免费') # 是否免费
    category = models.ForeignKey(Category, blank=False, verbose_name='分类名', on_delete=models.CASCADE) # 分类名,外键

    class Meta:
    db_table = 'movie' # 数据库表名
    verbose_name = '电影管理' # 在Admin后台显示的单个对象的名称
    verbose_name_plural = '电影管理' # 在Admin后台显示的对象列表的名称

    def __str__(self):
    return self.movie_name # 返回对象的字符串表示形式,即电影名

配置应用

在settings.py文件中找打INSTALLED APPS代码块,把movie应用加上去

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',
'movie',
]

执行迁移操作来生成数据表

1
2
3
python manage.py makemigrations
python manage.py makemigrations
python manage.py migrate

在admin.py里将模型里的类注册到后台上

1
2
3
4
5
6
7
from django.contrib import admin
from movie.models import Movie, Category

# 注册电影模型类
admin.site.register(Movie)
# 注册分类模型类
admin.site.register(Category)

启动项目,在后台可以进行增删改查操作,比如添加分类和电影等等

1
python manage.py runserver

创建接口

restful API

以前编写接口时,缺乏规范性,从而诞生了一套接口规范,符合这套规范的API被称为”restful API”。

在rest中定义了四个主要方法:GET用于获取资源,POST用于新建资源,PUT用于更新资源,DELETE用于删除资源。

为什么要使用restful API呢?
        在前后端分离的项目中,后端与数据库打交道并对数据进行读取,前后端之间通常选择一种通用的数据格式,如JSON格式数据。
        后端读取数据库后以JSON形式展示数据(后端也有页面的),前端通过请求后端接口获取JSON数据并展示在页面上,这是前后端分离的核心思想。

传统方式创建接口

  • 配置URL
    在全局配置文件中找到urls.py文件中的urlpatterns代码块,然后添加一个路由,命名为API/movie,并使用include引入movie.urls。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 从Django中导入必要的模块
    from django.contrib import admin
    from django.urls import path, include

    # 为项目定义URL模式
    urlpatterns = [
    # 管理员站点URL
    path('admin/', admin.site.urls),

    # 用于电影相关端点的URL模式
    # 当请求以'aip/movie/'开头时,将重定向到'movie.urls'的URL配置。
    # 'namespace'参数用于通过为包含的URL配置中定义的URL名称添加前缀来区分URL。
    path('aip/movie/', include('movie.urls', namespace='movie')),
    ]
  • 在movie文件夹下创建一个urls.py文件。在这个文件中,写上路由,模仿刚才的代码形式。
    首先,导入模型,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 从Django中导入路径函数和views模块
    from django.urls import path
    from movie import views

    # 设置应用程序名称
    app_name = 'movie'

    # 定义URL模式列表
    urlpatterns = [
    # 对根路径的视图MovieList进行映射,并命名为'list'
    path('', views.MovieList.as_view(), name='list'),
    ]
  • 接下来在movie文件下的views.py文件中创建movie list函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 从Django中导入render和JsonResponse函数
    from django.shortcuts import render
    from django.http import JsonResponse

    # 从models模块中导入Movie模型
    from .models import Movie

    def movie_list(request):
    # 获取所有电影数据
    movies = Movie.objects.all().values_list()
    # 将数据转换为列表形式
    data = list(movies)
    # 返回JSON响应
    return JsonResponse(data, safe=False)
  • 启动服务,在浏览器地址栏输入ulr127.0.0.1:8000/aip/movie/,此时浏览器页面就以列表显示了JSON格式数据

如果想返回一个数据,例如电影信息,可以自定义“data”字典,以包含特定的细节,如电影名称、上映日期和评分。无论输出什么内容,都取决于后台传递的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 从Django中导入render和JsonResponse函数
from django.shortcuts import render
from django.http import JsonResponse

# 从models模块中导入Movie模型
from .models import Movie

def movie_list(request):
# 获取所有电影数据
movies = Movie.objects.all().values_list()
data = {
'movie_name':‘碟中谍’,
'rate': 7.8
}
# 返回JSON响应
return JsonResponse(data, safe=False)

DRF方式创建接口

DRF 即 Django REST Framework

在使用DRF的时候,要创建一个序列化器,所谓的序列化器就是实现将Python转化为JSON格式数据。

在这个转化的过程中,可以加一些验证,比如说用户输入的一些信息,可以对它进行验证。

  • 在movie文件夹应用中创建一个文件,叫做serializers.py
  • 接着从rest_framework中导入serializers。
  • 创建一个序列化器,定一个类叫做MovieListSerializer,让它继承serializer类。
1
2
3
4
5
6
7
8
9
10
# 从rest_framework模块中导入序列化器基类
from rest_framework import serializers

# 定义电影序列化器
class MovieSerializer(serializers.ModelSerializer):
# 定义id字段,设置为只读
id = serializers.IntegerField(read_only=True)

# 定义movie_name字段,限制最大长度为100个字符
movie_name = serializers.CharField(max_length=100)

接下来,就可以在movie文件夹下的views.py视图文件中导入这个序列化器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 从Django中导入render和JsonResponse函数
from django.shortcuts import render
from django.http import JsonResponse
# 从models模块中导入Movie模型
from .models import Movie
# 从.serializers模块中导入MovieListSerializer序列化器
from .serializers import MovieListSerializer

# 定义电影列表视图
def movie_list(request):
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回JSON格式的响应
return JsonResponse(serializer.data, safe=False)

重新启动一下服务,再访问127.0.0.1:8000/api/movie/这个接口,就获取到了所有的电影的数据,包含了两个,一个是ID,一个是movie。

在使用这个DRF的时候,重点是创建这么一个serializers.py,把所有的需要序列化的内容全部放到这个文件中,然后对字段进行相应的处理。这就是一个最简单的DRF的实例。

使用ModelSerializer简化代码

由于字段比较多,在serializers.py里一个一个写的话,会非常麻烦。而在models.py文件中,同样创建了ID、电影名称等多个字段。
Model Serializer这个类的作用就是可以将模型中定义的字段类型转化为serializer中的字段类型。并且在内部进行了模型字段到序列化器字段的映射。通过使用ModelSerializer类,可以省去一个个定义字段的步骤,让它自动匹配模型中的字段。这样可以使代码更简洁,不需要手动定义每个字段的序列化过程。
在movie/serializers.py文件中修改代码让MovieListSerializer继承的是Model Serializer类。使用这个类以后,让它自动从模型中去匹配。

1
2
3
4
5
6
7
8
9
10
11
12
# 从rest_framework模块中导入序列化器基类
from rest_framework import serializers
# 从movie.models模块中导入Movie模型
from movie.models import Movie

# 定义电影序列化器
class MovieSerializer(serializers.ModelSerializer):
class Meta:
# 指定序列化器要使用的模型
model = Movie
# 指定要序列化的字段,'__all__'表示序列化所有字段
fields = '__all__'

这样的话,的接口就会显示全部字段。
如果只选择需要的字段,可以使用一个列表,将所有需要序列化的字段全部填写到这里。

1
2
3
4
5
6
7
8
9
10
11
12
# 从rest_framework模块中导入序列化器基类
from rest_framework import serializers
# 从movie.models模块中导入Movie模型
from movie.models import Movie

# 定义电影序列化器
class MovieSerializer(serializers.ModelSerializer):
class Meta:
# 指定序列化器要使用的模型
model = Movie
# 指定要序列化的字段
fields = ['movie_name', "release_year', 'director', 'actors']

再刷新浏览器页面,这次只显示4个字段

类的视图

DRF提供了多种方式来支持类的视图。DRF要提供这么多方式,考虑到了灵活性和便利性。
不同的情况可能需要不同的解决方式,但大多数操作都涉及到基本的增删改查功能。
即使模型不同,操作方式也基本相同,只需稍作调整。因此,DRF提供了多种方式来满足不同需求。

使用APIView实现

创建一个新的电影详情页。
在movie/views.py中,首先从rest_framework中导入views,然后导入APIView。
接着,创建一个类,命名为MovieDetail,并让它继承APIView。
接下来,实现增删改查操作方式。这些操作方式包括GET、POST、PUT、DELETE等方法。

首先,实现获取电影详情的方法,使用GET请求。
在这个方法中,需要获取请求对象,并根据电影的ID来获取对应的电影详情。如果电影不存在,则返回404错误。修改代码如下:

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
41
42
43
44
45
# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
@api_view(['GET', 'POST'])
def movie_list(request):
# 如果请求方法为GET
if request.method == 'GET':
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回序列化后的数据和状态码200 OK
return Response(serializer.data, status=status.HTTP_200_OK)
elif request. method == 'POST':
serializer = MovieListSerializer (data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status HTTP_400_BAD_REQUEST)
# 定义MovieDetail类,继承自APIView类
class MovieDetail(APIView):
# 定义get方法
def get(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化
serializer = MovieDetailSerializer(movie)
# 返回序列化后的数据作为响应
return Response(serializer.data)

接下来,使用序列化将电影信息返回给前端。
最后,在movie/serializers.py文件中定义一个MovieDetailSerializer来对电影详情进行序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 导入rest_framework中的serializers模块
from rest_framework import serializers
# 导入Movie模型
from .models import Movie

# 定义MovieSerializer类,继承自ModelSerializer类
class MovieSerializer(serializers.ModelSerializer):

# 定义Meta类
class Meta:
# 指定要序列化的模型为Movie
model = Movie
# 指定序列化的字段为所有字段
fields = '__all__'

# 定义MovieDetailSerializer类,同样继承自ModelSerializer类
class MovieDetailSerializer(serializers.ModelSerializer):

# 定义Meta类
class Meta:
# 指定要序列化的模型为Movie
model = Movie
# 指定序列化的字段为所有字段
fields = '__all__'

接下来,在URL中定义该路由。

  • 在movie/urls.py文件中来进行定义,这个路由是有一些特殊的,它是在基础URL的基础上加上了一个数字ID,这个数字是可变的,所以不能写死。可以使用int:pk来定义这个参数。
  • 然后,在视图中将使用MovieDetail类,并将其转化为可以执行的视图对象。同时,给它起一个别名Detail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 导入django.urls中的path函数
from django.urls import path
# 导入views模块中的MovieList和MovieDetail类
from movie import views

# 定义app名称为'movie'
app_name = 'movie'

# 定义URL模式列表
urlpatterns = [
# 定义空路径对应的视图为MovieList,并命名为'list'
path('', views.MovieList.as_view(), name='list'),
# 定义带有整数参数pk的路径对应的视图为MovieDetail,并命名为'detail'
path('<int:pk>/', views.MovieDetail.as_view(), name='detail'),
]

接下来,将修改电影信息的功能添加到接口中。
在movie/views.py中使用PUT方法来进行修改,同样需要指定一个ID来确定要修改的电影。

  • 首先,判断电影是否存在,如果不存在则报错。
  • 然后,使用序列化器对用户提交的数据进行验证。
  • 验证通过后,进行提交操作。
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
@api_view(['GET', 'POST'])
def movie_list(request):
# 如果请求方法为GET
if request.method == 'GET':
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回序列化后的数据和状态码200 OK
return Response(serializer.data, status=status.HTTP_200_OK)
elif request. method == 'POST':
serializer = MovieListSerializer (data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status HTTP_400_BAD_REQUEST)
# 定义MovieDetail类,继承自APIView类
class MovieDetail(APIView):
# 定义get方法
def get(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化
serializer = MovieDetailSerializer(movie)
# 返回序列化后的数据作为响应
return Response(serializer.data)

# 定义put方法,接收请求、电影主键pk作为参数
def put(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化,传入请求数据
serializer = MovieDetailSerializer(movie, data=request.data,partial=True)
# 如果序列化器验证通过
if serializer.is_valid():
# 保存序列化后的数据
serializer.save()
# 返回成功的响应,状态码为HTTP 202 ACCEPTED
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
# 如果验证不通过,返回错误信息响应,状态码为HTTP 400 BAD REQUEST
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

同样的流程适用于删除功能,使用DELETE方法来删除电影信息。同样需要指定一个ID来确定要删除的电影。

  • 首先判断电影是否存在,存在则进行删除操作,然后返回相应的响应码。
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
@api_view(['GET', 'POST'])
def movie_list(request):
# 如果请求方法为GET
if request.method == 'GET':
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回序列化后的数据和状态码200 OK
return Response(serializer.data, status=status.HTTP_200_OK)
elif request. method == 'POST':
serializer = MovieListSerializer (data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status HTTP_400_BAD_REQUEST)
# 定义MovieDetail类,继承自APIView类
class MovieDetail(APIView):
# 定义get方法
def get(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化
serializer = MovieDetailSerializer(movie)
# 返回序列化后的数据作为响应
return Response(serializer.data)

# 定义put方法,接收请求、电影主键pk作为参数
def put(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 使用MovieDetailSerializer对电影进行序列化,传入请求数据
serializer = MovieDetailSerializer(movie, data=request.data,partial=True)
# 如果序列化器验证通过
if serializer.is_valid():
# 保存序列化后的数据
serializer.save()
# 返回成功的响应,状态码为HTTP 202 ACCEPTED
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
# 如果验证不通过,返回错误信息响应,状态码为HTTP 400 BAD REQUEST
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# 定义delete方法,接收请求和电影主键pk作为参数
def delete(self, request, pk):
try:
# 尝试获取指定主键(pk)对应的电影
movie = Movie.objects.get(pk=pk)
except:
# 如果未找到对应的电影,抛出Http404异常
raise Http404
# 删除电影对象
movie.delete()
# 返回成功的响应,状态码为HTTP 204 NO CONTENT
return Response(status=status.HTTP_204_NO_CONTENT)

综上实现了查看、修改和删除功能,使用了继承自APIView的视图类,并创建了不同的方法来实现不同的功能。这是操作视图的一种方式,即继承APIView类。

使用通用的视图实现

对于数据的增长改查,可以说是每个项目的通用操作。如果有多个模型,每个都这么写get、put、delete,那是非常麻烦的,也体现不出DRF(Django REST Framework)它的优势。所以可以把一些相同的操作提取出来,可以把核心的内容给它提取出去,然后创建一个单独的类。

在movie/views.py文件中来新建一个 MovieDetail 类。在原始数据中还需要对模型进行各种操作,增删改查。
这个时候需要引入一个新的类,这个类叫做 mixins 也是复合类。它可以对模型的各种操作,所以对模型操作直接导入这个 Mixins里面的已经写好类就可以执行这样的操作了。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import mixins
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
@api_view(['GET', 'POST'])
def movie_list(request):
# 如果请求方法为GET
if request.method == 'GET':
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回序列化后的数据和状态码200 OK
return Response(serializer.data, status=status.HTTP_200_OK)
elif request. method == 'POST':
serializer = MovieListSerializer (data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status HTTP_400_BAD_REQUEST)

# 定义MovieDetail类,继承自RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin和GenericAPIView
class MovieDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
# 指定查询集为Movie模型的所有对象
queryset = Movie.objects.all()
# 指定序列化器为MovieDetailSerializer
serializer_class = MovieDetailSerializer

# 定义get方法,用于获取电影信息
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)

# 定义put方法,用于更新电影信息
def put(self, request, *args, **kwargs):
return self.partial.update(request, *args, **kwargs)

# 定义delete方法,用于删除电影信息
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)

上面的这些方法可以再进行简化。
在movie/views.py文件中来来修改,在generics.py中寻找能够替代这些操作的类。发现有RetrieveUpdateDestroyAPIView这个类,它相当于刚才继承了一系列类的情况。因为它的父类已经实现了这些操作,可以直接导入这个类,而无需再单独实现这些方法。这样操作后,代码量显著减少,但功能依然完整。

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
41
# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import mixins
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
@api_view(['GET', 'POST'])
def movie_list(request):
# 如果请求方法为GET
if request.method == 'GET':
# 获取所有电影数据
movies = Movie.objects.all()
# 使用MovieListSerializer对电影数据进行序列化
serializer = MovieListSerializer(movies, many=True)
# 返回序列化后的数据和状态码200 OK
return Response(serializer.data, status=status.HTTP_200_OK)
elif request. method == 'POST':
serializer = MovieListSerializer (data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status HTTP_400_BAD_REQUEST)

# 定义MovieDetail类,继承自RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin和GenericAPIView
class MovieDetail(generics.RetrieveUpdateDestroyAPIView):

# 指定查询集为Movie模型的所有对象
queryset = Movie.objects.all()
# 指定序列化器为MovieDetailSerializer
serializer_class = MovieDetailSerializer

在postman中,尝试使用put方法来修改电影信息,当点击提交后,报错了,提示错误信息是电影的其他字段必须填写。
引入的是RetrieveUpdateDestroyAPIView这个类,找到这个源码发现它这里要是提交更改部分信息使用的是patch方法。
所以试一下把put修改为patch,点击send,这一次就修改成功了,这也是put和patch这两种方式的区别。

当有不同的需求的时候,比如说要更改整个电影信息的话那么就使用put方法,如果只需要更改部分就使用patch方法。

如果想要修改MovieList方法,也可以采取相同的方法。

  • 首先注册这个视图,然后创建一个新的类来继承ListCreateAPIView,因为ListCreateAPIView已经包含了列表和新增的功能。
  • 接着只需要定义QuerySet和SerializerClass,就完成了对该接口的定义。
    这种方式使得只需简单地选择合适的类名进行继承,而无需再手动编写大量代码。
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
# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import mixins
# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovielistSerializer,MovieletailSerializer

# 定义电影列表视图
class MovieList (generics.ListCreateAPIView):
queryset = Movie. objects.all()
serializer_class = MovieListSerializer
# 定义MovieDetail类,继承自RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin和GenericAPIView
class MovieDetail(generics.RetrieveUpdateDestroyAPIView):

# 指定查询集为Movie模型的所有对象
queryset = Movie.objects.all()
# 指定序列化器为MovieDetailSerializer
serializer_class = MovieDetailSerializer

接下来再来测试这个接口能不能实现,回到postman中,找查看电影列表,点击发送,服务器已经断开了,解决这个错误需要修改一下url,把movie_list改成MovieList,找到movie/urls.py文件,来修改代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 导入 Django 中的路径模块和 movie 应用中的视图模块
from django.urls import path
from movie import views

# 设置 app 的命名空间
app_name = 'movie'

# 定义 URL 路由列表
urlpatterns = [
# 定义空路径,对应电影列表视图,并设置名称为 'list'
path('', views.MovieList.as_view(), name='list'),
# 定义带有电影 ID 参数的路径,对应电影详情视图,并设置名称为 'detail'
path('<int:pk>/', views.MovieDetail.as_view(), name='detail'),
]

综上使用了DRF中的通用视图类,通过继承相应的类,实现了各种接口功能。DRF的便利之处在于,只需定义少量代码,就能够实现完整的接口规范,包括列表查看、新增、修改和删除等功能。

使用视图集实现

称其为视图集是因为它将一系列操作进行了集合处理,包括列表展示、新增数据、查看详情、修改信息、删除等。可以将这些操作看作是一个集合,而视图集的作用就是将它们整合在一起。
实际上,对于REST接口来说,只是在操作一个接口,即/api/moive,对电影进行各种操作。视图集的作用就是将这些功能整合在一起。

新定义一个视图集,命名为MovieViewSet。让它继承DRF提供的ModelViewSet,并导入相应的模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 从Django中导入render函数
from django.shortcuts import render
# 从Django中导入JsonResponse类
from django.http import JsonResponse,Http404
# 从rest_framework.decorators模块中导入@api_view装饰器
from rest_framework.decorators import api_view
# 从rest_framework模块中导入status常量和Response类
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import mixins
from rest framework import viewsets

# 从当前目录下导入Movie模型和MovieListSerializer序列化器
from .models import Movie
from .serializers import MovieSerializer

class MovieViewSet(viewsets.ModelViewSet):
queryset = Movie.objects.all()
serializer_class = MovieSerializer

在queryset 中使用了之前的查询对象,即Movie.objects.all。
在serializer_class 中,新定义了一个MovieSerializer,用于对电影进行序列化。这个MovieSerializer同样继承了DRF提供的ModelSerializer。

接下来在movie /serializers.py文件中来修改代码:

1
2
3
4
5
6
7
8
9
10
11
12
# 导入 REST Framework 中的序列化模块和 movie 应用中的电影模型
from rest_framework import serializers
from movie.models import Movie

# 定义电影序列化器类
class MovieSerializer(serializers.ModelSerializer):

class Meta:
# 指定序列化器所使用的模型
model = Movie
# 指定需要序列化的字段为所有字段
fields = '__all__'

接下来定义路由。
在全局的URL配置中,使用了DRF提供的DefaultRouter。这样,就可以通过路由注册的方式将接口注册到全局路由中。这样的话,在创建新的路由时,只需在全局路由下进行添加,而访问API后面的对应接口时,就会自动使用include进行包含。
在urls.py文件中修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 导入 Django 中的管理模块、路径模块和包含默认路由器的 REST Framework 模块
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from movie import views

# 创建默认路由器对象
router = DefaultRouter()
# 注册 movie 视图集到路由器中,并指定路径前缀为 'movie'
router.register(r'movie', views.MovieViewSet)

# 定义 URL 路由列表
urlpatterns = [
# 设置 admin 管理路径
path('admin/', admin.site.urls),
# 设置 API 路径,包括注册的视图集路由
path('api/', include(router.urls))
]

最后,将原始的movie/urls.py文件内容全部注释掉,因为通过视图集的方式,DRF会按照其规则将所有电影的增删改查操作自动进行路由注册。

总的来说,视图集的最大优势在于最大程度地减少了所需的代码量,使能够专注于API的交互和表现形式,甚至可以忽略URL的细节。然而,由于抽象程度较高,一些需要定制化的需求可能需要特殊处理。在选择使用views、genericviews还是viewsets时,可以根据实际情况进行选择。