第四章 模板 1.標簽 (1)if/else {% if %} 標簽檢查(evaluate)一個變數,如果這個變數為真(即,變數存在,非空,不是布爾值假),系統會顯示在 {% if %} 和 {% endif %} 之間的任何內容,例如: {% else %} 標簽是可選的: {% if %} 標簽 ...
第四章 模板
1.標簽
(1)if/else
{% if %} 標簽檢查(evaluate)一個變數,如果這個變數為真(即,變數存在,非空,不是布爾值假),系統會
顯示在 {% if %} 和 {% endif %} 之間的任何內容,例如:
{% if today_is_weekend %} <p>Welcome to the weekend!</p> {% endif %}
{% else %} 標簽是可選的:
{% if today_is_weekend %} <p>Welcome to the weekend!</p> {% else %} <p>Get back to work.</p> {% endif %}
{% if %} 標簽接受 and , or 或者 not 關鍵字來對多個變數做判斷 ,或者對變數取反( not )
(2)for
{% for %} 允許我們在一個序列上迭代。 與Python的 for 語句的情形類似,迴圈語法是 for X in Y ,Y是要迭代的序列而X是在每一個特定的迴圈中使用的變數名稱。 每一次迴圈中,模板系統會渲染在 {% for %} 和
{% endfor %} 之間的所有內容。
例如,給定一個運動員列表 athlete_list 變數,我們可以使用下麵的代碼來顯示這個列表:
<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul>
給標簽增加一個 reversed 使得該列表被反向迭代:
{% for athlete in athlete_list reversed %}
...
{% endfor %}
可以嵌套使用 {% for %} 標簽:
{% for athlete in athlete_list %} <h1>{{ athlete.name }}</h1> <ul> {% for sport in athlete.sports_played %} <li>{{ sport }}</li> {% endfor %} </ul> {% endfor %}
Django不支持退出迴圈操作。 如果我們想退出迴圈,可以改變正在迭代的變數,讓其僅僅包含需要迭代的項目。 同理,Django也不支持continue語句,我們無法讓當前迭代操作跳回到迴圈頭部。
在每個`` {% for %}``迴圈里有一個稱為`` forloop`` 的模板變數。這個變數有一些提示迴圈進度信息的屬性。
forloop.counter 總是一個表示當前迴圈的執行次數的整數計數器。 這個計數器是從1開始的,所以在第一次迴圈時 forloop.counter 將會被設置為1。
例子:
def lists(request): lists = [1,2,3,4,5] return render(request,"lists.html",{'lists':lists})
{% for item in lists %} <p>{{ forloop.counter }}:{{ item }}</p> {% endfor %}
結果:
1:1
2:2
3:3
4:4
5:5
-->>forloop.counter0 類似於 forloop.counter ,但是它是從0計數的。 第一次執行迴圈時這個變數會被設置為0。
-->>forloop.revcounter0 類似於 forloop.revcounter ,但它以0做為結束索引。 在第一次執行迴圈時,該變數會被置為序列的項的個數減1
forloop.first 是一個布爾值,如果該迭代是第一次執行,那麼它被置為````
{% for item in lists %} {% if forloop.first %} <li style="color: red">{{ forloop.counter }}:{{ item }}</li> {% else %} <li>{{ forloop.counter }}:{{ item }}</li> {% endif %} {% endfor %}
結果:
forloop.last 是一個布爾值;在最後一次執行迴圈時被置為True。
forloop.parentloop 是一個指向當前迴圈的上一級迴圈的 forloop 對象的引用(在嵌套迴圈的情況下)。
2.註釋
就像HTML或者Python,Django模板語言同樣提供代碼註釋。 註釋使用 {# #} :
{# This is a comment #}
如果要實現多行註釋,可以使用`` {% comment %}`` 模板標簽,就像這樣:
{% comment %}
This is a
multi‐line comment.
{% endcomment %}
3.過濾器
模板過濾器是在變數被顯示前修改它的值的一個簡單方法。 過濾器使用管道字元,
{{ name|lower }} #它功能是轉換文本為小寫。
過濾管道可以被* 套接* ,既是說,一個過濾器管道的輸出又可以作為下一個管道的輸入,如此下去
{{ my_list|first|upper }} #查找列表的第一個元素並將其轉化為大寫。
4.locals() 技巧
如果你是個喜歡偷懶的程式員並想讓代碼看起來更加簡明,可以利用 Python 的內建函數 locals() 。它返回的字典對所有局部變數的名稱與值進行映射。 因此,前面的視圖可以重寫成下麵這個樣子:
def lists(request): lists = [1,2,3,4,5] return render(request,"lists.html",locals())
locals() 的值,它囊括了函數執行到該時間點時所定義的一切變數
5.模板繼承
(1)定義基礎模板base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site</p> {% endblock %} </body> </html>
這個叫做 base.html 的模板定義了一個簡單的 HTML 框架文檔,我們將在所有頁面中使用。 子模板的作用就是重載、添加或保留那些塊的內容。
所有的 {% block %} 標簽告訴模板引擎,子模板可以重載這些部分。 每個 {% block %} 標簽所要做的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。
(2)子模板current_time.html
{% extends 'base.html' %} {% block title %}The current time{% endblock%} {% block content %} <p>It is now {{ current_date }}</p> {% endblock %}
(3)views.py
from django.shortcuts import HttpResponse,render import datetime def current_datetime(request): current_date = datetime.datetime.now() return render(request,'cuttent_datetime.html',{'current_date':current_date})
以下是其工作方式:在載入 current_datetime.html 模板時,模板引擎發現了 {% extends %} 標簽,模板引擎立即裝載其父模板,即本例中的 base.html 。此時,模板引擎註意到 base.html 中的三個 {% block %} 標簽,並用子模板的內容替換這些 block 。因此,引擎將會使用我們在 { block title %} 中定義的標題,對 {% block content %} 也是如此。 所以,網頁標題一塊將由 {% block title %} 替換,同樣地,網頁的內容一塊將由 {% block content %} 替換。註意由於子模板並沒有定義 footer 塊,模板系統將使用在父模板中定義的值。 父模板 {% block %} 標簽中的內容總是被當作一條退路。繼承並不會影響到模板的上下文。 換句話說,任何處在繼承樹上的模板都可以訪問到你傳到模板中的每一個模板變數。
如果在模板中使用 {% extends %} ,必須保證其為模板中的第一個模板標記。 否則,模板繼承將不起作 用。 一般來說,基礎模板中的 {% block %} 標簽越多越好。 記住,子模板不必定義父模板中所有的代碼塊,因 此你可以用合理的預設值對一些代碼塊進行填充,然後只對子模板所需的代碼塊進行(重)定義。 俗話 說,鉤子越多越好。 如果發覺自己在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。 如果你需要訪問父模板中的塊的內容,使用 {{ block.super }} 這個標簽吧,這一個魔法變數將會表現出 父模板中的內容。 如果只想在上級代碼塊基礎上添加內容,而不是全部重載,該變數就顯得非常有用了。 不允許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是因為block 標簽的工作方式是雙 向的。 也就是說,block 標簽不僅挖了一個要填的坑,也定義了在 父 模板中這個坑所填充的內容。如果模 板中出現了兩個相同名稱的 {% block %} 標簽,父模板將無從得知要使用哪個塊的內容。 {% extends %} 對所傳入模板名稱使用的載入方法和 get_template() 相同。 也就是說,會將模板名稱被添 加到 TEMPLATE_DIRS 設置之後。 多數情況下, {% extends %} 的參數應該是字元串,但是如果直到運行時方能確定父模板名,這個參數也 可以是個變數。 這使得你能夠實現一些很酷的動態功能。模板繼承的一些訣竅
第五章 模型
1.MTV and MVC
把數據存取邏輯、業務邏輯和表現邏輯組合在一起的概念有時被稱為軟體架構的Model-View-Controller(MVC)模式。 在這個模式中, Model 代表數據存取層,View 代表的是系統中選擇顯示什麼和怎麼顯示的部分,Controller 指的是系統中根據用戶輸入並視需要訪問模型,以決定使用哪個視圖的那部分。M:數據存取部分,由django資料庫層處理,本章要講述的內容。
V:選擇顯示哪些數據要顯示以及怎樣顯示的部分,由視圖和模板處理。 C:根據用戶輸入委派視圖的部分,由 Django 框架根據 URLconf 設置,對給定 URL 調用適當的Python 函數由於 C 由框架自行處理,而 Django 里更關註的是模型(Model)、模板(Template)和視圖(Views),Django 也被稱為MTV 框架。在 MTV 開發模式中:
M:代表模型(Model),即數據存取層。 該層處理與數據相關的所有事務: 如何存取、如何驗證有效性、包含哪些行為以及數據之間的關係等
T:代表模板(Template),即表現層。 該層處理與表現相關的決定: 如何在頁面或其他類型文檔中進行顯示。
V:代表視圖(View),即業務邏輯層。 該層包含存取模型及調取恰當模板的相關邏輯。 你可以把它看作模型與模板之間的橋梁。
2.設置資料庫為Mysql
在settings裡面修改配置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django', #資料庫名字 'USER': 'root', #賬號 'PASSWORD': '123456', #密碼 'HOST': '127.0.0.1', #IP 'PORT': '3306', #埠 } }
在app的init.py裡面導入pymysql模塊
import pymysql pymysql.install_as_MySQLdb()
設置好後打開 python manage.py shell 來進行測試。輸入一下命令,如果沒有報錯說明,說明資料庫配置是正確的
>>> from django.db import connection >>> cursor = connection.cursor()
3.第一個模型
書籍/作者/出版商 資料庫
一個作者有姓,有名及email地址。
出版商有名稱,地址,所在城市、省,國家,網站。 書籍有書名和出版日期。 它有一個或多個作者(和作者是多對多的關聯關係[many-to-many]), 只有一個出版商(和出版商是一對多的關聯關係[one-to-many],也被稱作外 鍵[foreign key])from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() class Book(models.Model): title = models.CharField(max_length=100) publication_date = models.DateField() authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher,on_delete = models.CASCADE)
打開shell,添加publisher
當我們列印整個publisher列表時,我們沒有得到想要的有用信息,只需要為Publisher對象添加一個__str__方法 ,就可以對Publisher對象更容易理解
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __str__(self): return self.name class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() def __str__(self): return '%s%s'%(self.first_name,self.last_name) class Book(models.Model): title = models.CharField(max_length=100) publication_date = models.DateField() authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher,on_delete = models.CASCADE) def __str__(self): return self.title添加__str__方法
4.數據過濾
可以使用`` filter()`` 方法對數據進行過濾:
也可以傳入多個參數
Publisher.objects.filter(country="U.S.A.", state_province="CA")
魔術般的操作
Publisher.objects.filter(name__contains="Apr")其他的一些查找類型有: icontains(大小寫無關的LIKE),startswith和endswith, 還有range
5.獲取單個對象
上面的例子中`` filter()`` 函數返回一個記錄集,這個記錄集是一個列表。 相對列表來說,有些時候我們更需要獲取單個的對象, `` get()`` 方法就是在此時使用的:這樣,就返回了單個對象,而不是列表(更準確的說,QuerySet)。 所以,如果結果是多個對象,會導致拋出異常:
如果查詢沒有返回結果也會拋出異常:
6.數據排序
在運行前面的例子中,你可能已經註意到返回的結果是無序的。 我們還沒有告訴資料庫 怎樣對結果進行排序,所以我們返回的結果是無序的。 在你的 Django 應用中,你或許希望根據某欄位的值對檢索結果排序,比如說,按字母順序。 那麼,使用order_by()這個方法就可以搞定了。我們可以對任意欄位進行排序,如果需要以多個欄位為標準進行排序(第二個欄位會在第一個欄位的值相同的情況下被使用到),使用多個參數就可以了,如下:
我們還可以指定逆向排序,在前面加一個減號‐首碼:限制返回的數據
7.更新和刪除對象
update()方法對於任何結果集(QuerySet)均有效,這意味著你可以同時更新多條記錄。 以下示例演示如何將所有Publisher的country欄位值由’U.S.A’更改為’USA’:
update()方法會返回一個整型數值,表示受影響的記錄條數。 在上面的例子中,這個值是2
刪除資料庫中的對象只需調用該對象的delete()方法即可:
第六章 Django站點管理
1.創建admin用戶
python manage.py createsuperuser
訪問:http://127.0.0.1:8000/admin/,輸入用戶名,密碼登錄進入管理界面
可以看到只有Groutps和Users
要向讓app book裡面的models顯示在這裡面,只需把book註冊到admin
book app下的admin.py
from django.contrib import admin from book import models # Register your models here. admin.site.register(models.Author) admin.site.register(models.Book) admin.site.register(models.Publisher)
然後就可以在管理界面看到
中文顯示的方法:Meta
class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Meta: verbose_name_plural = "出版社" def __str__(self): return self.name
2.Admin工作原理
當服務啟動時,Django從`` url.py`` 引導URLconf,然後執行`` admin.autodiscover()`` 語句。 這個函數遍歷INSTALLED_APPS配置,並且尋找相關的admin.py文件。 如果在指定的app目錄下找到admin.py,它就執行其中的代碼。 在`` book`` 應用程式目錄下的`` admin.py`` 文件中,每次調用`` admin.site.register()`` 都將那個模塊註冊到管理工具中。 管理工具只為那些明確註冊了的模塊顯示一個編輯/修改的界面。應用程式`` django.contrib.auth`` 包含自身的`` admin.py`` ,所以Users和Groups能在管理工具中自動顯示。其它的django.contrib應用程式,如django.contrib.redirects,其它從網上下在的第三方Django應用程式一樣,都會自行添加到管理工具。3.設置欄位可選
你或許會發現管理工具有個限制:編輯表單需要你填寫每一個欄位,然而在有些情況下,你想要某些欄位是可選的。 舉個例子,我們想要Author模塊中的email欄位成為可選,即允許不填。 在現實世界中,你可能沒有為每個作者登記郵箱地址。
為了指定email欄位為可選,你只要編輯Book模塊class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(null=True,blank=True)
這些代碼告訴Django,作者的郵箱地址允許輸入一個空值
4.自定義欄位標簽
在編輯頁面中,每個欄位的標簽都是從模塊的欄位名稱生成的。規則很簡單: 用空格替換下劃線;首字母大寫
然而,欄位名稱並不總是貼切的。有些情況下,你可能想自定義一個標簽。 你只需在模塊中指定verbose_name。舉個例子,說明如何將Author.email的標簽改為e-mail,中間有個橫線。
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(null=True,blank=True,verbose_name='e-mail')
改之前
改之後
5.自定義ModelAdmi類
迄今為止,我們做的blank=True、null=True和verbose_name修改其實是模塊級別,而不是管理級別的。 也就是說,這些修改實質上是構成模塊的一部分,並且正好被管理工具使用,而不是專門針對管理工具的。
除了這些,Django還提供了大量選項讓你針對特別的模塊自定義管理工具。 這些選項都在ModelAdminclasses裡面,這些類包含了管理工具中針對特別模塊的配置。(1)自定義列表
預設情況下,Author下麵只顯示每個作者的姓名
我們可以在這基礎上改進,添加其它欄位,從而改變列表的顯示。比如說:在這個列表中可以看到作者的郵箱地址,為了達到這個目的,我們將為Author模塊定義一個ModelAdmin類。 這個類是自定義管理工具的關鍵,其中最基本的一件事情是允許你指定列表中的欄位。
修改admin.py
from django.contrib import admin from book import models class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name','last_name','email') admin.site.register(models.Author,AuthorAdmin) admin.site.register(models.Book) admin.site.register(models.Publisher)
可以看到如下效果
解釋一下代碼:
我們新建了一個類AuthorAdmin,它是從django.contrib.admin.ModelAdmin派生出來的子類,保存著一個類的自定義配置,以供管理工具使用。我們只自定義了一項:list_display,它是一個欄位名稱的元組,用於列表顯示。當然,這些欄位名稱必須是模塊中有的。我們修改了admin.site.register()調用,在Author後面添加了AuthorAdmin。你可以這樣理解:用AuthorAdmin選項註冊Author模塊。
(2)添加快速查詢欄
class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name','last_name','email') search_fields = ('first_name','last_name')
在頁面頂端看到一個查詢欄
(3)添加過濾器
接下來,讓我們為Book列表頁添加一些過濾器。class BookAdmin(admin.ModelAdmin): list_display = ('title','publisher','publication_date') list_filter = ('publication_date',) admin.site.register(models.Author,AuthorAdmin) admin.site.register(models.Book,BookAdmin) admin.site.register(models.Publisher)
可以看到右邊有filter,Django為日期型欄位提供了快捷過濾方式,它包含:今天、過往七天、當月和今年
另外一種過濾日期的方式是使用date_hierarchy選項,如:class BookAdmin(admin.ModelAdmin): list_display = ('title','publisher','publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date'修改好後,頁面中的列表頂端會有一個逐層深入的導航條,效果如下. 它從可用的年份開始,然後逐層細分到月乃至日。 請註意,date_hierarchy接受的是* 字元串* ,而不是元組。因為只能對一個日期型欄位進行層次劃分。 (4)降序排列 讓我們改變預設的排序方式,按publication date降序排列。
class BookAdmin(admin.ModelAdmin): list_display = ('title','publisher','publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',)
(5)自定義編輯表單
正如自定義列表那樣,編輯表單多方面也能自定義。 首先,我們先自定義欄位順序。 預設地,表單中的欄位順序是與模塊中定義是一致的。 我們可以通過使用ModelAdmin子類中的fields選項來改變它:class BookAdmin(admin.ModelAdmin): list_display = ('title','publisher','publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) fields = ('title', 'authors', 'publisher', 'publication_date')
改之前:
改之後:
通過fields這個選項,你可以排除一些不想被其他人編輯的fields 只要不選上不想被編輯的field(s)即可。
例如,在book資料庫中,我們可以隱藏publication_date,以防止它被編輯。fields = ('title', 'authors', 'publisher')
另一個常用的編輯頁面自定義是針對多對多欄位的。 真如我們在book編輯頁面看到的那樣,`` 多對多欄位`` 被展現成多選框。雖然多選框在邏輯上是最適合的HTML控制項,但它卻不那麼好用。 如果你想選擇多項,你必須還要按下Ctrl鍵。 雖然管理工具因此添加了註釋(help_text),但是當它有幾百個選項時,它依然顯得笨拙。 更好的辦法是使用 filter_horizontal。讓我們把它添加到BookAdmin中,然後看看它的效果。
class BookAdmin(admin.ModelAdmin): list_display = ('title','publisher','publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) filter_horizontal = ('authors',)請註意移除fields選項,以使得編輯頁麵包含所有欄位。 加之前
加之後,明顯方便多了
針對那些擁有十個以上選項的`` 多對多欄位`` 使用filter_horizontal。 這比多選框好用多了。 ModelAdmin類還支持filter_vertical選項。 它像filter_horizontal那樣工作,除了控制項都是垂直排列,而不是水平排列的。 至於使用哪個,只是個人喜好問題。filter_vertical = ('authors',)filter_horizontal和filter_vertical選項只能用在多對多欄位上, 而不能用於ForeignKey欄位。 預設地,管理工具使用`` 下拉框`` 來展現`` 外鍵`` 欄位。但是,正如`` 多對多欄位`` 那樣,有時候你不想忍受因裝載並顯示這些選項而產生的大量開銷。 例如,我們的book資料庫膨脹到擁有數千條publishers的記錄,以致於book的添加頁面裝載時間較久,因為它必須把每一個publisher都裝載並顯示在`` 下拉框`` 中。 解決這個問題的辦法是使用`` raw_id_fields`` 選項。它是一個包含外鍵欄位名稱的元組,它包含的欄位將被展現成`` 文本框`` ,而不再是`` 下拉框`` 。
class BookAdmin(admin.ModelAdmin): list_display = ('title','publisher','publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) filter_vertical = ('authors',) raw_id_fields = ('publisher',)
效果:
在這個輸入框中,你輸入什麼呢? publisher的資料庫ID號。 考慮到人們通常不會記住這些資料庫ID,管理工具提供了一個放大鏡圖標方便你輸入。點擊那個圖標將會彈出一個視窗,在那裡你可以選擇想要添加的publisher。第七章 用戶、用戶組和許可權
因為你是用超級用戶登錄的,你可以創建,編輯和刪除任何對像。 然而,不同的環境要求有不同的許可權,系統不允許所有人都是超級用戶。 管理工具有一個用戶許可權系統,通過它你可以根據用戶的需要來指定他們的許可權,從而達到部分訪問系統的目的。 你通過管理界面編輯用戶及其許可就像你編輯別的對象一樣。 瀏覽用戶和用戶組區域的時候已經見過這些了。 如你所想,用戶對象有標準的用戶名、密碼、郵箱地址和真實姓名,同時它還有關於使用管理界面的許可權定義。 首先,這有一組三個布爾型標記: 活動標誌:它用來控制用戶是否已經激活。 如果一個用戶帳號的這個標記是關閉狀態,而用戶又嘗試用它 登錄時,即使密碼正確,他也無法登錄系統。 成員標誌:它用來控制這個用戶是否可以登錄管理界面(即:這個用戶是不是你們組織里的成員) 由於用 戶系統可以被用於控制公眾頁面(即:非管理頁面)的訪問許可權(詳見第十四章),這個標誌可用來區分 公眾用戶和管理用戶。 超級用戶標誌:它賦予用戶在管理界面中添加、修改和刪除任何項目的許可權。 如果一個用戶帳號有這個標 志,那麼所有許可權設置(即使沒有)都會被忽略。 普通的活躍,非超級用戶的管理用戶可以根據一套設定好的許可進入。 管理界面中每種可編輯的對象(如:books、authors、publishers)都有三種許可權:創建許可,編輯許可和刪除許可。 給一個用戶授權許可也就表明該用戶可以進行許可描述的操作。 當你創建一個用戶時,它沒有任何許可權,該有什麼許可權是由你決定的。 例如,你可以給一個用戶添加和修改publishers的許可權,而不給他刪除的許可權。 請註意,這些許可權是定義在模塊級別上,而不是對象級別上的。據個例子,你可以讓小強修改任何圖書,但是不能讓他僅修改由機械工業出版社出版的圖書。 後面這種基於對象級別的許可權設置比較複雜,並且超出了本書的覆蓋範圍。註釋:
許可權管理系統也控制編輯用戶和許可權。 如果你給某人編輯用戶的許可權,他可以編輯自己的許可權,這種能力可能不是你希望的。 賦予一個用戶修改用戶的許可權,本質上說就是把他變成一個超級用戶。你也可以給組中分配用戶。 一個組簡化了給組中所有成員應用一套許可的動作。 組在給大量用戶特定許可權的時候很有用。