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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://close.gitbook.io/yun-wei-bi-ji/python/django/1django-ji-chu-yi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
