# 1、Django基础一

### Django 的安装

```python
pip3 install pipenv
cd /opt && mkdir appcation && cd appcation
pipenv --python 3.7
pipenv shell
pipenv install django==2.0.5
django-admin startproject devops
# 上解释： 创建虚拟环境、目录、 在虚拟环境中安装django，创建项目 devops

(appcation) [root@localhost appcation]# tree
.
├── devops
│   ├── db.sqlite3
│   ├── devops
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   └── manage.py
├── Pipfile
└── Pipfile.lock
```

#### 项目目录详解

* 最外层的devops/目录只是你项目的一个容器
* manage.py 一个实用的命令行工具，可让你以各种方式与该 Django 项目进行交互
* 内层 devops/ 目录是你项目中的实际Python包。通过它你可以导入它里面的任何东西
* devops/**init**.py: 一个空文件，告诉Python该目录是一个Python包
* devops/settings.py: 该Django项目的配置文件
* devops/urls.py: 该Django项目的 URL 声明
* devops/wsgi.py: 一个WSGI兼容的Web服务器的入口

#### 新建 App

```python
# 新建一个名为dashboard的app
python manage.py startapp dashboard
```

```python
# 配置url
from django.urls import path, include
urlpatterns = [
    path('dashboard/', include("dashboard.urls"))
]
```

```python
# 激活app
INSTALLED_APPS = (
	....
    'dashboard',
)
```

#### App中编写视图

```python
# 子路由： dashboard/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
]

# 子视图： dashboard/views.py
from django.http import HttpResponse
def index(request):
    return HttpResponse("Hello world !!!")
```

#### 启动服务

```python
python manage.py runserver                # 启动服务
python manage.py runserver 8000           # 指定端口启动服务
python manage.py runserver 0.0.0.0:8000   # 指定IP和端口启动服务
```

#### 启动 Django 报错解决：

> ModuleNotFoundError: No module named '\_sqlite3'

```python
yum install sqlite-devel
# 在重新编译 python
```

#### 配置静态文件

```python
STATIC_URL = '/static/' 
STATICFILES_DIRS = ( 
	os.path.join(BASE_DIR, "static"), 
)
```

#### 配置时区

```python
TIME_ZONE = ’Asia/Shanghai’
```

***

### HttpRequest对象

由django创建

**属性**

```python
HttpRequest.scheme   ===>　request.scheme    <=== 请求协议
HttpRequest.body     ===>　request.body      <=== 请求主体
HttpRequest.path     ===>　request.path      <=== 请求路径
HttpRequest.method   ===>　request.method    <=== 请求方式
HttpRequest.encoding ===>　request.encoding  <=== 请求编码
HttpRequest.GET      ===>　request.GET       <=== 请求实例对象参数
HttpRequest.POST     ===>　request.POST      <=== 请求实例对象参数
HttpRequest.META     ===>　request.META      <=== 请求元素
```

**方法**

```python
HttpRequest.get_host()      ===>　request.get_host()       <=== 请求的主机
HttpRequest.get_port()      ===>　request.get_port()       <=== 请求的端口
HttpRequest.get_full_path() ===>　request.get_full_path()  <=== 请求的路径
HttpRequest.is_secure()     ===>　request.is_secure()      <=== 请求是否是安全的
HttpRequest.is_ajax()       ===>　request.is_ajax()        <=== 请求是否 ajax 方式
```

### HttpResponse对象

> 传递一个字符串作为页面的内容到 HttpResponse 构造函数

```python
>>> from django.http import HttpResponse
>>> response = HttpResponse("Here's the text of the Web page.")
>>> response = HttpResponse("Text only, please.", content_type="text/plain")
```

**属性：**

```
HttpResponse.content          ===>　响应内容
HttpResponse.charset          ===>　响应编码
HttpResponse.status_code      ===>　响应状态
HttpResponse.reason_phrase    ===>　响应消息的原因说明
```

**方法**

```python
HttpResponse.__init__(content=”, content_type=None, status=200, reason=None, charset=None)
```

### JsonResponse 对象

**方法**

```python
JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs)
```

**示例**

```python
>>> from django.http import JsonResponse 
>>> response = JsonResponse({'foo': 'bar'}) 
>>> response.content 
b'{"foo": "bar"}' 
>>> response = JsonResponse([1, 2, 3], safe=False)        # 加上 safe=False , 允许非字典
```

**视图错误**

```python
报错： In order to allow non-dict objects to be serialized set the safe parameter to False.
解决： return JsonResponse(data=data, safe=False)
```

***

*

### QueryDIct对象

*   在HttpRequest 对象中，GET 和POST 属性是django.http.QueryDict 的实例，它是一个自定

    义的类似字典的类，用来处理同一个键带有多个值。这个类的需求来自某些HTML 表单元

    素传递多个值给同一个键
*   request.POST 和request.GET 的QueryDict 在一个正常的请求/响应循环中是不可变的。若

    要获得可变的版本，需要使用.copy()

#### 实例化QueryDict

```python
QueryDict.__init__(query_string=None, mutable=False, encoding=None)
```

#### 示例

```python
>>> QueryDict('a=1&a=2&c=3') 
<QueryDict: {'a': ['1', '2'], 'c': ['3']}>
```

#### 通过fromkeys实例化QueryDict (1.11新增)

```python
classmethod QueryDict.fromkeys(iterable, value=”, mutable=False, encoding=None)
```

**示例**

```python
>>> QueryDict.fromkeys(['a', 'a', 'b'], value='val') 
<QueryDict: {'a': ['val', 'val'], 'b': ['val']}>
```

#### QueryDict方法：

```python
QueryDict.get(key,default=None)
QueryDict.setdefault(key,default=None)[source]
QueryDict.update(other_dict)
QueryDict.items()
QueryDict.values()
QueryDict.copy()
QueryDict.getlist(key,default=None)
QueryDict.setlist(key,list_)[source]
QueryDict.appendlist(key,item)
QueryDict.setlistdefault(key,default_list=None)
QueryDict.lists()
QueryDict.pop(key)
QueryDict.popitem()
QueryDict.dict()
QueryDict.urlencode(safe=None)
```

### GET与POST请求

*   GET请求与传参 那如何获取url后面的这个参数呢?---它是被存放在request.GET中，是一种QueryDict数据类型

    那怎样取到cc变量中某一个值?

    ```python
    print(request.GET.get("aa"))  -->(打印aa变量)结果为bb
    prin t(request.GET.getlist("cc")) --->结果为列表['dd', 'ee']         # 解决传入同样的key，只获取一个值
    print(request.GET) -->(完整的request请求)结果为<QueryDict: {'aa': ['bb'], 'cc': ['dd', 'ee']}>
    ```

### 加载摸版

*   django.template.loader 这个模块提供了两种方法加载模板

    ```python
    get_template(template_name, using=None)
    ```
*   加载指定模板并返回Template对象; 它与get\_template类似，它尝试每个名称并返回第一个存在的模板

    ```python
    select_template(template_name_list, using=None)
    ```
*   从文件加载内容

    ```python
    from django.template import Context, loader
    def index(request):
        t = loader.get_template("test.html")
        context = {"name": "hello reboot !!!"} 
        return HttpResponse(t.render(context, request))
    ```
*   快捷方式：render()

    ```python
    from django.shortcuts import render 
    def index(request):
        context = {'name': "reboot"} 
        return render(request, 'test.html', context)
    ```

### 实战用户登陆

#### 配置django 数据库

```python
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',
        'USER': 'root',
        'PASSWORD': 'aabb122..',
        'HOST': '192.168.1.33',
        'PORT': 3306,
    }
}
```

#### 同步数据

```python
python manage.py migrate
```

#### Django API 创建用户

> \#使用 django shell 创建普通用户：创建users最直接的方法是使用create\_user()辅助函数

**普通用户**

```python

>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user("rock", "rock@51reboot.com", "123456")
```

**创建管理员**

```python
# 创建管理员
python manage.py createsuperuser --username=reboot --email=reboot@51reboot.com
```

**修改密码**

> Django不会在user模型上存储原始的（明文）密码，而只是一个哈希。因为这个原因，不要尝
>
> 试直接操作user的password属性。这也是为什么创建一个user时要使用辅助函数

```python
>>> from django.contrib.auth.models import User 
>>> u = User.objects.get(username='rock') 
>>> u.set_password('654321') 
>>> u.save()
```

**执行用户登录**

```python
用户登录的时候，数据库中会保留 session 数据，可以看到
```

***

### Django如何处理一个请求

1. django 加载 ROOT\_URLCONF指定的模块，并寻找可用的urlpattems，它是 django.conf.urls.url()实例的的一个python列表。
2. django 依次匹配每个URL，在于请求的URL匹配到第一个url停下来
3. 一旦其中的正则表打死匹配上，django将导入并调用给出的视图，它是一个简单的Python函数（或者一个基于类的视图）。 视图将获得如下参数：
   * 参数一： 一个 HttpRequest 实例
   * 参数二：如果匹配的正则表达式返回了没有命名的组，那么正则表达式匹配的内容将作为位置参数提供给视图。
   * 参数三：关键字参数由正则表达式匹配的命名组组成，但是可以被 django.conf.urls.url() 的可选参数 kwargs 覆盖
4. 如果没有匹配到正则表达式，或者如果过程中抛出一个异常，Django 将调用一个适当的错误处理视图： handler404, handler500, hander403, handler400

### URL 路由

> 对于高质量的Web 应用来说，使用简洁、优雅的URL路由是一个非常值得重视的细节。 Django 让你随心所欲设计你的URL，不受框架束缚。 为了给一个应用设计URL，你需要创建一个Python 模块，通常称为URLconf（URL conÒguration）。这个模块是纯粹的Python 代码，包含URL 模式（简单的正则表达式）到 Python 函数（你的视图）的简单映射

```python
# setting.py
ROOT_URLCONF = '项目.urls'
```

#### URLconf

> URLconf 是一个python模块，是一个URL模式（简单正则表达式）到Python函数（视图）之间的映射

#### URLpatterns

* urlpatterns 是一个 url() 实例类型的Python列表
* urlpatterns 中的每个正则表达式在第一次访问它们时被编译

#### URl 函数

```python
url(regex, view, kwargs=None, name=None)
```

* regex: 一个字符串(原始字符串)或简单的正则表达式
* view: view参数是一个视图函数或as\_view()的结果（基于类的视图）
* kwargs: 传递额外的参数给视图
* name: url名称

#### include

```python
include(module, namespace=None, app_name=None) 
include(pattern_list) 
include((pattern_list, app_namespace), namespace=None) 
include((pattern_list, app_namespace, instance_namespace))
```

* module: URLconf模块
* namespace：URL命名空间
* app\_name: app的命名空间
* pattern\_list: 可迭代的django.conf.urls.url()实例
* app\_namespace: 应用的命名空间
* instance\_namespace: 实例的命名空间

**include 示例**

**示例1：**

```python
urlpatterns = [
    url(r'^$', RedirectView.as_view(url='/dashboard/')),
    url(r'^dashboard/', include('dashbard.urls')),
    url(r'^account/', include('account.urls')),
    url(r'^admin/', admin.site.urls),
]
```

**示例2：**

```python
urlpatterns = [
    url(r'^user/', include([
        url(r'^list/', view.userlist, name='user_list'),
        url(r'^info/', view.userinfo, name='userer_inf'),
        url(r'^modify/', include([
            url(r'status/$', view.modifystatus, name='user_modify_status')
        ]))
    ]))
]
```

***

#### URL参数—— 位置参数

> 若要从URL 中捕获一个值，只需要在它周围放置一对圆括号。

```python
from django.conf.urls import url 
from . import views 
urlpatterns = [ 
	url(r'^articles/2003/$', views.special_case_2003), 
	url(r'^articles/([0-9]{4})/$', views.year_archive), 
	url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), 
	url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), 
]

请求地址： /articles/2003/03/03/ 
调用函数：views.article_detail(request, "2003", '03', '03')
```

***

#### URL参数——关键字参数

```python
# 语法
(?p<name>pattern)
```

* name 是传给视图参数的名字，
* pattern 是一个正则表达式，也是关键字参数的值

```python
# 示例
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$',views.article_detail),
]

请求地址：/articles/2003/03/03/ 
调用函数：views.article_detail(request, year='2003', month='03', day='03')
```

***

#### URL参数——额外参数

* URLconfs 具有一个钩子，让你传递一个Python 字典作为额外的参数传递给视图函数
* django.conf.urls.url() 函数可以接收一个可选的第三个参数，它是一个字典，表示想要传递给视图函数的额外关键字参数

```python
# 示例
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

请求地址：/blog/2005/
调用函数：views.year_archive(request, year='2005',foo='bar')
```

***

#### URL反向解析

* django给每个URL取了一个名字，存储每个URL与name的映射关系
*   根据Django 视图的标识和将要传递给它的参数的值，获取与之关联的URL。这种方式叫做

    反向解析URL、反向URL匹配、反向URL查询或者简单的URL反查

```python
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'articles/(0-9){4}/$', view.year_archive, name='news-year-archive'),
]
```

视图中使用

```python
from django.core.urlresolvers import reverse
year = 2006
return HttpResponseRedirect(reverse('news-year-archive', arg=(year, )))
```

摸版中使用

```python
<a href="<div data-gb-custom-block data-tag="url" data-0='news-year-archive'></div>">{{ yearvar }} Archive</a>
```

***

#### 基于类的视图

* 视图是一个可调用的对象，它接受一个请求然后返回一个响应，这个可调用对象可以不只是函数，Django提供一些可以用作视图的类
* 基于类的视图使用Python对象实现视图，它提供除函数视图之外的另一种方式

View

> 属性 :\
> http\_method\_names 方法： as\_view() dispatch() http\_method\_not\_allowed()

类视图登陆验证

```python
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorato
class FooView(View):
    @method_decorator(login_required)
    def get(request, *args, **kwargs):
        return HttpResponse("hello world")
```

***

#### 数据分页

> Paginator对象 And Page对象

```
class Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True)
```

```python
# 属性
- Paginator.count 所有页面的objects总数
- Paginator.num_pages 页面总数
- Paginator.page_range 页码的范围，从1开始，例如[1, 2, 3, 4]

# 方法
- Paginator.page(number) 返回一个page对象,number， 当前显示的是第几页
```

> Page 对象

```python
class Page(object_list, number, paginator)
```

```python
# 属性
- Page.object_list 当前页面的对象列表 
- Page.number 当前页的序号，从1开始 
- Page.paginator Paginator对象

# 方法
- Page.has_next() 如果有下一页，返回True 
- Page.has_previous() 如果有上一页，返回 True 
- Page.has_other_pages() 如果有上一面或下一页，返回True 
- Page.next_page_number() 返回下一页的页码。如果不存在，抛出InvalidPage异常 
- Page.previous_page_number() 返回上一页的页码。如果不存在，抛出InvalidPage异常 
- Page.start_index() 返回当前页上的第一个对象，相对于分页列表的所有对象的序号 
- Page.end_index() 返回当前页上的最后一个对象，相对于分页列表的所有对象的序号
```

***

### 模型

#### 什么是模型

> 模型是你数据唯一的、权威的信息源。它包含你所存储数据的必要字段和行为，每个模型对应数据库中的唯一的一张表

#### 如何编写模型

* 模型： 每个模型都用一个类表示，该类继承自 django.db.models.Model, 每个模型有多个类的属性变量，而每个类的属性变量又都代表了数据库中的一个字段
* 字段： 每个字段通过Field类的一个实例表示 --- 例如字符字段CharField和日期字段 DateTimeField. 这种方法告诉 Django，每个字段中报错着什么类型的数据
* 字段名： 每个Field实例中（例如username）就是自动的名字，并且是机器可短的格式，你将在Pythoin代码中使用到它的值，并且你的数据库将它用作列表的列表

#### 常见模型字段

* CharField
* BooleanField
* IntegerField
* DateField / DateTimeField
* EmailFied
* TextField
* TimeField

#### 自增主键字段

*   默认情况下Django会给每个模型添加羡慕这个字段

    ```python
    id = models.AutoField(primary_key=True)
    ```
* 如果Django看到你显式地设置了Field.primary\_key， 就不会自动添加id列
* 每个模型只能有一个字段指定 primary\_key=True (无论是显式生命还是自动添加)

#### 字段的自述名

* 每个字段类型都解散一个可选的位置参数----自动的自数名，如果没有给定自述名，Django将根据字段的署名名称自动创建自述名---将属性名的下划线替换成空格
*   ForeignKey、ManyToManyField 和 OneToOneField 这三个可以使用 verbose\_name指定自述名

    ```python
    # 例如： 自述名为： "person's first name"
    first_name = models.CharField("person's first name", max_length=30)

    # 例如： 自述名为： "first name"
    first_name = models.Charfield(max_length=30)
    ```

#### 字段选项

* null ===> 如果为True, Django 将用NULL来再数据库中存储空值，默认 False
* blank ===> 如果为True,该字段允许不填，默认False, null 是纯数据范畴，blank 是数据验证范畴
*   choices ===> 由二元组组成的一个可迭代对象，如果设置了choices，默认表单将是选择框，值是 choices中选项

    ```python
    YEAR_IN_SCHOOL_CHOICES = (
    	('FR', 'Freshman'),
        ('SO', 'Sophomore'),
        ('JR', 'Junior'),
        ('SR', 'Senior'),
    )
    ```
* default ===> 字段的默认值，可以是一个值或者调用的对象
* primary\_key ===> 如果为True，那么这个字段就是模型的主键
* unique ===> 如果该值为True，这个字段的值在整张表中必须是唯一的

#### 模型 **meta** 选项

> 模型元数据是 **任何不是字段的数据**， 比如排序选项（**ordering**）, 数据库表名（**db\_table**）.在模型中添加 class Meta是可选的

```python
# 使用内部的class Meta 定义模型的元数据，例如
from django.db import models
class User(models.Model):
    username = models.IntegerField()
    class Meta:
        ordering = ['username']
```

**meta选项： db\_table**

* db\_table 是用于指定自定义数据库表名的。 Django有一套默认的按照一定规则生成数据模型对应的数据库表妹，如果你想使用自定义表妹，就通过这个属性指定
*   惹不提供该参数， Django会使用 app\_label + "\_" + module\_name 作为表的名字

    ```python
    class Meta:
        db_table = 'server'
    ```
* Django 会根据模型类的名称和包含它的应用的名称自动指定数据表名称，一个模型的数据库表名称，由这个模型的应用名和模型类名称之间加上下划线组成
* 使用Meta类中的 db\_table 参数来重写数据表的名称
* 当你通过 db\_table 覆写表名称时，强烈推荐使用小写字母给表命名

**meta选项：order**

*   这个字段是告诉Django模型对象返回的记录结果集是安装哪个字段排序的

    ```python
    # 例如三列：　order_date、 pub_date 、author
    class Meta:
        ordering = ['order_date']             # 按订单升序排列
        ordering = ['-order_date']            # 按订单降序排列， - 表示降序
        ordering = ['?order_date']            # 随机排序， ？ 表示随机
        ordering = ['-pub_date', 'author']    # 对 pub_date 降序， 然后对 author 升序
    ```

**meta选项：app\_label**

*   app\_label 这个选项只在一种情况下使用，就是你的模型类不在默认的应用程序包下的 **model.py**文件中，这时候你需要制定你这个模型类是哪个应用程序的

    ```python
    class Meta:
        appp_label = 'myapp'
    ```

**meta选项：get\_lates\_by**

* 由于Django的管理方法中有个\*\*lastest()\*\*方法，就是得到最近一行记录，如果你的数据模型中有DateField 或 DateTimeField类型的字段，你可以通过这个选项来制定 lastest() 是按照哪个字段进行选取的
*   一个 DateField 活 DateTimeField 字段的名字，惹提供该选项，该模块将拥有一个 get\_latest() 函数以得到最新的对象（依据那个字段）

    ```python
    class Meta:
        get_lastest_by = 'order_date'
    ```

**meta选项：verbose\_name**

*   verbose\_name 的意思很简单，就是给你的模型类起一个更可读的名字

    ```python
    class Meta:
        verbose_name = 'pizza'
    ```

**meta选项：managed**

* 由于Django会自动根据模型类生成映射的数据库表，如果你不希望Django这么做，那么把managed的值设置为 False
* 默认值为T入耳，这个选项为True时，Django可以对数据库进行 migrate活migrations、删除等操作，在这个时间Django将管理数据库表的生命周期
* 如果为False的时候，不会对数据库进行创建、删除等操作。可以用于先有表、数据库视图等，其他操作是一样的

#### 数据库迁移

* 迁移是Django用户同步你的发生改变的模型（添加一个字段，删除一个字段，等待）到你的数据库

**迁移命令**

* makemigrations ===> 负责基于你的模型修改创建一个新的迁移
* migrate ===> 负责执行迁移，以及撤销和列出迁移的状态
* sqlmigrate ===> 展示迁移的sql语句

**生成迁移文件**

> 例如APP名字： dashboard

```python
python manage.py makemigrations dashboard
# 会扫描和比较你当前迁移文件里面的版本，同事新的迁移文件会被创建
```

**展示迁移的 sql 语句**

```python
python manage.py sqlmigrate dashboard 0007
```

**数据迁移**

```python
python manage.py migrate dashboard
```

#### 创建对象

* 使用关键字参数实例化模型实例来创建一个对象，然后调用save()把它保存到数据库中
* 也可以使用一条语句穿件并保存一个对象，使用create()方法

**查询对象**

```python
all_users = User.objects.all()
u = User.objects.filter(username='rock')
u = User.objects.all().filter(username='rock')

Entry.object.filter(                      ===> 查询以 'what' 开头，发布日期在2005年1月30日至当天之前直接的所有记录
	headline__startswith='what'
).exclude(
	pub_date__gte=datetime.date.today()
).filter(
	pub_date__gte=datetime(2005, 1, 30)
)
```

```python
one_entry = Entry.objects.get(pk=1)      ===> 通过主键查询
# 如果没有查询到，将返回 DoesNotExist 异常，
# 如果有多条记录，将返回 MultipleObjectsReturned 异常
```

```python
# 限制查询集
Entry.object.all()[:5]
Entry.object.all()[5:10]
```

```python
# 字段查询
Entry.objects.filter(pub_date__lte='2006-01-01')    ===  select * from blog_entry where pub_date <= '2006-01-01'
```

* exact 精确匹配
* iexact 大小写不敏感的匹配
* contains 大小写敏感的包含指定字符串
* icontains 大小写不敏感的包含指定字符串
* startswith，endswith 以指定字符串开头或结尾
* istartswith，iendswith 忽略大小写以指定字符串开头或结尾
* in 在指定的列表内
* gt 大于
* gte 小于或等于
* lt 小于
* lte 小于或等于
* range 在指定访问呢
* year / month /day /week\_day 对应日期和日期时间字段 ，匹配年/月/日/星期

```python
>>> Entry.objects.get(headline__exact="Man bites dog")
select ... where headline = 'Man bites dog'
>>> Blog.objects.get(id__exact=14)
>>> Blog.objects.get(id=14)
>>> Blog.objects.get(pk=14)       # django内置， id必须是主键
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline') # 降序，再升序
Entry.objects.order_by('?')          # 随机排序
```

**values**

* 返回一个 ValuesQuerySet-------QuerySet的一个自雷，迭代时返回字典而不是模型实例对象
* 每个字典表示一个对象，键对应于模型对象的属性名称
*   values() 接受可选的位置反水 _fields_， 它指定 SELECT 应该限制哪些字段。如果指定字段，每个字典将只包含指定的字段的键、值。如果没有指定字段，每个字段将包含数据库表中所有字段的键和值

    ```python
    User.objects.values('id','username')
    ```

**values\_list**

*   与values()类似，只是在迭代时返回的元组而不是字典。每个元组包含传递给values\_list()调用的字段值-----所有第一个元素为第一个字段，以此类推

    ```python
    User.objects.values_list('id','username')
    ```

**defer**

*   在一些复制的数据建模情况下，你的模型可能包含大量字段，其中一些可能包含大量数据（例如：文本），或者需要昂贵的处理来将它们转换为Python对象。如果你在某些情况下使用查询集的结果，当你最初获取数据时不知道是否需要某些特定字段，可以告诉Django不要从数据库中检索它们。

    ```python
    User.objects.defer('username', 'email')
    ```

**删除对象**

*   删除对象使用delete()。这个方法将立即删除对象且没有返回值

    ```python
    Entry.objects.filter(pub_date__year=2005).delete()
    ```

**拷贝模型实例**

> 虽然没有建的方法用于拷贝模型实例，但还是很容易创建一个新的实例并让它的所有字段都拷贝过来。最简单的方法是，只需要将 **pk** 设置为 **None**

```python
blog = Blog(name="My blog", tagline='Blogging is easy')
blog.save()        # blog.pk == 1
blog.pk = None
blog.save()        # blog.pk == 2
```

**更新对象**

*   更新对象使用 update()

    ```python
    Entry.objects.filter(pub_date__year=2007).update(headline="Everything is the same")
    ```

**F()**

*   F() 允许Django在未实际链接数据的情况下具有对数据库字段的值的引用，通常在更新数据时需要先从数据库里将原数据取出后放在内存里，然后编辑某些属性，最后提交。例如

    ```python
    # 普通方法
    order = Order.objects.get(orderid='123456')
    order.amount += 1
    order.save()
    # F() 方法
    from django.db.models import F
    order = Order.objects.get(orderid='123456')
    order.amount = F('amount') + 1
    order.save()
    ```

**Q()**

*   Q 对象（django.db.models.Q)可以对关键字参数进行封装，从而更好第应用多个查询，可以组合 **and, or ,not** 操作符，当一个操作符是用于两个Q的对象，它产生一个新的Q对象

    ```python
    Order.objects.get(
        Q(name__startswith='who'),
    	Q(create_time=date(2016,10,2)) | Q(create_time=date(2016,10,5))
    )
    # 相当于
    select * from polls where name LIKE "who%" and (create_time='2005-05-02' or create_time='2016-010-05')
    ```

**序列化模型对象**

```python
from django.core import serializers
data = serializers.serialize('json', SomeModel.objects.all())
```

*   序列化子集

    ```python
    from django.core import serializers
    data = serializers.serialize('json', User.objects.all()[0:10], fields=('username','is_active'))
    ```

***

#### 模型关系

**多对一关系**

* django 是使用 django.db.models.ForeignKey 定义多多疑关系
*   ForeignKey 需要一个位置参数来制定本Model关联的Model，ForeignKey关联的Model是‘一’, ForeignKey所在的 Model是‘多‘

    > 比如汽车和制造商的例子， 一辆汽车智能属于一个制造商，但是一个制造商有多辆汽车

    ```python
    class Manufacturer(models.Model):                  # 制造商
        name = models.CharField(max_length=30)
        
    class Car(models.Model):                           # 汽车
        manufacturer = models.ForeignKey(Manufacturer)
        name = models.CharField(max_length=30)
        
    # 正向查询 （ForeignKey所在的模型查询关联的模型）
    car = Car.objects.get(pk=2)
    car.manufacturer                                   # 返回一条 Manufacturer 对象

    # 反向查询 （ForeignKey指向的模型查询ForeignKey所在的模型）
    manufacturer = Manufacturer.objects.get(pk=1)
    manufacturer.car_set.all()                         # 返回多个  car  对象
    ```
* add(obj, obj2,....) # 添加----指定的模型对象到管理的对象集中
* create(\*\*kwargs) # 创建一个新的对象，将它保存并放在关联的对象集中，返回新创建的对象
* remove(obj1,obj2,...) # 从关联的对象集中删除指定的模型对象
* clear() # 从关联的对象集中删除所有的对象

**多对多关系**

* 要实现多对多，就要使用 django.db.models.ManyToManyField类， 和 ForeignKey一样，它也有一个位置参数，用来指定和它管理的Model
*   如果不仅仅要知道两个 **Model**之间是多对多的关系，还需要知道这个关系的更多信息，比如 Person 和 Group是多对多的关系，每个 person 可以在多个Group里，那么 group里可以有多个 person

    ```python
    class Group(models.Model):
        #....
    class Person(models.Model):
        groups = models.ManyToManyField(Group)
    ```

    * 建议以被管理模型名称的复数形式作为 ManyToManyField　的名字
    * 在哪个模型中设置 ManyToManyField 并不重要，在**两个模型中任选一个即可**----不要在两个模型中都设置

**一对一关系**

* 一多疑是通过 django.db.models.OneToOneField 来实现的，被管理的Model会被加上 Unique的限制， OneToOneField 要一个位置参数，与模型关联的类
* 当某个对象想扩展自另一个对象时，最常用的方式就是在这个对象的主键上添加一对一关系

### Django 日志

> Django 使用Python内建的loggin模块打印日志，Python 的logging配置由四个部分组成：
>
> * 记录器 ---- Logger
> * 处理程序 ---- Handler
> * 过滤器 ---- Filter
> * 格式化 ---- Formatter

#### 记录器

* Logger为日志系统的入口。 每个logger命名都是bucket， 你可以向这个bucket写入需要处理的消息
* 每个logger都有一个日志级别。日志接不表示该logger将要处理的消息的严重性。Python日志级别：
* ```python
  DEBUG: 用户调试目的底层系统消息
  INFO:  普通的系统消息
  WARNING: 表示出现一个较小的问题
  ERROR: 表示出现一个较大的问题
  CRITICAL: 表示出现一个致命的问题
  ```
* 写入logger 的每条消息都是一条日志。每条日志也具有一个日志级别，它表示对应的消息的严重性。每个日志记录还可以包含表述正在打印的事件的元信息
* 当一条消息传递给logger时，消息的日志级别将与logger的日志级别进行比较。日过消息的日志级别大于等于logger的日志级别，该消息将往下继续处理。如果小于，该消息将被忽略
* logger一旦决定消息需要处理，它将传递该消息给一个Handler
  *   logger日志级别

      | 级别       | 值  | 描述      |
      | -------- | -- | ------- |
      | CRITICAL | 50 | 关键错误/消息 |
      | ERROR    | 40 | 错误      |
      | WARNING  | 30 | 警告消息    |
      | INFO     | 20 | 通知消息    |
      | DEBUG    | 10 | 调试      |
      | NOTSET   | 0  | 无级别     |

**Logger配置**

> logger对应的值是个自动，其每一个键都是logger的名字，每一个值又是个字典，描述了如何配置毒药的Logger实例
>
> \- level （可选的） logger 的级别 - propagate (可选的) logger的传播设置 - filters （可选的） logger 的filter 的标识付列表 - handlers (可选的) logger的handlers的标识符的列表

```python
# 配置示例
LOGGING = {
    'loggers':{
        reboot: {
            'handlers': ['file_handler', 'console_handler'],
            'level': 'DEBUG'
        }
    }
}
```

#### 处理程序 ---- Handler

* Handler 决定如何处理logger中的每条消息。 它表示一个特定的日志行为，例如将消息写入到屏幕上，写到文件中或者写到网络 socket
* 与logger一样，handler也有一个日志级别。如果消息的日志级别小于handler的级别，handler将忽略改消息
*   Logger 可以有多个handler，而每个handler可以有不同的日志级别。利用这种方式，可以根据消息的重要性提供不同形式的处理

    ```python
    LOGGING = {
        'handlers': {
            'reboot': {
                "level": 'DEBUG',
                'class': 'logging.StreamHandler',
                'formatter': 'simple',
            }
        }
    }
    ```

#### 过滤器---- Filters

* Filter 用于对从logger传递给handler的日志记录进行额外的控制。
* 默认情况下，满足日志级别的任何消息都将被处理。通过安装 一个filter，你可以对日志处理添加额外的条件。例如，你可以安装一个filter，只运行处理来自特定源的ERROR消息
* Filters还可以用户修改将要处理的日志记录的优先级。例如，如果日志记录满足特定的条件，你可以编写一个filter将日志记录从ERROR降为WARNING
* Filters 可以安装在logger上或者Handler上； 多个filter可以串联起来实现多层filter行为

#### 格式化----Formatters

*   最后，日志记录需要转换成文本。Formatter表示文本的格式。Fomatter通常由包行日志记录熟悉的python格式字符串组成；你也可以编写自定义的formatter来实现自己的格式

    ```python
    LOGGINT = {
        'formatters': {
            'reboot': {
                'format': '%(asctime)s - %(pathname)s:%(lineno)d[%(levelname)s] - %(message)s'
            }
            'simple': {
                'format': '%(asctime)s %(levelname)s %(message)s'
            }
        }
    }
    ```

#### Format 日志小小格式

| 格式             | 描述                |
| -------------- | ----------------- |
| %(name)s       | 记录器的名称            |
| %(levelno)s    | 数字形式的日志记录级别       |
| %(levelname)s  | 日志记录级别的文本名称       |
| %(filename)s   | 执行日志记录调用的源文件的文件名称 |
| %(pathname)s   | 执行日志记录调用的源文件的路径名称 |
| %(fullcName)s  | 执行日志记录调用的函数名称     |
| %(module)s     | 执行日志记录调用的模块名称     |
| %(lineno)s     | 执行日志记录调用的行号       |
| %(created)s    | 执行日志记录时间          |
| %(asctime)s    | 日期和时间             |
| %(msecs)s      | 毫秒部分              |
| %(thread)s     | 线程ID              |
| %(threadName)s | 线程名称              |
| %(process)s    | 进程ID              |
| %(message)s    | 记录的消息             |

#### Django内置logger

* django获取所有日志
* django.request 处理与请求相关的日志，5xx响应爆出errror日志，4xx报出WARNING日志
* django.db.backends 处理与数据库之间交互的日志
* django.security.\* 处理与安全相关的日志
* django.db。backends。schemea处理数据库迁移时日志

[更多参考](https://docs.djangoproject.com/en/1.11/topics/logging/#id3)

***

### 序列化和反序列化

1. 在开发REST接口时，视图中做的最主要的三件事
   * 将请求的数据（如：Json格式）转换为模型类对象（反序列化）
   * 操作数据库
   * 将模型类对象转换为响应的数据（如： Json格式）（序列化）
2. 序列化
   *   概念

       > 将程序中的一个数据结构类型转化为其他格式（字典，xml，json等）。例如：将django中的模型类对象转换为json字符串，这个转换过程称之为序列化
   *   序列化时机

       > 当需要给前端响应模型数据时，需要将模型数据序列化成前端需要的格式
3.  反序列化

    *   概念

        > 将其他格式（字典、Json、XML等）转换成程序中的数据，例如：将Json字符串转换成django模型类对象。这个过程称为反序列化
    *   反序列化时机

        > 当需要将用户发送的数据存储到数据库之前，需要反序列化

    总结：

    > 在开发REST接口时，在视图中需要做的**最核心的事**是：
    >
    > **将数据库数据序列化为前端所需要的格式，并返回**
    >
    > **将前端发送的数据反序列化为模型类对象，并保存到数据库中**
