1. 程式人生 > >[python] python django web 開發 —— 15分鐘送到會用(只能送你到這了)

[python] python django web 開發 —— 15分鐘送到會用(只能送你到這了)


1、安裝python環境

1.1 安裝python包管理器:

wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py

 
1.2 安裝python虛擬環境virtualenv virtualenvwrapper

首先說明下為什麼要裝這兩個包:

First, it’s important to understand that a virtual environment is a special tool used to keep the dependencies required by different projects in separate places by creating isolated, independent Python environments for each of them.

In short, it solves the “Project X depends on version 1.x, but Project Y needs 4.x” dilemma. It also keeps your global site-packages neat, tidy, and free from clutter.

If you would like a full explanation on why Python virtual environments are good practice, absolutely give this excellent blog post on RealPython a read.

用虛擬開發環境可以為每個工程提供獨立的python開發環境、獨立的包、獨立的版本,每個獨立的環境會在~/.virtualenvs/下形成資源包~

sudo pip install virtualenv virtualenvwrapper
sudo rm -rf ~/.cache/pip

之後在~/.profile檔案最後新增下面幾行:

# virtualenv and virtualenvwrapper
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh

之後如果想用python虛擬環境,在每次開啟一個新的terminal就要執行一次source ~/.profile

source ~/.profile

接下來我們生成一個python虛擬環境來用於python-web的開發提供環境:(這裡用的是python2.7)

mkvirtualenv python_web -p python2

:再次說明python虛擬環境是完全獨立的,也就是說在python_web的環境下安裝的python包,步適用於全域性;在全域性安裝的包,不適合python_web。

如何驗證你如何將python_web環境生成好了呢?——新開一個terminal,執行下列命令:

source ~/.profile
workon python_web

如果terminal前面的文字變成了(python_web)表明成功建立了名為cv的python虛擬環境;

 

2、安裝Django

從官網上得知2.7版本的python可使用最高1.11版本的Django,因此在python_web環境中安裝:

pip install Django==1.11

測試Django有沒有安裝成功,進入python命令互動模式:

import django
django.VERSION

 

3、第一個例子hello world

找到你的django-admin.py檔案,並把它加入系統路徑。如果用的是setup.py工具安裝的Django,django-admin.py應該已被加入了系統路徑中。我的django-admin.py的目錄為:

/root/.virtualenvs/python_web/lib/python2.7/site-packages/django/bin 

進入該目錄下,執行如下命令,新建一個專案:

python django-admin.py startproject mysite

startproject命令建立一個目錄,包含一個名為mysite的資料夾和一個名為manage.py的檔案。其中mysite資料夾下包含有四個檔案,分別為:

(python_web) ➜  mysite tree
.
├── db.sqlite3
├── manage.py
└── mysite
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

為了安裝後更多的體驗,讓我們執行一下django開發伺服器看看我們的準系統。django開發服務是可用在開發期間的,一個內建的,輕量的web服務。 我們提供這個伺服器是為了讓你快速開發站點,也就是說在準備釋出產品之前,無需進行產品級 Web 伺服器(比如 Apache)的配置工作。 開發伺服器監測你的程式碼並自動載入它,這樣你會很容易修改程式碼而不用重啟動服務。如果你還沒啟動伺服器的話,請切換到你的專案目錄裡 (cd mysite),執行下面的命令:

python manage.py runserver

你會看到如下內容:

Django version 1.11, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:3000/
Quit the server with CTRL-BREAK

這將會在埠3000啟動一個本地伺服器, 並且只能從你的這臺電腦連線和訪問。 既然伺服器已經執行起來了,現在用網頁瀏覽器訪問 http://127.0.0.1:8000/ 。 你應該可以看到一個令人賞心悅目的淡藍色Django歡迎頁面。 表明它開始工作了。

但是我的伺服器搭在阿里雲上,並且綁定了phage.cc的域名,因此可以通過這樣的方式使之能訪問:

python manage.py runserver 0.0.0.0:3000

注:0.0.0.0”這個IP地址,告訴伺服器去偵聽任意的網路介面。
注:採用phage.cc:3000去訪問會報錯誤 alid HTTP_HOST header: 'www.phage.cc:3000'. You may need to add u'www.phage.cc' to ALLOWED_HOSTS. 可以通過新增允許來實現通過:

settings.py : ALLOWED_HOSTS = [u'www.phage.cc']

 

4、自己建檢視

4.1 靜態檢視hello world

在資料夾mysite中新建一個views.py的檔案:

from django.http import HttpResponse

def hello(request):
    return HttpResponse("Hello world")

在這段程式碼中:我們定義一個叫做hello 的檢視函式,這個函式只有簡單的一行程式碼: 它僅僅返回一個HttpResponse物件,這個物件包含了文字“Hello world”。

注:每個檢視函式至少要有一個引數,通常被叫作request。 這是一個觸發這個檢視、包含當前Web請求資訊的物件,是類django.http.HttpRequest的一個例項。在這個示例中,我們雖然不用request做任何事情,然而它仍必須是這個檢視的第一個引數。

注:檢視函式的名稱並不重要;並不一定非得以某種特定的方式命名才能讓 Django 識別它。 在這裡我們把它命名為:hello,是因為這個名稱清晰的顯示了檢視的用意。

 
4.2 URLconf將檢視和URL繫結(類似nodejs中的路由)

URLconf 就像是 Django 所支撐網站的目錄。 它的本質是 URL 模式以及要為該 URL 模式呼叫的檢視函式之間的對映表。 你就是以這種方式告訴 Django,對於這個 URL 呼叫這段程式碼,對於那個 URL 呼叫那段程式碼。

這個對映表在urls.py中,我們想要實現訪問/hello/呼叫hello檢視,返回hello world需要做下面樣子修改:

from django.conf.urls import url

urlpatterns = [
    url(r'^hello/$', hello),
]

注: 這裡的^hello/$是正則表示式,匹配所有/hello/形式的請求。

之後我們執行該伺服器,在瀏覽器中可以訪問hello檢視: http://www.phage.cc:3000/hello/

 
4.3 動態內容檢視請求當前時間

在views.py中新增一個新檢視current_datatime:

from django.http import HttpResponse
import datetime

def hello(request):
    return HttpResponse("Hello world")

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

類似hello檢視,這裡用了python的datetime工具,獲取時間併合成一個html字串,作為檢視返回。

同理,我們也需要在urls.py中做url對映:

from django.conf.urls import url
from mysite.views import hello, current_datetime

urlpatterns = [
    url(r'^hello/$', hello),
    url(r'^time/$', current_datetime),
]

這樣我們通過訪問 http://www.phage.cc:3000/time/ 可以獲取time檢視返回。

 
4.4 動態URL檢視

在我們的current_datetime 檢視範例中,儘管內容是動態的,但是URL ( /time/ )是靜態的。 在 大多數動態web應用程式,URL通常都包含有相關的引數。 舉個例子,一家線上書店會為每一本書提供一個URL,如:/books/243/、/books/81196/。

讓我們建立第三個檢視來顯示當前時間和加上時間偏差量的時間,設計是這樣的: /time/plus/1/ 顯示當前時間+1個小時的頁面 /time/plus/2/ 顯示當前時間+2個小時的頁面 /time/plus/3/ 顯示當前時間+3個小時的頁面,以此類推。

注: 在java或php中有可能見到這樣的實現:/time/plus?hours=3,但這樣被認為不漂亮

之前我們已經看到url是以正則表示式的形式出現,因此想要實現/time/plus/xxx/也就比較容易了:

from django.conf.urls import url
from mysite.views import hello, current_datetime, hours_ahead

urlpatterns = [
    url(r'^hello/$', hello),
    url(r'^time/$', current_datetime),
    url(r'^time/plus/(\d{1,2})/$', hours_ahead),
]

那麼我們如是實現hours_ahead來接收請求中的xxx數字呢?

from django.http import Http404, HttpResponse
import datetime

...
def hours_ahead(request, offset):
    try:
        offset = int(offset)
    except ValueError:
        raise Http404()
    dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
    html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
    return HttpResponse(html)

hours_ahead 和我們以前寫的 current_datetime 很象,關鍵的區別在於: 它多了一個額外引數,時間差。

注: offset 是從匹配的URL裡提取出來的。 例如:如果請求URL是/time/plus/3/,那麼offset將會是3;如果請求URL是/time/plus/21/,那麼offset將會是21。請注意:捕獲值永遠都是字串(string)型別,而不會是整數(integer)型別,即使這個字串全由數字構成(如:“21”)。

注: 在這裡我們命名變數為 offset ,你也可以任意命名它,只要符合Python 的語法。 變數名是無關緊要的,重要的是它的位置,它是這個函式的第二個 引數 (在 request 的後面)。 你還可以使用關鍵字來定義它,而不是用 位置。

 

5、模板

5.1 最簡單的模板DEMO

模板的好處是將python和html分開,下面是一個最簡單的例子:

def template_test(request):
    now = datetime.datetime.now()
    t = Template("<html><body>It is now {{ current_date }}.</body></html>");
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

上面的例子在html中嵌入一個 current_date 變數,通過context給變數賦值,通過render來渲染。除了雙大括號表示的變數,還有迴圈、條件等各種玩法: https://docs.djangoproject.com/en/2.1/ref/templates/builtins/ 。

 
5.2 將html和python徹底分離

但是上面我們並沒有真正將html和python分離,更進一步的做法是將html單獨放置:

1) 在mysite下新建一個資料夾:templates,並在其中新建一個template_test1.html:

<html><body>It is now {{ current_date }}.</body></html>

2) 而我們的template_test就能改造成:

def template_test1(request):
    now = datetime.datetime.now()
    t = get_template('template_test1.html');
    html = t.render({'current_date': now})
    return HttpResponse(html)

3) 最後我們得通過下面方法讓get_template的輸入引數不用寫完整路徑:

TEMPLATES = [
    {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),],
    'APP_DIRS': True,
    'OPTIONS': {

注:我們還可以用render_to_response來簡化template_test操作:

def template_test2(request):
    now = datetime.datetime.now()
    return render_to_response('template_test1.html', {'current_date': now})

 
5.3 模板繼承

一個多頁面的網站,其每個頁面可能會有相同的頭部、尾部的結構,主頁面的內容存在更新變動。如果我們為每個頁面單獨建立一個獨立的html將會產生大量冗餘,此外如果我們想要對所有頁面的頭部做一個修改,也將比較麻煩。此時我們可以採用模板的思想來完美解決這個問題:

1)新建一個母版html(base.html)

(python_web) ➜  templates git:(master) cat base.html 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
    <head>
    <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>

2) 建立一個繼承base.html的template_test2_use_base_1.html:

(python_web) ➜  templates git:(master) cat template_test2_use_base_1.html 
{% extends "base.html" %}

{% block title %}The current time{% endblock %}

{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

3) 再建立一個繼承base.html的template_test2_use_base_2.html:

(python_web) ➜  templates git:(master) cat template_test2_use_base_2.html 
{% extends "base.html" %}

{% block title %}Future time{% endblock %}

{% block content %}
<p>In {{ hour_offset  }} hour(s), it will be {{ next_time  }}.</p>
{% endblock %}

可見base.html中的{% block title %}{% endblock %} 、{% block content %}{% endblock %} 、{% block footer %}{% endblock %} 都可以被繼承者們重新實現!

注:當然,如果繼承者沒有實現,則不會顯示。

 

6、python django的資料庫操作

6.1 安裝MYSQL資料庫

我們先在linux上安裝資料庫:

sudo apt-get install mysql-server
sudo apt-get install libmysqlclient-dev

安裝過程中會提示設定密碼什麼的,注意設定了不要忘了,安裝完成之後可以使用如下命令來檢查是否安裝成功:

sudo netstat -tap | grep mysql

通過上述命令檢查之後,如果看到有mysql 的socket處於 listen 狀態則表示安裝成功。

登陸mysql資料庫可以通過如下命令:

mysql -u root -p

-u 表示選擇登陸的使用者名稱, -p 表示登陸的使用者密碼,上面命令輸入之後會提示輸入密碼,此時輸入密碼就可以登入到mysql。

下面是一些命令列中操作的DEMO,可做今後參考:

mysqladmin -u root -p create blog
mysql  mysql -u root -p
show databases;
use blog;

CREATE TABLE IF NOT EXISTS `blog_table`(
   `blogId` BIGINT UNSIGNED,
   `url` VARCHAR(100) NOT NULL,
   `title` VARCHAR(1000) NOT NULL,
   `support` INT UNSIGNED,
   `pageView` INT UNSIGNED,
   PRIMARY KEY ( `blogId` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `tag_table`(
   `tagId` INT UNSIGNED AUTO_INCREMENT,
   `tagName` VARCHAR(100) NOT NULL,
   PRIMARY KEY ( `tagId` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `blog_tag_relation_table`(
   `relationId` INT UNSIGNED AUTO_INCREMENT,
   `blogId` BIGINT UNSIGNED,
   `tagId` INT UNSIGNED,
   PRIMARY KEY ( `relationId` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

show tables;
desc blog_table;
desc tag_table;
desc blog_tag_relation_table;

//change blogId int 2 bigint
alter table blog_table change blogId blogId BIGINT UNSIGNED;
//show data
select * from blog_table;
//delete data
delete from blog_table where blogId=201801021423;

INSERT INTO blog_table(blogId,url,title,support,pageView) 
VALUES(201801021423,'http://106.14.226.191:3000/blog/201607281658.html','[商業_法務] 1、公司一款新消費類電子產品如何快速全面的專利保護',0,0);

//too short
alter table blog_table change title title VARCHAR(1000) NOT NULL;


INSERT INTO tag_table(tagId,tagName) 
VALUES(0,'硬體_類比電路');

select * from blog_table;
select * from tag_table;
select * from blog_tag_relation_table;

delete from blog_table where blogId>0;
delete from tag_table where tagId>=0;
delete from blog_tag_relation_table where relationId >= 0;

select a.title , a.url, b.tagName from blog_table a, tag_table b, blog_tag_relation_table c WHERE a.blogId = c.blogId AND a.blogId = 201602021408 AND b.tagId = c.tagId;

select a.title , a.url, b.tagName from blog_table a, tag_table b, blog_tag_relation_table c WHERE a.blogId = c.blogId AND b.tagId = c.tagId ORDER BY b.tagId;

為了python操作mysql需要執行下面命令:

pip install MySQL-python

 
6.2 配置及測試資料庫

在settings.py中下面幾項是對資料庫的配置:

DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'pyserver',
    'USER': 'root',
    'PASSWORD': '123456',
    }
}

一旦在輸入了那些設定並儲存之後應當測試一下你的配置。 我們可以在mysite 專案目錄下執行python manage.py shell 來進行測試(沒有錯誤表示成功):

from django.db import connection
cursor = connection.cursor()

 
6.3 建立books app

mysite 專案檔案下輸入下面的命令來建立books app:

python manage.py startapp books

這個命令並沒有輸出什麼,它只在 mysite 的目錄裡建立了一個 books 目錄。 讓我們來看看這個目錄的內容:

(python_web) ➜  books tree
.
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│   └── __init__.py
├── models.py
├── tests.py
└── views.py

這個目錄包含了這個app的模型和檢視。

 
6.4 編寫模型

編輯models.py :

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)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()

Publisher 模組相當於SQL語句:

CREATE TABLE "books_publisher" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(30) NOT NULL,
    "address" varchar(50) NOT NULL,
    "city" varchar(60) NOT NULL,
    "state_province" varchar(30) NOT NULL,
    "country" varchar(50) NOT NULL,
    "website" varchar(200) NOT NULL
);

 
6.5 由模型自動生成建立表SQL

再次編輯settings.py,將下面列出選項加#註釋掉,並新增‘mysite.books’INSTALLED_APPS 的末尾:

INSTALLED_APPS = [
    #'django.contrib.admin',
    #'django.contrib.auth',
    #'django.contrib.contenttypes',
    #'django.contrib.sessions',
    #'django.contrib.messages',
    #'django.contrib.staticfiles',
    'books', 
]

MIDDLEWARE = [
    #'django.middleware.security.SecurityMiddleware',
    #'django.contrib.sessions.middleware.SessionMiddleware',
    #'django.middleware.common.CommonMiddleware',
    #'django.middleware.csrf.CsrfViewMiddleware',
    #'django.contrib.auth.middleware.AuthenticationMiddleware',
    #'django.contrib.messages.middleware.MessageMiddleware',
    #'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

你可能會執行python manage.py validate ,然後你會特別傷心的看到人家提示Unknown command: 'validate'Type 'manage.py help' for usage.,對吧?所以你要用如下這個命令:

python manage.py check

然後你還想生成sql語句,你就運行了python manage.py sqlall books,錯誤提示是Unknown command: 'sqlall'Type 'manage.py help' for usage.同樣如果你想提交sql語句到資料庫而執行syncdb,錯誤提示是Unknown command: 'syncdb'

Type 'manage.py help' for usage. 為什麼沒有這些命令,因為它們被淘汰了。所以你只需執行如下的命令:

python manage.py makemigrations books    #用來檢測資料庫變更和生成資料庫遷移檔案
python manage.py migrate     #用來遷移資料庫(直接到資料庫)
python manage.py sqlmigrate books 0001 # 用來把資料庫遷移檔案轉換成資料庫語言

 
6.6 基本資料訪問

一旦你建立了模型,Django自動為這些模型提供了高階的Python API。 執行 python manage.py shell 並輸入下面的內容試試看:

>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
...     city='Berkeley', state_province='CA', country='U.S.A.',
...     website='http://www.apress.com/')
>>> p1.save()
>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
...     city='Cambridge', state_province='MA', country='U.S.A.',
...     website='http://www.oreilly.com/')
>>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]

其他常用基本操作如下:

  • 建立:p1 = Publisher.objects.create(....)
  • 修改:p1.name = 'Apress Publishing'
  • 過濾:Publisher.objects.filter(country="U.S.A.", state_province="CA")
  • 單個:Publisher.objects.get(name="Apress")
  • 排序:Publisher.objects.order_by("state_province", "address")
  • 查詢:Publisher.objects.filter(country="U.S.A.").order_by("-name")
  • 陣列:Publisher.objects.order_by('name')[0] or [0:2]
  • 多個:Publisher.objects.filter(id=52).update(name='Apress Publishing')
  • 儲存:p.save()
  • 刪除:p.delete()

注意:上述操作除了刪除,每個操作之後都別忘了儲存!

 

7、站點管理

7.1 基本操作

編輯settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'books', 
]

MIDDLEWARE = [
    #'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    #'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    #'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

之後執行python manage.py migrate這一步將生成管理介面使用的額外資料庫表。 當你把'django.contrib.auth'加進INSTALLED_APPS後,第一次執行syncdb命令時, 系統會請你建立一個超級使用者。 如果你不這麼作,你需要執行python manage.py createsuperuser來另外建立一個admin的使用者帳號,否則你將不能登入admin(我這裡設定user:admin password:xxxxxx)

將admin訪問配置在URLconf(記住,在urls.py中)

from django.conf.urls import url
from django.contrib import admin
from django.conf.urls import include
#from mysite.views import hello
from mysite.views import hello, current_datetime, hours_ahead, template_test, template_test1, template_test2, template_test3, template_test4

admin.autodiscover()

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    ...
]

此時執行python manage.py runserver 0.0.0.0:8080,然後在瀏覽器中訪問: http://www.phage.cc:8080/admin/

注:NameError: name 'include' is not defined錯誤需要from django.conf.urls import include;
注:django nginx admin css丟失需要在settings.py中INSTALLED_APPS中加django.contrib.staticfiles;

 
7.2 管理工具簡介

管理介面的設計是針對非技術人員的,所以它應該是自我解釋的。 儘管如此,這裡簡單介紹一下它的基本特性:

1)登入頁面(使用者名稱密碼就是剛剛生成的admin,xxxxxxxxx)

你要使用你原來設定的超級使用者的使用者名稱和密碼。 如果無法登入,請執行python manage.py createsuperuser ,確保你已經建立了一個超級使用者。

2)一旦登入了,你將看到管理頁面:

這個頁面列出了管理工具中可編輯的所有資料型別。 現在,由於我們還沒有建立任何模組,所以這個列表只有寥寥數條類目: 它僅有兩個預設的管理-編輯模組:使用者組(Groups)和使用者(Users)。

3)點進去USER可以新增、修改、刪除等操作,非常方便:

 
7.3 將其他Models加入到Admin管理中

有一個關鍵步驟我們還沒做。 讓我們將自己的模組加入管理工具中,這樣我們就能夠通過這個漂亮的介面新增、修改和刪除資料庫中的物件了。 我們將繼續第五章中的book 例子。在其中,我們定義了三個模組: Publisher 、 Author 和 Book 。

編輯mysite/books/admin.py,然後輸入:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib import admin
from books.models import Publisher, Author, Book

admin.site.register(Publisher)
admin.site.register(Author)
admin.site.register(Book)

# Register your models here.

重啟伺服器,現在再去admin主頁,就會看到Publisher 、 Author 和 Book模組,這樣就能編輯這些模組了!

 
7.4 工作原理

當服務啟動時,Django從url.py 引導URLconf,然後執行admin.autodiscover() 語句。 這個函式遍歷INSTALLED_APPS配置,並且尋找相關的 admin.py檔案。 如果在指定的app目錄下找到admin.py,它就執行其中的程式碼。

books 應用程式目錄下的admin.py 檔案中,每次呼叫admin.site.register() 都將那個模組註冊到管理工具中。 管理工具只為那些明確註冊了的模組顯示一個編輯/修改的介面。

 
7.5 設定欄位可選

  • 可以為空:email = models.EmailField(**blank=True** )
  • 自定義欄位標籤:email = models.EmailField(blank=True, **verbose_name='e-mail'** )
  • 自定義列表:

      class Author(models.Model):
          first_name = models.CharField(max_length=30)
          last_name = models.CharField(max_length=40)
          email = models.EmailField(blank=True, verbose_name='e-mail')
    
          **def __unicode__(self):**
          **return u'%s %s' % (self.first_name, self.last_name)**

注:更多展示自定義可以參考[12]. The Django Book - 第六章 Django站點管理

 

8、表單

8.1 URL相關資訊

HttpRequest物件包含當前請求URL的一些資訊:

屬性/方法 說明 舉例
request.path 除域名以外的請求路徑,以正斜槓開頭 "/hello/"
request.get_host() 主機名(比如,通常所說的域名) "127.0.0.1:8000" or "www.example.com"
request.get_full_path() 請求路徑,可能包含查詢字串 "/hello/?print=true"
request.is_secure() 如果通過HTTPS訪問,則此方法返回True, 否則返回False True 或者 False

 
8.2 一個簡單的SEARCH表單

views.search()

def search(request):
    error = False
    if 'q' in request.GET:
    q = request.GET['q']
    if not q:
        error = True
    else:
        books = Book.objects.filter(title__icontains=q)
        return render_to_response('search_results.html',{'books': books, 'query': q})
    return render_to_response('search_form.html',{'error': error})

search_form.html

<html>
    <head>
    <title>Search</title>
    </head>
    <body>
    {% if error %}
        <p style="color: red;">Please submit a search term.</p>
    {% endif %}
    <form action="" method="get">
        <input type="text" name="q">
        <input type="submit" value="Search">
    </form>
    </body>
</html>

search_results.html

<p>You searched for: <strong>{{ query  }}</strong></p>

{% if books %}
    <p>Found {{ books|length  }} book{{ books|pluralize  }}.</p>
    <ul>
    {% for book in books %}
    <li>{{ book.title  }}</li>
    {% endfor %}
    </ul>
{% else %}
    <p>No books matched your search criteria.</p>
{% endif %}

更高階的用法我們在後面的DEMO中介紹!

 

9、實戰-實操一個github上1.6K star量的部落格系統

專案地址: https://github.com/zmrenwu/django-blog-tutorial
專案master: https://github.com/zmrenwu

 
9.1 將專案部署在我們買AliYun上

下載專案到本地:

cd ~/App/
git clone https://github.com/zmrenwu/django-blog-tutorial.git

建立並激活虛擬環境(一定要注意是python3):

virtualenv blogproject_env -p python3
source blogproject_env/bin/activate

安裝依賴並資料遷移(sqlite的):

pip install -r requirements.txt
python manage.py migrate

建立後臺管理員賬戶並啟動伺服器(我這裡其他埠被佔用,因此用8080):

python manage.py createsuperuser
python manage.py runserver 0.0.0.0:8080

由於我們不是在本地執行,因此還得在settings.py中新增: ALLOWED_HOSTS = [u'www.phage.cc'] 。此時,便可以訪問 http://www.phage.cc:8080/admin/ 對品類、文章、標籤、使用者等進行管理了:

訪問: http://www.phage.cc:8080/ 便可看到我們在後臺新增的 python-django 文章:

 
9.2 程式碼解析

首先看看所有支援的url入口配置檔案:/blog/urls.py

from django.conf.urls import url

from . import views

app_name = 'blog'
urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^post/(?P<pk>[0-9]+)/$', views.PostDetailView.as_view(), name='detail'),
    url(r'^archives/(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/$', views.ArchivesView.as_view(), name='archives'),
    url(r'^category/(?P<pk>[0-9]+)/$', views.CategoryView.as_view(), name='category'),
    url(r'^tag/(?P<pk>[0-9]+)/$', views.TagView.as_view(), name='tag'),
    # url(r'^search/$', views.search, name='search'),
]

其主頁呼叫views.IndexView.as_view()實現的,但是為什麼有第三個引數name='idnex'呢?我們看/blog/view.py的IndexView就明白了,這裡類IndexView繼承Django的Generic display views來實現的,看一下Generic display views中的ListViewd的用法就明白了。其他的各種入口則依此類推:

class IndexView(ListView):
...

class PostDetailView(DetailView):
...

class ArchivesView(ListView):
...

class CategoryView(ListView):
...

class TagView(ListView):
...

我們倒著分析各個入口的實現(倒著由淺入深),看第一個TagView的實現:

class TagView(ListView):
    model = Post
    template_name = 'blog/index.html'
    context_object_name = 'post_list'

    def get_queryset(self):
        tag = get_object_or_404(Tag, pk=self.kwargs.get('pk'))
        return super(TagView, self).get_queryset().filter(tags=tag)

TagView繼承ListView:重設定model和tempale_name為會導致 —— 通用檢視將查詢資料庫以獲取指定model(Post)的所有記錄,然後呈現位於/templates/blog/index.html的模板;而context_object_name重定義的意義在於 —— your own name for the list as a template variable;重寫get_queryset方法 —— 從資料庫中過濾出所有tag,將get_queryset方法新增到基於類的自定義檢視中,並指定order_by()。

這裡的get_object_or_404的功能在於如果找不到記錄,就引發Http404異常的快捷方式,見下面的例子

def book_detail_view(request, primary_key):
    try:
        book = Book.objects.get(pk=primary_key)
    except Book.DoesNotExist:
        raise Http404('Book does not exist')
    
    return render(request, 'catalog/book_detail.html', context={'book': book})

利用get_object_or_404來實現:

from django.shortcuts import get_object_or_404

def book_detail_view(request, primary_key):
    book = get_object_or_404(Book, pk=primary_key)
    return render(request, 'catalog/book_detail.html', context={'book': book})

接下來的CategoryView、ArchivesView和TagView一樣,我們重點看PostDetailView和IndexView:

class IndexView(ListView):
    model = Post
    template_name = 'blog/index.html'
    context_object_name = 'post_list'
    paginate_by = 10

    def get_context_data(self, **kwargs):
    ...
    
    def pagination_data(self, paginator, page, is_paginated):
    ...  

上面已經介紹:

  • model : 將 model 指定為 Post,告訴 Django 我要獲取的模型是 Post。
  • template_name : 指定這個檢視渲染的模板。
  • context_object_name : 指定獲取的模型列表資料儲存的變數名。這個變數會被傳遞給模板。

等效於:

blog/views.py

def index(request):
    post_list = Post.objects.all()
    return render(request, 'blog/index.html', context={'post_list': post_list})

而PostDetailView則繼承了DetailView,該模板用於從資料庫中取出一條記錄並渲染,其中model、template、context_object_name和ListView類似;這裡覆寫了get方法是為了閱讀量加1的運算,同時注意到用super繼承了原來的response並返回;覆寫 get_object 方法的目的是因為需要對 post 的 body 值進行渲染;覆寫 get_context_data 的目的是因為除了將 post 傳遞給模板外(DetailView 已經幫我們完成),還要把評論表單、post 下的評論列表傳遞給模板。

更詳細的操作大家可以從GIT上獲取: https://github.com/zmrenwu/django-blog-tutorial


: 完~
: 大家覺得不錯,可以點推薦給更多人~


[1]. 02、PI3安裝openCV開發環境做影象識別(詳細版)
[2]. 利用Django進行Web開發系列(一)
[3]. 部落格園python web關鍵詞搜尋
[4]. The Django Book
[5]. The Django Book - 第三章 檢視和URL配置
[6]. Built-in template tags and filters
[7]. The Django Book - 第五章 模型
[8]. Ubuntu下安裝MySQL及簡單操作
[9]. MySQL-python 1.2.5
[10]. 在Django中使用資料庫遇到的問題
[11]. Django manage.py Unknown command: 'syncdb'
[12]. The Django Book - 第六章 Django站點管理
[13]. Chapter 6: The Django Admin Site
[14]. The Django Book PDF
[15]. The Django Book - 第八章:高階檢視和URL配置
[16]. Django Tutorial Part 6: Generic list and detail views
[17]. 基於類的通用檢視:ListView 和 DetailView


@beautifulzzzz
智慧硬體、物聯網,熱愛技術,關注產品
部落格:http://blog.beautifulzzzz.com
園友交流群:414948975