Django官方文件學習筆記
Django官方文件筆記
文章開始把我喜歡的這句話送個大家:這個世界上還有什麼比自己寫的程式碼執行在一億人的電腦上更酷的事情嗎,如果有那就是讓這個數字再擴大十倍
1.函式 include() 允許引用其它URLconfs
我們設計 include() 的理念是使其可以即插即用。因為投票應用有它自己的URLconf( polls/urls.py ),他們能夠被放在"/polls/" ,"/fun_polls/" ,"/content/polls/",或者其他任何路徑下,這個應用都能夠正常工作。
何時使用 include()
當包括其它URL
我們想在myapp中定義一個主頁,然後通過"http://localhost:8000/myapp/homepage"來訪問,首先我們在myproject/myapp//view.py中定義一個叫homePage的函式(名字隨意,不一定叫這名字):
然後在myproject/myproject/urls.py的urlpatterns列表中新增一個url配置:
然後執行項目,就可以用瀏覽器通過http://localhost:8000/myapp/homepage
但假如一個project中有多個app,用以上的方式來管理url可能會造成比較混亂的局面,為了解決這個問題,我們可以用include的方法來配置url,
首先我們在自己的app中建立一個urls.py,即myproject/myapp/目錄下建立urls.py,然後在其中輸入如下內容:
然後在項目的urls中包含剛剛app中新增的url配置,我們要做的是在myproject/myproject/urls.py輸入如下內容:
2.編輯 mysite/settings.py 檔案前,先設定 TIME_ZONE 為你自己時區。
此外,關注一下檔案頭部的 INSTALLED_APPS 設定項。這裡包括了會在你專案中啟用的所有Django 應用。應用能在多個專案中使用,你也可以打包並且釋出應用,讓別人使用它們。
通常, INSTALLED_APPS 預設包括了以下Django 的自帶應用:
- django.contrib.admin -- 管理員站點,你很快就會使用它。
- django.contrib.auth -- 認證授權系統。
- django.contrib.contenttypes -- 內容型別框架。
- django.contrib.sessions -- 會話框架。
- django.contrib.messages -- 訊息框架。
- django.contrib.staticfiles -- 管理靜態檔案的框架。
這些應用被預設啟用是為了給常規專案提供方便。
python manage.py migrate
這個 migrate 命令檢查 INSTALLED_APPS 設定,為其中的每個應用建立需要的資料表,至於具體會建立什麼,這取決於你的 mysite/settings.py 設定檔案和每個應用的資料庫遷移文件migrate 命令只會為在 INSTALLED_APPS 裡聲明瞭的應用進行資料庫遷移。
如果你不需要某個或某些應用,你可以在執行 migrate 前毫無顧慮地從 INSTALLED_APPS 裡注釋或者刪除掉它們。
mysite/settings.py
INSTALLED_APPS =[
'polls.apps.PollsConfig',//自己新增的應用要新增到setting檔案中所有應用都在.apps中
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
你模型的遷移資料,它被儲存在 polls/migrations/0001_initial.py 裡。
遷移是非常強大的功能,它能讓你在開發過程中持續的改變資料庫結構而不需要重新刪除和建立表- 它專注於使資料庫平滑升級而不會丟失資料。我們會在後面的教程中更加深入的學習這部分內容,現在,你只需要記住,改變模型需要這三步:
- 編輯 models.py 檔案,改變模型。
- 執行 python manage.py makemigrations 為模型的改變生成遷移檔案。
- 執行 python manage.py migrate 來應用資料庫遷移。
3.建立一個管理員賬號
$ python manage.py createsuperuser
啟動開發服務器
$ python manage.py runserver
進入管理站點頁面¶
現在,試著使用你在上一步中建立的超級使用者來登入。然後你將會看到Django 管理頁面的索引頁:
向管理頁面中加入投票(某某)應用¶
但是我們的投票應用在哪呢?它沒在索引頁面裡顯示。
只需要做一件事:我們得告訴管理頁面,問題 Question 物件需要被管理。開啟 polls/admin.py 檔案,把它編輯成下面這樣:
from django.contrib import admin
from .models import Question
admin.site.register(Question)
4.polls/urls.py
fromdjango.urlsimportpath
from.importviews
urlpatterns =[
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
5.模板
模板名稱空間
雖然我們現在可以將模板檔案直接放在 polls/templates 資料夾中(而不是再建立一個 polls 子資料夾),但是這樣做不太好。Django 將會選擇第一個匹配的模板檔案,如果你有一個模板檔案正好和另一個應用中的某個模板檔案重名,Django 沒有辦法 區分 它們。我們需要幫助Django 選擇正確的模板,最簡單的方法就是把他們放入各自的 名稱空間 中,也就是把這些模板放入一個和 自身 應用重名的子資料夾裡。
「載入模板,填充上下文,再返回由它生成的 HttpResponse 物件」是一個非常常用的操作流程。於是Django 提供了一個快捷函式,我們用它來重寫 index() 檢視:
polls/views.py
fromdjango.shortcutsimportrender
from.modelsimportQuestion
defindex(request):
latest_question_list =Question.objects.order_by('-pub_date')[:5]
context ={'latest_question_list': latest_question_list}
returnrender(request, 'polls/index.html', context)
丟擲404 錯誤¶
現在,我們來處理投票詳情檢視——它會顯示指定投票的問題標題。下面是這個檢視的程式碼:
polls/views.py
from django.http import Http404
from django.shortcuts import render
from .models import Question
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
一個快捷函式: get_object_or_404()¶
嘗試用 get() 函式獲取一個物件,如果不存在就丟擲 Http404 錯誤也是一個普遍的流程。Django 也提供了一個快捷函式,下面是修改後的詳情 detail() 檢視程式碼:
polls/views.py
fromdjango.shortcutsimportget_object_or_404, render
from.modelsimportQuestion
defdetail(request, question_id):
question =get_object_or_404(Question, pk=question_id)
returnrender(request, 'polls/detail.html', {'question': question})
去除模板中的硬編碼URL
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
使用 {% url %} 標籤代替硬編碼
這個標籤的工作方式是在 polls.urls 模組的URL 定義中尋具有指定名字的條目。你可以回憶一下,具有名字'detail' 的URL 是在如下語句中定義的:
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
如果你想改變投票詳情檢視的URL,比如想改成 polls/specifics/12/ ,你不用在模板裡修改任何東西(包括其它模板),只要在 polls/urls.py 裡稍微修改一下就行:
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
為URL 名稱新增命名空間
polls/urls.py
fromdjango.urlsimportpath
from.importviews
app_name='polls'
urlpatterns =[
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
修改為指向具有名稱空間的詳細檢視:
polls/templates/polls/index.html
<li><ahref="{%url'polls:detail'question.id%}">{{question.question_text}}</a></li>
6.表單
- 由於我們建立一個POST 表單(它具有修改資料的作用),所以我們需要小心跨站點請求偽造。Django 已經擁有一個用來防禦它的非常容易使用的系統。簡而言之,所有針對內部URL 的POST 表單都應該使用 {% csrf_token %} 模板標籤。
- 每個單選按鈕的 name 是 "choice" 。這意味著,當有人選擇一個單選按鈕並提交表單提交時,它將傳送一個POST 資料 choice=# ,其中# 為選擇的Choice 的ID。這是HTML 表單的基本概念。
表單的 action 為 {% url 'polls:vote' question.id %} ,並設置 method="post" 。使用 method="post"無論何時,當你需要建立一個改變伺服器端資料的表單時,請使用 ``method="post" 。
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
在增加Choice 的得票數之後,程式碼返回一個 HttpResponseRedirect 而不是常用的 HttpResponse 、 HttpResponseRedirect 只接收一個引數:使用者將要被重定向的URL(請繼續看下去,我們將會解釋如何構造這個例子中的URL)。
在這個例子中,我們在 HttpResponseRedirect 的建構函式中使用 reverse() 函式。這個函式避免了我們在檢視函式中硬編碼URL。它需要我們給出我們想要跳轉的檢視的名字和該檢視所對應的URL 模式中需要給該檢視提供的引數。在本例中,使用在 教程第3 部分 中設定的URLconf, reverse() 呼叫將返回一個這樣的字串:
'/polls/3/results/'
使用通用檢視
根據URL 中的引數從資料庫中獲取資料、載入模板檔案然後返回渲染後的模板
改良URLconf
polls/urls.py
fromdjango.urlsimportpath
from.importviews
app_name ='polls'
urlpatterns =[
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
注意,第二個和第三個匹配準則中,路徑字串中匹配模式的名稱已經由 <question_id> 改為 <pk>。
改良檢視
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Choice, Question
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
使用基於類的檢視¶
從本質上講,基於類的檢視允許您使用不同的類例項方法響應不同的HTTP請求方法,而不是在單個檢視函式中使用條件分支程式碼。
那麼在GET檢視函式中處理HTTP的程式碼看起來像這樣:
fromdjango.httpimportHttpResponse
defmy_view(request):
ifrequest.method =='GET':
# <view logic>
returnHttpResponse('result')
在基於類的檢視中,這將變為:
fromdjango.httpimportHttpResponse
fromdjango.viewsimportView
classMyView(View):
defget(self, request):
# <view logic>
returnHttpResponse('result')
因為Django的URL解析器期望將請求和關聯的引數發送到可調用的函式而不是類,所以基於類的檢視具有 as_view()類方法,該類方法返回當請求到達匹配關聯模式的URL時可以呼叫的函式。該函式建立類的例項並呼叫其dispatch()方法。dispatch檢視請求以確定它是否為GET,POST等等,並將請求中繼到匹配方法(如果已定義),或者HttpResponseNotAllowed如果不是則引發 :
# urls.py
from django.urls import path
from myapp.views import MyView
urlpatterns = [
path('about/', MyView.as_view()),
]
值得注意的是,您的方法返回的內容與從基於函式的檢視返回的內容相同,即某種形式的 HttpResponse。這意味著http快捷方式或 TemplateResponse物件在基於類的檢視中有效。
雖然最小的基於類的檢視不需要任何類屬性來執行其工作,但類屬性在許多基於類的設計中很有用,並且有兩種方法來配置或設定類屬性。
總結:
1.reverse() :在這個例子中,我們在 HttpResponseRedirect 的建構函式中使用 reverse() 函式。這個函式避免了我們在檢視函式中硬編碼URL。
2.render() (:「載入模板,填充上下文,再返回由它生成的 HttpResponse 物件」是一個非常常用的操作流程。
3.as_view():基於類的檢視返回
4.generic :通用檢視
Django 的原始檔在哪裡?
如果你不知道Django 原始碼在你係統的哪個位置,執行以下命令:
$ python -c "import django; print(django.__path__)"
所有的Django 默認後臺模板均可被複寫。若要複寫模板,像你修改 base_site.html 一樣修改其它檔案——先將其從默認目錄中拷貝到你的自定義目錄,再做修改。
models
1.欄位選項
null如果True,Django將NULL在資料庫中存儲空值。默認是False。
blank如果True,該欄位允許為空。預設是False。
請注意,這不同於null。 null純粹與資料庫相關,而 blank與驗證相關。如果欄位有 blank=True,則表單驗證將允許輸入空值。如果欄位有blank=False,則需要該欄位
多對一關係¶
要定義多對一關係,請使用django.db.models.ForeignKey。您可以像使用任何其他Field型別一樣使用它:將其包含為模型的類屬性
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE) //manufacturer為car的外來鍵,一個car一個manufacturer,一個manufacturer多個car
多對多關係
class Topping(models.Model):
pass
class Pizza(models.Model):
toppings = models.ManyToManyField(Topping)
一般情況,普通的多對多已經夠用,無需自己創建第三張關係表。但是某些情況可能更復雜一點,比如如果你想儲存額外欄位資訊,例如:某個人加入某個分組的時間呢?想儲存進組的原因呢?-----中間表。與普通的多對多不一樣,使用自定義中間表的多對多不能使用add(), create(),remove(),和set()方法來創建、刪除關係為什麼?因為上面的方法無法提供加入時間、邀請原因等中間模型需要的欄位內容。唯一的辦法只能是通過建立中間模型的例項來建立這種型別的多對多關聯。但是,clear()方法是有效的,它能清空所有的多對多關係。
book = person.book_set.all()//person book 一對多
django 預設每個主表的物件都有一個是外來鍵的屬性,可以通過它來查詢到所有屬於主表的子表的資訊。
這個屬性的名稱預設是以子表的名稱小寫加上_set()來表示,預設返回的是一個querydict物件,你可以繼續的根據情況來查詢等操作。
在實際專案中,我們使用最多的還是related_name
如果你覺得上面的定義比較麻煩的話,你也可以在定義主表的外來鍵的時候,給這個外來鍵定義好一個名稱。要用related_name比如在Book表中: //book表中定義ForeignKey為主表名
person = models.ForeignKey(Person, related_name='person_books')
那麼實現上面的需求,可以使用person.book_set.all()也可以使用person.person_books.all()
在使用中間模型定義從模型到自身的多對多關係時,必須使用 symmetrical=False
模型方法和屬性
Python“魔術方法”,返回任何對象的字串表示形式
這告訴Django如何計算物件的URL。Django在其管理介面中使用它,並且只要它需要找出物件的URL。具有唯一標識它的URL的任何物件都應定義此方法。
模型最重要的屬性是 Manager。默認名稱為 objects。管理員只能通過模型類訪問,而不能通過模型實例訪問。
Django中有三種可能的繼承方式。
- 通常,您只想使用父類來儲存您不希望為每個子模型鍵入的資訊。這個類不會被孤立使用,所以抽象基類就是你所追求的。它不生成資料庫表或具有管理器,並且無法直接實例化或儲存。
- 如果你是現有模型的子類(可能是完全來自另一個應用程式的東西),並希望每個模型都有自己的資料庫表,那麼 多表繼承是最佳選擇。
- 最後,如果您只想修改模型的Python級行為,而不以任何方式更改模型欄位,則可以使用 代理模型
1. 建立和儲存物件,請使用該 create()方法
b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
b.save()
2.更新ManyToManyField工作的方式略有不同
entry.authors.add(john, paul, george, ringo)//add方法可一次性新增多個
3.filter(**kwargs)返回QuerySet包含與給定查找引數匹配的新對象。
exclude(**kwargs)返回QuerySet包含與給定查找引數不匹配的新對象
Entry.objects.filter( headline__startswith='What').exclude(
pub_date__gte=datetime.date.today()).filter(
pub_date__gte=datetime.date(2005, 1, 30) )
manager類
4.filter()QuerySet即使只有一個對象與查詢匹配,它總會給你一個 - 在這種情況下,它將 QuerySet包含一個元素。如果您知道只有一個對象與您的查詢匹配,則可以使用直接返回對象的 get()方法 Manager
基本查找關鍵字引數採用表單field__lookuptype=value。(這是一個雙重下劃線)
Entry.objects.filter(pub_date__lte='2006-01-01')
將(粗略地)翻譯成以下SQL:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
但是有一個例外,如果ForeignKey你可以指定字尾的欄位名稱_id。在這種情況下,value引數應包含外部模型主鍵的原始值。例如:
>>> Entry.objects.filter(blog_id=4)
果您的關鍵字引數不包含雙下劃線- 則假定查詢型別為 exact。
例如,以下兩個語句是等效的:
>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14)
iexact不區分大小寫的匹配項
還有一個不區分大小寫的版本icontains
跨越關係的查找:雙下劃線
Blog.objects.filter(entry__headline__contains='Lennon')
跨越多值關係
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
過濾器可以引用模型上的字段
from django.db.models import F
請注意,這delete()是唯一 QuerySet未在Manager自身上公開的方法 。這是一種安全機制,可以防止您意外請求Entry.objects.delete()和刪除所有條目。如果您確實要刪除所有物件,則必須顯式請求完整的查詢集:
Entry.objects.all().delete()
使用自定義反向管理器¶
預設情況下,RelatedManager用於反向關係的是 該模型的預設管理器的子類。如果要為給定查詢指定其他管理器,可以使用以下語法:
e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')
a = Author.objects.get(id=5)
a.entry_set.all()# Returns all Entry objects for this Author.//而“反向”模型使用原始模型的小寫模型名稱,加上'_set'(就像反向一對多關係一樣)
檢視
1.使用register_converter()以下命令在URLconf中註冊自定義轉換器類 :
from django.urls import path, register_converter
from . import converters, views
register_converter(converters.FourDigitYearConverter, 'yyyy')
//class FourDigitYearConverter
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<yyyy:year>/', views.year_archive),
相關推薦
Django官方文件學習筆記
&nb
elasticsearch官方文件學習筆記----Getting Started
Getting Started 基本概念 1)準實時:ES搜尋是一個接近實時的搜尋平臺。這意味著從您索引一個文件的時間到它可搜尋的時間,有一個輕微的延遲(通常是一秒)。 2)叢集:ES是一個叢集,一個叢集由一個惟一的名稱標識id,預設情況下是“elasticsearch
elasticsearch官方文件學習筆記----ElasticSearch引數配置
配置ElasticSearch 主要介紹了ES啟動前 重要的基本配置 重要的系統基本配置 設定JVM引數 此檔案的預設位置是config/jvm.options(從tar或zip發行版/etc/elasticsearch/jvm.options安裝時)和(從Debi
Flume.apache.org 官方文件學習筆記 part one
Apache Flume 是一個分散式,可靠且可用的系統,用於有效地從許多不同的源收集,聚合和移動大量日誌資料到集中式資料儲存。 Apache Flume的使用不僅限於日誌資料聚合。由於資料來源是可定製的,因此Flume可用於傳輸大量事件資料,包括但不限於網路流量資料
Flume.apache.org 官方文件學習筆記 part two
配置個體元件: 當你定義了這個流之後,你需要去設定每個資源、接收器、通道的屬性。這是在你設定元件型別和每個元件的特定屬性值的同一層名稱空間內完成的。 # properties for sources <Agent>.sources.<S
elasticsearch6.x官方文件學習筆記----Document API翻譯
開始之前先學習 副本 介紹:Elasticsearch的每個索引都被分成shard ,每個shard 可以有多個副本。這些副本稱為複製組,在新增或刪除文件時必須保持同步。如果我們不這樣做,從一個副本閱讀將會導致與從另
elasticsearch6.x官方文件學習筆記----ElasticSearch引數配置
配置ElasticSearch 主要介紹了ES上線前 重要的基本配置 重要的系統基本配置 設定JVM引數 此檔案的預設位置是config/jvm.options(從tar或zip發行版/etc/elasticsearch/jvm.options安裝時)和(從Debian或RPM軟
Flume.apache.org 官方文件學習筆記 part three
JMS 源: jms源閱讀從jms目的地發來的資訊,例如佇列,主題等。 作為一個jms應用程式,他應該和jms提供程式一起工作,但是僅使用ActiveMQ進行測試。JMS源提供可配置的批量大小,訊息選擇器,使用者/傳遞還有訊息到接收器事件轉換器。 要
Flume.apache.org 官方文件學習筆記 part five
kafka 源: Kafka 源是Apache Kafka 消耗者,讀取來自kafka主題的資訊。如果你有多個Kafka源在執行,你可以給他們配置一樣的使用者群組,以便每個源都讀取一組唯一的主題分割槽。 要注
Libev 官方文件學習筆記
請注意這是 libev 而不是 libevent 的文章! 這篇文章是第二篇,主要講 libev 裡的 watcher 的一些基礎操作。 Watcher 解析 以下是一段示意性的程式碼,使用的是ev_io: static void my_cb (struct ev
xarray官方文件 學習筆記(序章)
個人需要開了這個坑不定時更新希望能和大家共同學習和交流工作中難免有不足和錯誤,希望大家多多批評指正此處設定的版本為stable,stable版本的官方文件是本專欄的主要參考資料。-------------------------------------------------
Kafka Introduction 官方文件學習筆記
補充:例如途Partition 0分割槽的0,1,2....10,11,12...都是分割槽中訊息的偏移。 Each partition is an ordered, immutable sequence of records that is continually appended to—a struct
tensorflow學習筆記十六:tensorflow官方文件學習 Image Recognition(Inception v3模型)
我們大腦的成像過程似乎很容易。人們毫不費力地就能區分出獅子和美洲虎,閱讀符號,或是識別面孔。但是這些任務對於計算機而言卻是一個大難題:它們之所以看上去簡單,是因為我們的大腦有著超乎想象的能力來理解影象。 在過去幾年裡,機器學習在解決這些難題方面取得了巨大的進步。其中,
vue.js 2.0 官方文檔學習筆記 —— 01. vue 介紹
lan fun 數據 特性 sem https 代碼 guide pos 這是我的vue.js 2.0的學習筆記,采取了將官方文檔中的代碼集中到一個文件的形式。目的是保存下來,方便自己查閱。 !官方文檔:https://cn.vuejs.org/v2/guide/ 01.
使用DOM解析器解析XML文件 學習筆記
使用DOM解析器解析XML文件 學習筆記dom解析和dom4j原理一致 Node是所有元素的父接口 常用的API: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();取得DOM解析器工廠 DocumentBuilder
pm2 官方文檔 學習筆記
ORC 3.1 iou rev 守護 itl 監視 如何 gist 一、安裝 1、安裝 npm install pm2 -g 2、更新 npm install pm2 -g && pm2 update pm2 update 是為了刷新 PM2 的守護
java獲取文件下指定文件學習筆記
指定 文件路徑 bool with nds println 獲取 amp string //獲取路徑 File dirs = new File("C:\\Users\\lw\\\\Pictures"); //匿名函數獲取文件路徑 String[] arr = dirs.
OpenCV-Python 官方文件學習
影象學習: 目標: 1.在這裡你會學習到如何讀取一個影象,如何取顯示並且儲存它。 2.你將徐匯這些方法:cv2.imread() , cv2.imshow() , cv2.imwrite() ; 3.自然而然的你就學會了如何使用Matplotlib庫去顯示圖片。 OpenCV的使用
webpack官方文件學習
安裝依賴的簡寫說明:以vue為例 cnpm i vue -S // 生產依賴 等價於 --save cnpm i vue-loader css-loader vue-template-compiler -D
java字符串去重寫文件學習筆記
app 使用 一行 寫文件 ont 字符數 code ava buffer /** * * 使用Scanner從鍵盤讀取一行輸入,去掉其中重復字符, 打印出不同的那些字符 * aaaabbbcccddd * * 分析: