Django&DRF

如何学习框架

  • 如何学习框架

    • 如何搭建工程程序
      • 工程的组建
      • 工程的配置
      • 路由定义
      • 视图函数定义
    • 如何获取请求数据(request对象)
    • 如何构造响应数据(response对象)
    • 如何使用中间层
    • 框架提供的其他功能组件的使用
      • 数据库
      • 模板
      • 表单
      • admin
  • Django文档

  • Django开发包

    # Django
    pip install django=1.11.11
    
    # djangorestframework
    pip install djangorestframework
    
    # PyMySQL
    pip install PyMySQL
    
    # redis
    pip install django-redis
    
    # 图片相关
    pip install pillow
    

1. 搭建工程

  • 通过bash搭建

    mkvirtualenv django_py3_1.11 --python=/usr/bin/python3  # 创建独立环境
    pip install django==1.11.11  # 安装django包
    django-admin startproject hello_world  # 创建工程
    python manage.py startapp users  # 创建应用
    
    # users/apps.py
    class UserConfig(AppConfig):
    	name = 'users'  # 用于注册应用
    	verbose_name = '用户管理'  # 用于admin站点的管理
    
  • Django工程的启动

    python manage.py runserver ip:端口
    
  • Django工程目录结构调整

    • celery_tasks # 异步任务
    • front_end_pc # 前端资源
    • django_project # 后端工程
      • docs # 文档
      • logs # 日志
      • django_project # 与项目同名子应用
        • apps # 项目应用
          • users
        • libs # 第三方
        • settings # 应用配置
        • utils # 工具
      • scripts # 脚本
      • manage.py

2. 工程配置

  • manage.py

    # 指定项目的配置文件
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "meiduo_mall.settings.dev")
    
  • settings.py

    # 指定项目以什么目录为根目录 dirname:获取目录路径,abspath:获取文件路径
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # 获取项目目录
    
    # 添加导包路径, 导包时可从新增加的路径向下寻找,如: BASE_DIR/apps
    import sys
    sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
    
    # 项目开启DEBUG模式
    DEBUG = True  # 项目开启DEBUG模式
    
    # 注册app
    INSTALL_APPS = [
        # 指定session的保存位置,3选1
    	SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # session默认保存在数据库中 
    	SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # session保存在缓存中
    	SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'  # 先缓存,没有再数据库
        
        'rest_framework',
        'users.apps.UsersConfig',
    ]
    
    # 允许在哪些服务器上访问项目接口, '*' 表示任意
    ALLOWED_HOSTS = ['api.meiduo.site', '127.0.0.1', 'localhost']
    
    # 设置语言与时区
    LANGUAGE_CODE = 'zh-hans'  # 设置语言
    TIME_ZONE = 'Asia/Shanghai'  # 设置时区
    
    # 设置静态文件访问
    STATIC_URL = '/static/'  # 通过静态路径访问静态资源  非DEBUG模式下不提供访问静态文件的功能
    STATICFILES_DIRS = [  # 决定静态资源的文件夹
    	os.path.join(BASE_DIR, 'static_files')
    ]  # 在根目录下创建static_files文件夹
    
    # 中间件
    MIDDLEWARE = [
    	'django.midd.....csrf'  # 默认开启CSRF, 注释关闭
        'django.contrib.sessions...'  # 默认开启session
    ]
    
    # redis存储session
    # pip install django-redis
    CACHES = {
    	"default": {
    		"BACKEND": "django_redis.cache.RedisCache",
    		"LOCATION": "redis://127.0.0.1:6379/1",
    		"OPTIONS": {
    			"CLIENT_CLASS": "django_redis.client.DefaultClient",
    			"PASSWORD": ""
    		}
    	}
    }
    SESSEION_ENGINE = "django.contrib.sessions.backends.cache"
    SESSION_CACHE_ALIAS = "default"
    
    # 数据库配置
    DATABASES = {
    	'default': {
    		'ENGINE': 'django.db.backends.mysql',
    		'HOST': '127.0.0.1',
    		'PORT': 3306,
    		'USER': 'root',
    		'PASSWORD': 'mysql',
    		'NAME': 'django_demo'
    	}
    }
    
    # 配置日志
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,  # 是否禁用已经存在的日志器
        'formatters': {  # 日志信息显示的格式
            'verbose': {
                'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
            },
            'simple': {
                'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
            },
        },
        'filters': {  # 对日志进行过滤
            'require_debug_true': {  # django在debug模式下才输出日志
                '()': 'django.utils.log.RequireDebugTrue',
            },
        },
        'handlers': {  # 日志处理方法
            'console': {  # 向终端中输出日志
                'level': 'INFO',
                'filters': ['require_debug_true'],
                'class': 'logging.StreamHandler',
                'formatter': 'simple'
            },
            'file': {  # 向文件中输出日志
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',
                'filename': os.path.join(os.path.dirname(BASE_DIR), "logs/meiduo.log"),  # 日志文件的位置
                'maxBytes': 300 * 1024 * 1024,
                'backupCount': 10,
                'formatter': 'verbose'
            },
        },
        'loggers': {  # 日志器
            'django': {  # 定义了一个名为django的日志器
                'handlers': ['console', 'file'],  # 可以同时向终端与文件中输出日志
                'propagate': True,  # 是否继续传递日志信息
                'level': 'INFO',  # 日志器接收的最低日志级别
            },
        }
    }
    
    # DRF异常处理
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'meiduo_mall.utils.exceptions.exception_handler',
    }
    
    AUTH_USER_MODEL = 'users.User'  # 告知Django认证系统使用我们自定义的模型
    
  • 项目同名子目录下__init__.py增加数据库的配置

    import pymysql
    
    pymysql.install_as_MySQLdb()
    
  • utils/exceptions.py

    修改Django REST framework的默认异常处理方法,补充处理数据库异常和Redis异常。

    from rest_framework.views import exception_handler as drf_exception_handler
    import logging
    from django.db import DatabaseError
    from redis.exceptions import RedisError
    from rest_framework.response import Response
    from rest_framework import status
    
    # 获取在配置文件中定义的logger,用来记录日志
    logger = logging.getLogger('django')
    
    def exception_handler(exc, context):
        """
        自定义异常处理
        :param exc: 异常
        :param context: 抛出异常的上下文
        :return: Response响应对象
        """
        # 调用drf框架原生的异常处理方法
        response = drf_exception_handler(exc, context)
    
        if response is None:
            view = context['view']
            if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
                # 数据库异常
                logger.error('[%s] %s' % (view, exc))
                response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
    
        return response
    

3. 路由

  • 定义路由

    # url包含关系写法
    urlpatterns = [
        url(r'^', include('users.urls'))  # 不使用前缀的写法
    	url(r'^users/', include('users.urls', namespace='users'))  # 包含命名空间
    ]
    
    # 定义具体路由
    # 工程/应用/urls.py
    from . import views
    urlpatterns = [
        # 定义函数视图路由
    	url(r'^index/$', views.index, name='index')  # 定义普通路由,及路由名称
        # 定义类视图路由
        url(r'^index/$', views.RegisterView.as_view(), name='register')
    ]
    
  • 反解析路由

    # 路由反解析, 可以没有命名空间
    url = reverse("index")
    url = reverse("users:index")
    
    

4. 视图

  • 函数视图

    def index(request)
    	"""
    	:params request: 用于接收请求HttpRequest对象
    	:return: 返回响应HttpResponse对象
    	"""
    	return HttpResponse('hello Django')
    
    
  • 类视图

    from django.views.generic import View
    
    class RegisterView(View):
    	def get(self, request):
    		"""处理GET请求"""
    		return render(request, 'register.html')
    		
    	def post(self, request):
    		"""处理POST请求"""
    		return HttpResponse('pose注册逻辑')
    
    
  • 装饰器装饰视图

    # 装饰器
    def my_decorator(view_func):
    	def wrapper(request, *args, **kwargs):
    		print('装饰器被调用')
    		return view_func(request, *args, **kwargs)
    	return wrapper
    
    
    class RegisterView(View):
        # 装饰到一个指定函数中
    	@method_decorator(my_decorator)
    	def get(self, request):
    		return render(request, 'register.html')
    		
    	def post(self, request):
    		return HttpResponse('pose注册逻辑')
    
    
    class RegisterView(View):
        # 重写dispatch方法实现全部装饰
    	@method_decorator(my_decorator)
    	def dispatch(self, request, *args, **kwargs)
    		return super().dispatcher(request, *args, **kwargs)
    	
    	def get(self, request):
    		return render(request, 'register.html')
    		
    	def post(self, request):
    		return HttpResponse('pose注册逻辑')
    
    # 在class上实现装饰
    @method_decorator(my_decorator, name='get')  # 在get上添加装饰
    @method_decorator(my_decorator, name='dispatcher')  # 在所有方法上添加装饰
    class RegisterView(View):
    
    	def dispatch(self, request, *args, **kwargs)
    		return super().dispatcher(request, *args, **kwargs)
    	
    	def get(self, request):
    		"""处理GET请求"""
    		return render(request, 'register.html')
    		
    	def post(self, request):
    		return HttpResponse('pose注册逻辑')
    
    
  • 构造Mixin扩展类装饰视图

    class BaseViewMixin(object):
    	@classmethod
    	def as_view(cls, *args, **kwargs):
    		view = mydecorator(super().as_view(cls, *args, **kwargs))
    		return view
    
    class BaseView2Mixin(object):
    	@classmethod
    	def as_view(cls, *args, **kwargs):
    		view = mydecorator2(super().as_view(cls, *args, **kwargs))
    		return view
    
    class RegisterView(BaseViewMixin, BaseView2Mixin, View):
    	pass
    
    

    路由中的as_view方法会找到BaseViewMixin的as_view重写方法,由于python的多继承机制,BaseViewMixin和BaseView2Mixin和view都继承自object同一个父类,所以又会调用BaseView2Mixin的as_view方法,最后调用View的as_view方法,从而保证所有装饰器的调用

5. 获取请求参数

  • 封装的request对象

    视图中

    def index(request):
        return HttpResponse('index')
    
    
  • 获取URL路径参数

    # 不命名参数提取参数
    url(r'^weather/([a-z]+)/(\d{4})/$', views.weather)
    def weather(request, city, year):  # 按顺序一个一个提取
    	return HttpResponse("%s %s"%(city, year))
    
    # 命名参数写法 ?P<year> 提取参数
    url(r'^weather/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.weather)
    def weather(request, year, city):  # 顺序不定,可以不用按照规定顺序
    	retrun
    
    
  • 获取查询字符串参数

    # /weather/beijing/2018/?key1=1&key1=11&key2=2
    value2 = request.GET.get('key2')  # value2:
    value2_list = request.GET.getlist('key1')  # ['1', '11']
    
    
  • 获得表单格式的请求体数据

    value1 = request.POST.get('key1')  # value1:1
    value1_list = request.POST.getlist('key1')  # a:['1', '2']
    
    
  • 获取非表单格式请求体数据 如:json/xml等格式的数据

    json_bytes = request.body
    json_str = json_bytes.decode()
    req_dict = json.loads(json_str)
    value3 = req_dict.get('c')
    value4 = req_dict.get('d')
    
    
  • 获取请求头数据

    request_header_data = request.META.get('CONTENT_TYPE')
    
    

    常用请求头

    • CONTENT_LENGTH – The length of the request body (as a string).
    • CONTENT_TYPE – The MIME type of the request body.
    • HTTP_ACCEPT – Acceptable content types for the response.
    • HTTP_ACCEPT_ENCODING – Acceptable encodings for the response.
    • HTTP_ACCEPT_LANGUAGE – Acceptable languages for the response.
    • HTTP_HOST – The HTTP Host header sent by the client.
    • HTTP_REFERER – The referring page, if any.
    • HTTP_USER_AGENT – The client’s user-agent string.
    • QUERY_STRING – The query string, as a single (unparsed) string.
    • REMOTE_ADDR – The IP address of the client.
    • REMOTE_HOST – The hostname of the client.
    • REMOTE_USER – The user authenticated by the Web server, if any.
    • REQUEST_METHOD – A string such as “GET” or “POST”.
    • SERVER_NAME – The hostname of the server.
    • SERVER_PORT – The port of the server (as a string).

    用户自定义请求头a, 获取方式为:

    • HTTP_A
  • 获取其它请求信息

    request.method  # GET, POST
    request.user  # 请求用户对象
    request.path  # str请求页面的完整路径,不包含域名和参数部分
    request.encoding  # 一个提交数据的编码方式
    
    

6. 构造响应数据

  • HttpResponse响应

    import django.http.HttpResponse
    
    resp =  HttpResponse(content='{"name":"zs"}',        # 响应体
                        content_type='application/json', # 响应体数据类型
                        status=400)						 # 状态码
    resp['it'] = 'python'  # 设置响应头
    
    

    content_type:

    • text/html html
    • text/plain 普通文本
    • application/json json
  • HttpResponseRedirect 重定向

    from django.shortcuts import redirect
    
    def resp(request):
        url = reverse('index')  # 动态生成URL地址,解决url硬编码维护麻烦的问题。
    	return redirect(url)
    
    
  • JsonResponse 返回json数据

    帮助我们将dict转换为json字符串,再返回给客户端

    自动设置响应头 Content-Type 为 application/json

    from django.http import JsonResponse
    
    resp = JsonResponse({'city': 'beijing', 'subject': 'python'},
                        json_dumps_params={'ensure_ascii':False})  # 返回中文,而非中文编码
    
    

    中文: 张三

    中文编码: \u5f20\u4e09

7. 中间件

7.1 状态保持

  • Cookie

    # 设置Cookie
    HttpResponse.set_cookie(key, value, max_age=6)  # 设置名为key的cookie值为value,有效期为6s
    # 读取Cookie
    request.COOKIES.get('key')
    # 删除Cookie
    response.delete_cookie('mark')
    # 是否有某个cookie
    request.COOKIES.has_key('mark')  # true False
    
    
  • Session

    配置请参照工程配置部分

    # 存储session
    request.session['key1'] = value1
    
    # 读取session
    request.session.get('key1', default1)  # 没有对应key1的session时返回默认值default1
    
    # 清除所有session 值部分
    request.session.clear()
    
    # 清除所有session 数据
    request.session.flush()
    
    # 删除指定session
    del request.session['key1']
    
    # 设置有效期
    request.session.set_expiry(time)
    	# time为一个整数,默认None 2周过期,0表示浏览器关闭时过期
    	# 在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值,单位s
    
    

7.2 请求勾子

  • 定义中间件

    middleware.py

    def simple_middleware(get_response):
    	# 此处处理Django第一次配置和初始化的时候执行
    	
    	def middleware(request):
    		# 此处编写代码会在每个请求处理视图前被调用
    		
    		response = get_response(request)
    		# 此处编写的代码会在每次请求处理之后被调用
    		
    		return response
    	return middleware
    
    
  • 注册中间件

    settings.py

    多个中间件的执行顺序: 视图执行前,自上而下调用, 视图执行后,自下而上调用

    MIDDLEWARE = [
    	'middleware.simple_middleware'
    ]
    
    

8. 模板

  • 使用示例

    settings.py

    TEMPLATES = [
    	'DIRS': [os.path.join(BASE_DIR, 'templates')],
    ]
    
    

    视图

    # Django原始方法
    def index(request):
    	# 1.获取模板
    	template = loader.get_template('booktest/index.html')
    	# 2.定义上下文 (django1.11版本,此步不需要, context直接是一个字典)
    	context = RequestContext(request, {'city':'beijing'}
    	# 3.渲染模板
    	return HttpResponse(template.render(context))
    
    # Django封装了 render(request, 模板文件路径, 模板数据字典) 返回模板
    def index(request):
    	data = {
    		'city': 'beijing',
    		'alist': [1,2,3],
    		'adict': {
    			'name': 'python'
    		}
            'adate': datetime.datetime.now()
    	}
    	render(request, index.html, data)
    
    
  • 模板语法

    • 模板变量

      {{ city }}
      {{ alist.0 }}  # 列表元素的取值
      {{ adict.0 }}  # 字典元素取值
      {{ adict.name }}  # 字典元素取值
      
      
    • if, for 控制语句

      {% for item in 列表 %}
      	{{forloop.counter}}  # 表示循环到第几次,从1开始,forloop.counter0表示从0开始
      {% empty %}
      	# 列表为空或不存在时执行此逻辑
      {% endfor %}
      
      {% if ... %}
      {% elif ... %}
      {% else %}
      {% endif %}
      # if的条件: == != > < >= <= and or not
      
      

      注意if控制中必须格式正确, {% if num==1 %}报错,必须写成{% if num == 1 %}

    • 过滤器

      {# 语法: 变量|过滤器:参数 #}{#  #}
      {{ city|safe }}  {# safe: 禁止html转义(显示html标签样式,而不是字符串) #}
      {{ city|length }}  {# 长度(str,list,dict个数或长度) #}
      {{ city|default:'beijing' }}  {# 给变量设置默认值 #}
      {{ adate|date:"Y年m月j日 H时i分s秒" }}  {# Y(2019)y(19)m(01-12)d(01-31)j(1-31)H(0-23)h(0-12)i(0-59)s(0-59) #}
      
      
    • 注释

      {# 单行注释 #}
      
      {% comment %}
      多选注释
      {% endcomment %}
      
      
    • 继承

      {% block b1 %}
      父模板block
      {% endblock b1 %}
      
      
      {% extends 父模板 %}
      {% block b1 %}
      	# 子模板显示父模板的内容
      	{{ block.super }}
      	# 子模板重写父模板内容
      {% endblock b1 %}
      
      

9.数据库

9.1 配置迁移

  • 使用shell工具

    python manage.py shell
    # 配置好环境,可直接对数据库进行CRUD操作 类似于ipython3
    
    
  • 数据库配置

    # 安装mysql数据库驱动 pip install PyMySQL
    
    # 在Django工程同名子目录__init__.py中添加如下代码
    from pymysql import install_as_MySQLdb
    install_as_MySQLdb()
    
    # settings.py中配置DABABASES,详情查看工程配置部分
    
    
  • 数据库迁移

    python manage.py makemigrations
    python manage.py migrate
    
    

9.2 ORM

  • 定义模型类

    django自动生成主键,若自定义则覆盖默认

    • 字段类型
    字段类型特殊参数说明
    AutoField主键, 通常不用指定,Django自动会生成主键
    BooleanFieldTrue或False
    NullBooleanFieldNull,True,False三种字段
    CharFieldmax_length=10
    TextField一般超过4000时使用
    IntegerField
    DecimalFieldmax_digits=None
    decimal_places=None
    max_digists:总位数
    decimal_places:小数位数
    FloatField
    DateFieldauto_now=False
    auto_now_add=False
    auto_now:修改时间
    auto_now_add:创建的时间
    两者不可同时指定
    TimeField同上
    DateTimeField同上
    FileField
    ImageFieldupload_to=‘booktest’
    ForeignKey一对多
    ManyToManyField多对多
    • 选项(字段共有参数)
    选项(参数)含义注意
    null是否允许为空True允许,False不允许,默认False
    blank是否允许为空白同上
    db_column字段名称默认为属性名称
    db_index是否在表中为此字段创建索引默认False
    default字段默认值
    primary_key是否为主键默认False,一般不用指明,Django自动生成主键
    unique是否唯一默认False(不唯一)
    on_delete是否级联删除CASCADE:级联删除, PROTECT:保护,删除时抛出异常,
    • 示例
    class BookInfo(models.Model):
    	btitle = models.CharField(max_length=20, verbose_name='名称')  # verbose_name admin显示名称
    bpub_date = models.DateField(verbose_name='发布日期')
    	bread = models.IntegerField(default=0, verbose_name='阅读量')
    	bcomment = models.IntegerField(default=0, verbose_name='评论量')
    	is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
    	
    	class Meta: #元信息类
    		db_table = 'tb_books'  # 数据库表名
    		verbose_name = '图书'  # admin显示表名
            verbose_name_plural = verbose_name  # 指明人性化复数显示
    		
    class HeroInfo(models.Model):
    	GENDER_CHOICES = (
    		(0, 'male')
    		(1, 'femle')
    	)
    	hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
    	
    	hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # CASCADE级联删除,默认不允许直接删除
    
    
  • 操作模型类

    模型类管理器: objects实现对数据的操作

    objects方法返回类型作用
    模型类.objects.create()模型类对象新增一条数据
    模型类.objects.get()模型类对象查询一个对象
    若查到多个或空,则抛出异常
    模型类.objects.all()QuerySet查询所有的对象
    模型类.objects.count()数字查询总共有多少条数据
    模型类.objects.filter()QuerySet查询满足条件的对象
    模型类.objects.exclude()QuerySet查询不满条件的对象
    模型类.objects.order_by()QuerySet对查询结果集进行排序
    模型类.objects.aggregate()字典,例如:进行聚合操作 Sum, Count, Max, Min, Avg
    {‘salary__avg’: 9500.0}
    • 自定义管理器
    class BookInfoManager(models.Manager):
    	# 自定义管理器类,修改过滤方法
    	def all(self):
    		retrun super().filter(is_delete=False)
    	
    	def create_book(self, title, pubdate):
    		# 设置特定的过滤方法
    		book = self.model()  # 创建模型类对象
    		book.btitle = title
    		book.bpub_date = pub_date
    		book.bread = 0
    		book.bcomment = 0
    		book.is_delete = False
    		book.save()  # 将数据插入数据库表
    		return book
    
    
    class BookInfo():
    	objs = BookInfoManager()  # 自定义管理器
    
    

9.3 Django用户模型类

  • 创建自定义用户模型类

    models.py

    from django.contrib.auth.models import AbstractUser
    
    class User(AbstractUser):
        """用户模型类"""
        mobile = models.CharField(max_length=11, unique=True,
                                  verbose_name='手机号')
    
        class Meta:
            db_table = 'tb_users'
            verbose_name = '用户'
            verbose_name_plural = verbose_name
    
    

    settings.py

    AUTH_USER_MODEL = 'users.User'  # 告知Django认证系统使用我们自定义的模型
    
    

9.4 数据的增删改

  • 增加

    # 方法一: save
    hero = HeroInfo(hname='zhangsan')  # 模型类对象
    hero.save()  # 保存模型类对象到数据库相应数据
    
    # 方法二: create
    HeroInfo.objects.create(
    	hname='zhangsan'
    )
    
    
  • 修改

    # 方法一: save
    hero = HeroInfo.objects.get(hname='song')
    hero.hname = 'wu'
    hero.save()
    
    # 方法二: update
    HeroInfo.objects.filter(hname='song').update(hname='wu')
    
    
  • 删除

    # 方法一: delete
    hero.delete()
    
    # 方法二: delete
    HeroInfo.objects.filter(id=1).delete()
    
    

9.5 查询数据

  • 基本查询

    all, get, count

    all_book = BookInfo.objects.all()  # all查询所有结果
    b1 = all_book[0]  # 取出查询集中第一个元素
    bs = all_book[0:2]  # 切片操作取出元素
    
    b2 = BookInfo.objects.get(pk=1)
    b2 = BookInfo.objects.get(id=1)  # 与pk一样
    b2 = BookInfo.objects.get(btitle='xiyou')
    
    bcount = BookInfo.objects.count()  # 返回个数
    
    
  • 过滤查询

    filter

    # filter
    books = BookInfo.objects.filter(id__exact=1)  # 相等exact
    books = BookInfo.objects.filter(id=1)  # 效果同上
    books = BookInfo.objects.filter(btitle__contains='you')[0]  # 包含:contains
    books = BookInfo.objects.filter(btitle__startswith='you')  # startswith endswith
    books = BookInfo.objects.filter(btitle__isnull=False)  # 判空: isnull
    books = BookInfo.objects.filter(id__in=[1,2,3,4])  # 范围查询: in
    books = BookInfo.objects.filter(id__gt=10)  # 比较查询 gt, gte, lt, lte (> >= < <=)
    books = BookInfo.objects.filter(id__exclude=10)  # 不等 exclude
    books = BookInof.objects.filter(bpub_date__year=1980)  # 日期查询 year month, day, week_day, hour, minute, second
    from datetime import date
    books = BookInfo.objects.filter(bpub_date__gt=date(1990,1,1))
    
    
  • F对象Q对象

    F对象: 比较两个字段的值

    Q对象: 多个查询条件

    from django.db.models import F, Q
    
    # F()
    BookInfo.objects.filter(bread_gte=F('bcomment'))
    BookInfo.objects.filter(bread_gte=F('bcomment')*2)
    
    # 与 Q() & Q()
    BookOnfo.objects.filter(id=5, btitle__contains='you')
    BookInfo.objects.filter(Q(id=5) & Q(bread__gte=5))
    
    # 或 Q() | Q()
    BookInfo.objects.filter(Q(id=5) | Q(bread__gte=5))
    
    # 非 !Q()
    BookInfo.objects.filter(~Q(id=5))
    
    
  • 聚合函数

    # 聚合函数: Avg,Max,Min,Sum,Count
    BookInfo.objects.aggregate(Sum('bread'))
    
    
  • 排序查询

    BookInfo.objects.all().order_by('bread')  # 升序
    BookInfo.objects.all().order_by('-bread')  # 降序
    
    
  • 关联查询

    # 一查多  一的一方模型类对象.多的一方模型类名小写_set.all()
    heros = book.heroinfo_set.all()
    # 多查一  多的一方模型类对象.多的一方定义的一的一方的属性名
    book = hero.hbook
    # 跨表关联查询
    books = BookInfo.objects.filter(heroinfo__hname__contains('song'))
    heros = HeroInfo.objects.filter(hbook__btitle__contails('you'))
    
    

10. Admin站点

  • 创建Admin超级管理员

    python manage.py createsuperuser
    
    
  • 注册模型类

    admin.py

    admin.site.register(BookInfo, BookInfoAdmin)
    admin.site.register(HeroInfo)
    
    class BookInfoAdmin(admin.ModelAdmin):  # @admin.register(BookInfo) 也可以装饰器注册
    	pass
    
    
  • 自定义管理页面

    • 展示对象信息

      class BookInfo():
      	def __str__(self):
      		return self.btitle
      
      
    • 调整列表展示页面

      class BookInfoAdmin(admin.ModelAdmin)
      	# 每页展示的数据个数
      	list_per_page = 10
      	
      	# `操作选项` 的位置
      	action_on_top = True
      	action_on_bottom = True  # top和bottom可以同时存在
      	
      	# 展示信息字段
      	list_display = ['id', 'btitle']
      	
      	# 把方法当为一列展示
      	list_display = ['id', 'btitle', 'pub_date']
      	# 模型类中定义方法
      	class BookInfo():
      		def pub_date(self):
      			return self.bpub_date.strftime('%Y年%m月%d日')  # strftime时间转为字符串
      		# 给函数对象添加属性改变展示名称
      		pub_date.short_description = '发布日期'
      		# 给函数对象添加属性使其可排序
      		pub_date.admin_order_field = 'bpub_date'
      		
      # 展示关联对象
      Class HeroInfo():
      	list_display = ['id', 'hbook', 'read']
      	def read(self):
      		return self.hbook.bread
      	read.short_description= ''
      	
      # 右侧栏过滤器
      # list_filter = []
      Class HeroInfoAdmin():
      	list_filter = ['hbook', 'hgender']
      	
      # 搜索框
      Class HeroInfoAdmin():
      	search_fields = ['hname']
      
      
    • 调整编辑页面

      # 控制编辑显示字段
      class BookInfoAdmin():
      	fields = ['btitle']
      	
      # 分组显示
      	fieldset = (
      		('组1标题', {'fields':('字段1','字段2')}),
      		('组2标题', {
      					'fields':('字段1','字段2')}
      					'classes': ('collapse')  # 是否折叠显示
      					)
      	)
      	
      # 关联对象
      # StackedInline 以块形式嵌入
      # TabularInline 以表格的形式嵌入
      class HeroInfoStackInline(admin.StackedInline):
      	model = HeroInfo  # 要编辑的对象
      	extra = 1  # 附加编辑的数量
      	
      class BookInfoAdmin(admin.ModelAdmin):
      	inlines = [HeroInfoStackInline]
      
      
    • 调整整体页面标题等

      admin.site.site_header  # 设置网站页头
      admin.site.site_title  # 设置页面标题
      admin.site.index_title  # 设置首页标语
      
      

11. 表单

  • 定义表单

    forms.py

    from django import forms
    
    class BookForm(forms.Form):
    	title = forms.ChaiField(label='书名', required=True, max_length=50')
    	pub_date = forms.DateField(label='出版日期', required=True)
    	
    	class Meta:
    		model = BookInfo  # 指明属于哪个模型类
    		field = ('btitle', 'bpub_date')  # 指明向表单中添加模型类的哪个字段
    
    
  • 视图处理

    class BookView(View):
    	def get(self, request):
    		book_form = BookForm()
    		return render(request, 'book.html', {'form': book_form})
    		
    	def post(self, request):
    		book_form = BookForm(request.POST)
    		if form.is_valid():  # 验证表单数据
    			data = form.cleaned_data  # 获取验证后的表单数据
    			return HttpResponse('OK')
    		else:
    			return render(request, 'book.html', {'form': book_form})
    
    
  • 模板处理

    <form method='post'>
    	{% csrf_token %}
    	{{ form }}
    	<input type='submit'>
    </form>
    
    

12. Redis的使用

  • 视图中使用Redis

    from django_redis import get_redis_connection
    
    # 获取redis连接对象
    redis_conn = get_redis_connection('verify_codes')  # 参数verify_codes在settings.py中指定
    # 新增数据
    redis_conn.setex("img_%s" % image_code_id,
                     constants.IMAGE_CODE_REDIS_EXPIRES, 
                     text)
    # 查询数据
    real_image_code_text = redis_conn.get('img_%s' % image_code_id)
    
    # 删除数据
    redis_conn.delete('img_%s' % image_code_id)
    
    # 同时执行多条命令用管道
    pl = redis_conn.pipeline()
    pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    pl.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
    pl.execute()
    
    

读取数据

real_image_code_text = redis_conn.get(‘img_%s’ % image_code_id)


如何学习框架

  • 如何搭建工程程序
    • 工程的组建
    • 工程的配置
    • 路由定义
    • 视图函数定义
  • 如何获取请求数据(request对象)
  • 如何构造响应数据(response对象)
  • 如何使用中间层
  • 框架提供的其他功能组件的使用
    • 数据库
    • 模板
    • 表单
    • admin

1. DRF概述

  • DRF --> Django REST framework

  • 官方文档

  • Github源码

  • Web应用模式

    • 前后端不分离
      • 浏览器请求动态页面 --> 应用服务器查询数据库,渲染模板,返回HTML页面或重定向页面
    • 前后端分离
      • 浏览器请求静态页面 --> 静态文件服务器返回静态文件
      • 浏览器运行JS请求后端数据填充到页面中 --> 通过对外统一的API通过请求数据,返回JSON/XML数据
  • RESTful设计方法

    • 域名

      在API下部署域名

      https://api.example.com
      # 如果API简单,可放在主域名下
      https://example.com/api/
      
    • 版本

      应该将API的版本号放在URL中

      http://www.example.com/api/1.0/foo
      http://www.example.com/api/1.1/foo
      
      # 也可以将版本号放在RequestHead中 (github使用此模式)
      Accept: vnd.example-com.foo+json; version=1.0
      Accept: vnd.example-com.foo+json; version=1.1
      
    • 路径

      路径又称为终点(Endpoint),是一种资源, 往往与数据库中的表对应

      API中只能有名词不能有动词, 且为名词复数

      使用HTTP方法分离网址中的资源名称的操作

      GET /products :将返回所有产品清单
      POST /products :将产品新建到集合
      GET /products/4 :将获取产品 4
      PATCH(或)PUT /products/4 :将更新产品 4
      
    • HTTP动词

      • GET(SELECT):从服务器取出资源(一项或多项)。
      • POST(CREATE):在服务器新建一个资源。
      • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
      • DELETE(DELETE):从服务器删除资源。

      还有三个不常用的HTTP动词。

      • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
      • HEAD:获取资源的元数据。
      • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
      GET /zoos:列出所有动物园
      POST /zoos:新建一个动物园(上传文件)
      GET /zoos/ID:获取某个指定动物园的信息
      PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
      PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
      DELETE /zoos/ID:删除某个动物园
      GET /zoos/ID/animals:列出某个指定动物园的所有动物
      DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
      
    • 过滤信息

      常见的过滤参数

      ?limit=10:指定返回记录的数量
      ?offset=10:指定返回记录的开始位置。
      ?page=2&per_page=100:指定第几页,以及每页的记录数。
      ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
      ?animal_type_id=1:指定筛选条件
      
    • 状态码

      200 OK - [GET]:服务器成功返回用户请求的数据
      201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
      202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
      204 NO CONTENT - [DELETE]:用户删除数据成功。
      400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
      401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
      403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
      404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
      406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
      410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
      422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
      500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
      
    • 错误处理(Error handling)

      如果状态码是4xx,服务器就应该向用户返回出错信息。

      一般来说,返回的信息中将error作为键名,出错信息作为键值

      {
          error: "Invalid API key"
      }
      
    • 返回结果

      服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

      - GET /collection:返回资源对象的列表(数组)
      - GET /collection/resource:返回单个资源对象
      - POST /collection:返回新生成的资源对象
      - PUT /collection/resource:返回完整的资源对象
      - PATCH /collection/resource:返回完整的资源对象
      - DELETE /collection/resource:返回一个空文档
      
    • 超媒体(Hypermedia API)

      参照api.github.com

  • REST接口开发的核心任务

    序列化: 将程序中的一个数据结构类型转换为其他格式(字典、JSON、XML等)

    反序列化: 将其他格式(字典、JSON、XML等)转换为程序中的数据

    • 将数据库数据序列化为前端所需要的格式,并返回;
    • 将前端发送的数据反序列化为模型类对象,并保存到数据库中。

2. 安装配置

  • 安装DRF

    pip install djangorestframework
    
    
  • 配置

    settings.py

    INSTALLED_APPS = [
        'rest_framework',
    ]
    
    

3. DRF使用示例

  • 创建序列化器

    serializers.py

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo  # 数据字段从模型类BookInfo参考生成
            fields = '__all__'  # 包含模型类中的哪些字段,'__all__'指明包含所有字段
    
    
  • 编写视图

    views.py

    from rest_framework.viewsets import ModelViewSet
    from .serializers import BookInfoSerializer
    from .models import BookInfo
    
    class BookInfoViewSet(ModelViewSet):
        queryset = BookInfo.objects.all()  # 指明该视图集在查询数据时使用的查询集
        serializer_class = BookInfoSerializer  # 指明该视图在进行序列化或反序列化时使用的序列化器
    
    
  • 定义路由

    urls.py

    from . import views
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()  # 可以处理视图的路由器
    router.register('books', views.BookInfoViewSet, name='books')  # 向路由器中注册视图集
    urlpatterns += router.urls  # 将路由器中的所以路由信息追到到django的路由列表中
    
    
  • 测试

    127.0.0.1:8000 可以获取所有数据的接口

4. Serializer序列化器

  • 定义Serializer

    serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。

    models.py

    class BookInfo(models.Model):
        btitle = models.CharField(max_length=20, verbose_name='名称')
        bpub_date = models.DateField(verbose_name='发布日期', null=True)
        bread = models.IntegerField(default=0, verbose_name='阅读量')
        bcomment = models.IntegerField(default=0, verbose_name='评论量')
        image = models.ImageField(upload_to='booktest', verbose_name='图片', null=True)
    
    

    serializer.py

    class BookInfoSerializer(serializers.Serializer):
        """图书数据序列化器"""
        id = serializers.IntegerField(label='ID', read_only=True)
        btitle = serializers.CharField(label='名称', max_length=20)
        bpub_date = serializers.DateField(label='发布日期', required=False)
        bread = serializers.IntegerField(label='阅读量', required=False)
        bcomment = serializers.IntegerField(label='评论量', required=False)
        image = serializers.ImageField(label='图片', required=False)
    
    
  • 定义模型类序列化器

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo  # 指明参照哪个模型类
            # fields = ('id', 'btitle', 'bpub_date')
            fields = '__all__'  # 指明为模型类的哪些字段生成
            exclude = ('image',)  # 排除掉哪些字段
            depth = 1  # 关联层级  默认使用主键作为关联字段, 指定depth使用关联对象嵌套序列化
            read_only_fields = ('id', 'bread', 'bcomment')  # 指明仅用于序列化输出的字段
            extra_kwargs = {	# 添加额外参数
                'bread': {'min_value': 0, 'required': True},
                'bcomment': {'min_value': 0, 'required': True},
            }
    
    
  • 字段与选项

    常用字段类型:

    字段字段构造方式
    BooleanFieldBooleanField()
    NullBooleanFieldNullBooleanField()
    CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
    EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
    RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
    SlugFieldSlugField(max_length=50, minlength=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
    URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
    UUIDFieldUUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    IPAddressFieldIPAddressField(protocol=‘both’, unpack_ipv4=False, **options)
    IntegerFieldIntegerField(max_value=None, min_value=None)
    FloatFieldFloatField(max_value=None, min_value=None)
    DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
    DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
    DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
    TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
    DurationFieldDurationField()
    ChoiceFieldChoiceField(choices) choices与Django的用法相同
    MultipleChoiceFieldMultipleChoiceField(choices)
    FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ListFieldListField(child=, min_length=None, max_length=None)
    DictFieldDictField(child=)

    选项参数:

    参数名称作用
    max_length最大长度
    min_lenght最小长度
    allow_blank是否允许为空
    trim_whitespace是否截断空白字符
    max_value最小值
    min_value最大值

    通用参数:

    参数名称说明
    read_only表明该字段仅用于序列化输出,默认False
    write_only表明该字段仅用于反序列化输入,默认False
    required表明该字段在反序列化时必须输入,默认True
    default反序列化时使用的默认值
    allow_null表明该字段是否允许传入None,默认False
    validators该字段使用的验证器
    error_messages包含错误编号与错误信息的字典
    label用于HTML展示API页面时,显示的字段名称
    help_text用于HTML展示API页面时,显示的字段帮助提示信息
  • 使用Serializer

    Serializer(instance=None, data=empty, **kwarg)

    1)用于序列化时,将模型类对象传入instance参数

    2)用于反序列化时,将要被反序列化的数据传入data参数

    3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

    serializer = AccountSerializer(account, context={'request': request})

    通过context参数附加的数据,可以通过Serializer对象的context属性获取。

5. 序列化

  • 序列化

    book = BookInfo.objects.get(id=2)
    serializer = BookInfoSerializer(book)
    serializer.data  # 获取单个序列化数据
    
    serializer = BookInfoSerializer(book_qs, many=True)  # 获取QuerySet序列化数据
    serializer.data
    
    
  • 关联对象嵌套序列化定义

    # PrimaryKeyRelatedField  此字段将被序列化为关联对象的主键
    # 参数: read_only=True ==> 该字段该字段将不能用作反序列化使用
    hbook = serializers.PrimaryKeyRelatedField(label='图书', read_only=True)
    # 参数: queryset ==> 该字段被用作反序列化时参数校验使用
    hbook = serializers.PrimaryKeyRelatedField(label='图书',
                                               queryset=BookInfo.objects.all())
    
    # 使用关联对象的序列化器
    hbook = BookInfoSerializer()
    
    # StringRelatedField 字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值)
    hbook = serializers.StringRelatedField(label='图书')
    
    

    关联对象有多个(如: 一对多时): 在以上三种方式中加参数many=True指明

6. 反序列化

  • 验证

    data = {'bpub_date': 123}  # 反序列化数据
    serializer = BookInfoSerializer(data=data)  # 构造反序列化器
    serializer.is_valid()  # 验证
    serializer.errors  # 验证失败的错误信息
    serializer.validated_data  # 验证通过的数据
    
    
  • 保存

    class BookInfoSerializer(serializers.Serializer):
        def create(self, validated_data):
            """新建"""
            return BookInfo.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            """更新,instance为要更新的对象实例"""
            instance.btitle = validated_data.get('btitle', instance.btitle)
            instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
            instance.bread = validated_data.get('bread', instance.bread)
            instance.bcomment = validated_data.get('bcomment', instance.bcomment)
            instance.save()
            return instance
    
    
    # 如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。
    book = serializer.save()
    
    # 创建
    from db.serializers import BookInfoSerializer
    data = {'btitle': '封神演义'}
    serializer = BookInfoSerializer(data=data)
    serializer.is_valid()  # True
    serializer.save()  # <BookInfo: 封神演义>
    
    # 更新
    from db.models import BookInfo
    book = BookInfo.objects.get(id=2)
    data = {'btitle': '倚天剑'}
    serializer = BookInfoSerializer(book, data=data)
    serializer.is_valid()  # True
    serializer.save()  # <BookInfo: 倚天剑>
    book.btitle  # '倚天剑'
    
    # 部分更新  默认序列化器必须传递所有required的字段  partial允许部分字段更新
    serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
    
    

7. 视图

  • APIView

    继承自Django View 定义get(), post()等方法+序列化器+request,response实现

    支持定义的属性:
    • authentication_classes 列表或元祖,身份认证类
    • permissoin_classes 列表或元祖,权限检查类
    • throttle_classes 列表或元祖,流量控制类
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    # url(r'^books/$', views.BookListView.as_view()),
    class BookListView(APIView):
        def get(self, request):
            books = BookInfo.objects.all()
            serializer = BookInfoSerializer(books, many=True)
            return Response(serializer.data)
    
    
  • GenericAPIView

    继承自APIView, 增加数据库查询方法, 配合Mixin扩展类使用

    """GenericAPIView提供的属性和方法"""
    
    serializer_class  # 指明视图使用的序列化器
    get_serializer_class(self)  # 返回序列化器类,默认返回serializer_class, 可重写
    get_serializer(self, args, *kwargs)  # 返回序列化器对象
    
    queryset  # 指明使用的数据查询集
    get_queryset(self)  # 返回视图查询集
    get_object(self)  # 返回详情视图所需的模型类数据对象
    
    
  • Mixin扩展类

    • ListModelMixin

      """ListModelMixin提供的方法"""
      
      list(request, *args, **kwargs)  # 快速实现列表视图,返回200状态码。会对数据进行过滤和分页。
      
      

      使用示例:

      from rest_framework.mixins import ListModelMixin
      
      class BookListView(ListModelMixin, GenericAPIView):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request):
              return self.list(request)
      
      
    • CreateModelMixin

      """CreateModelMixin提供的方法"""
      
      create(request, *args, **kwargs)  # 快速实现创建资源的视图,成功返回201,序列化器对前端发送的数据验证失败,返回400错误。
      
      

      示例 :

      from rest_framework.mixins import CreateModelMixin
      
      class BookListView(CreateModelMixin, GenericAPIView):
          serializer_class = BookInfoSerializer
      
          def create(self, request):
              return self.create(request)
      
      
    • RetrieveModelMixin

      """RetrieveModelMixin提供的方法"""
      
      retrieve(request, *args, **kwargs)  # 返回一个存在的数据对象。如果存在返回200否则返回404。
      
      

      示例:

      class BookDetailView(RetrieveModelMixin, GenericAPIView):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              return self.retrieve(request)
      
      
    • UpdateModelMixin

      """UpdateModelMixin提供的方法"""
      
      update(request, *args, **kwargs)  # 更新一个存在的数据对象
      partial_update(request, *args, **kwargs)  # 实现局部更新
      # 成功返回200,序列化器校验数据失败时,返回400错误。
      
      

      示例:

      class BookDetailView(UpdateModelMixin, UpdateModelMixin):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              return self.update(request)
      
      
    • DestroyModelMixin

      """DestroyModelMixin提供的方法"""
      
      destroy(request, *args, **kwargs)  # 删除一个存在的数据对象。成功返回204,不存在返回404。
      
      

      示例:

      class BookDetailView(DestroyModelMixin, UpdateModelMixin):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              return self.destroy(request)
      
      
  • 继承自Mixin与GenericAPIView的子类视图

    子类继承的父类提供的方法
    CreateAPIViewGenericAPIView、CreateModelMixinpost
    ListAPIViewGenericAPIView、ListModelMixinget
    RetrieveAPIViewGenericAPIView、RetrieveModelMixinget
    DestoryAPIViewGenericAPIView、DestoryModelMixindelete
    UpdateAPIViewGenericAPIView、UpdateModelMixinput 和 patch
    RetrieveUpdateAPIViewGenericAPIView、RetrieveModelMixin、UpdateModelMixinget、put、patch
    RetrieveUpdateDestoryAPIViewGenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixinget、put、patch、delete
  • ViewSet

    mixin+GenericViewSet

    from rest_framework import mixins
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.decorators import action
    
    class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
        
        
    urlpatterns = [
        url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
        url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
    ]
    
    

    ModelViewSet

    继承自GenericViewSet,包括ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

    ReadOnlyModelViewSet

    继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

    自定义action

    from rest_framework import mixins
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.decorators import action
    
    class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def latest(self, request):
            """
            返回最新的图书信息
            """
            book = BookInfo.objects.latest('id')
            serializer = self.get_serializer(book)
            return Response(serializer.data)
    
        def read(self, request, pk):
            """
            修改图书的阅读量数据
            """
            book = self.get_object()
            book.bread = request.data.get('read')
            book.save()
            serializer = self.get_serializer(book)
            return Response(serializer.data)
        
        
    urlpatterns = [
        url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
        url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})),
        url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
        url(r'^books/(?P<pk>\d+)/read/$', views.BookInfoViewSet.as_view({'put': 'read'})),
    ]
    
    

    根据action属性,判断action

    def get_serializer_class(self):
        if self.action == 'create':
            return OrderCommitSerializer
        else:
            return OrderDataSerializer
    
    

8. 路由

  • SimpleRouter

    对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。

    urls.py

    from rest_framework import routers
    
    router = routers.SimpleRouter()
    router.register(r'books', BookInfoViewSet, base_name='book')
    
    urlpatterns += router.urls
    # 或
    url(r'^', include(router.urls))
    
    '''形成路由如下
    ^books/$    name: book-list
    ^books/{pk}/$   name: book-detail
    '''
    
    

8. 获取请求参数

DRF对request对象进行了再次封装

  • 路径参数

    # r'image_codes/(?P<image_codes_id>.*)/$'
    
    class ImageCodes(APIView):
        def get(self, request, image_codes_id):
            pass
    
    
  • request.data

    同django中的request.POST

  • request.query_params

    同django中的request.GET

9. 构造响应

  • Response

    • 修改响应数据格式

      settings.py

      REST_FRAMEWORK = {
          'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
              'rest_framework.renderers.JSONRenderer',  # json渲染器
              'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
          )
      }
      
      
    • 构造响应

      rest_framework.response.Response
      Response(data, status=None, template_name=None, headers=None, content_type=None)
      
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值