django 菜鳥篇+進階篇
django自帶web server, 故django開發的專案可以獨立的執行,也可以安置在apache(+mod_python)下執行
django的官網手冊 http://www.djangobook.com/en/2.0/; 對應的中文翻譯版本 http://download.csdn.net/detail/xiarendeniao/4232144http://djangobook.py3k.cn/下面是菜鳥篇:
django處理請求的過程:
1. 進來的請求轉入/hello/.
2. Django 通過在ROOT_URLCONF 配置來決定根URLconf.
3. Django 在 URLconf 中的所有 URL 模式中,查詢第一個匹配/hello/的條目。
4. 如果找到匹配,將呼叫相應的檢視函式
5. 檢視函式返回一個HttpResponse
6. Django 轉換HttpResponse 為一個適合的HTTP response, 以 Web page 顯示出來
M ,資料存取部分,由django 資料庫層處理,本章要講述的內容。
V ,選擇顯示哪些資料要及怎樣顯示的部分,由檢視和模板處理。
C ,根據使用者輸入委派檢視的部分,由Django 框架通過按照URLconf 設定,對給定URL 呼叫合適的python 函式來自行處理。
一、檢視函式(views.py中的函式):第一個引數型別是HttpRequest物件,返回值是HttpResponse物件
二、URLconf(urls.py):繫結檢視函式和URL (urlpatterns只有一個空串時django顯示歡迎頁面)
(r'^time/plus/(\d{1,2})/$', hours_ahead),urls.py用圓括號從正則中提取資料;
def hours_ahead(request, offset):...,views.py檢視函式的第二個引數是從url中提取的字串
三、除錯,在檢視的任何位置插入一個assert False來觸發django的出錯頁
四、模板引擎
1.模板是一個文字,用於分離文件的表現形式和內容。模板定義了佔位符以及各種用於規範文件該如何顯示的各部分基本邏輯(模板標籤)。模板通常用於產生HTML,但是 Django 的模板也能產生任何基於文字格式的文件。
2.用兩個大括號括起來的文字(例如{{ person_name }} )稱為變數(variable) 。這意味著將按照給定的名字插入變數的值。
3.被大括號和百分號包圍的文字(例如 {% if ordered_warranty %} )是 模板標籤(template tag) 。標籤(tag)定義比較明確,即:僅通知模板系統完成某些工作的標籤。
4.filter 過濾器,它是一種最便捷的轉換變數輸出格式的方式。如這個例子中的{{ship_date|date:”F j, Y” }},我們將變數ship_date 傳遞給date 過濾器,同時指定引數”F j,Y”。date過濾器根據引數進行格式輸出。
5.模板使用
1>可以用原始的模板程式碼字串建立一個Template 物件,Django 同樣支援用指定模板檔案路徑的方式來建立Template 物件;
2>呼叫模板物件的render 方法,並且傳入一套變數context。它將返回一個基於模板的展現字串,模板中的變數和標籤會被context 值替換。
python manage.py shell 進入互動模式
>>> from django import template >>> t = template.Template('My name is {{ name }}.') >>> c = template.Context({'name': 'Adrian'}) >>> print t.render(c) My name is Adrian. >>> c = template.Context({'name': 'Fred'}) >>> print t.render(c) My name is Fred.
6.template.Context跟字典的結構類似;t.render(c)返回的是一個unicode物件,not 普通python字串
7.在 Django 模板中遍歷複雜資料結構的關鍵是句點字元(.);假設你要向模板傳遞一個Python 字典。 要通過字典鍵訪問該字典的值,可使用一個句點;同樣,也可以通過句點來訪問物件的屬性;點語法也可以用來引用物件的"方法",呼叫方法時並沒有使用圓括號而且也無法給該方法傳遞引數,你只能呼叫不需引數的方法;不允許使用負數列表索引,像 {{ items.-1 }} 這樣的模板變數將會引發`` TemplateSyntaxError``
8.get_template() 函式以模板名稱為引數,在檔案系統中找出模組的位置,開啟檔案並返回一個編譯好的Template物件.要定位某個模板檔案在你的系統裡的位置, get_template()方法會自動為你連線已經設定的TEMPLATE_DIRS 目錄和你傳入該法的模板名稱引數
9.比get_template()+Context+HttpResponse更方便的方式:render_to_response() 的第一個引數必須是要使用的模板名稱。如果要給定第二個引數,那麼該引數必須是為該模板建立Context 時所使用的字典。如果不提供第二個引數,render_to_response() 使用一個空字典
10.{% include "xx.html" %} 把一個網頁嵌入到另一箇中,適用於頭部和底部的共有部分,對網頁中間部分使用不方便
11.{% extents "xx.html" %} {% block yy %} {% endblock %} 模板繼承就是先構造一個基礎框架模板,而後在其子模板中對它所包含站點公用部分和定義塊進行過載
12.如果你需要訪問父模板中的塊的內容,使用{{ block.super }}這個標籤吧,這一個魔法變數將會表現出父模板中的內容。如果只想在上級程式碼塊基礎上新增內容,而不是全部過載,該變數就顯得非常有用了
補充:
1.setting.py中INSTALLED_APPS 告訴 Django 專案哪些 app 處於啟用狀態,可以啟用對應app下面的模型
2.在app的目錄下新增management/commands目錄,django就會自動的為commands目錄下的每個模組自動註冊manage.py命令,可以用python manage.py command_name來呼叫,具體命令的檔案編寫格式如下:
#polls/management/commands/closepoll.py
from django.core.management.base import BaseCommand, CommandError
from example.polls.models import Poll
class Command(BaseCommand):
args = '<poll_id poll_id ...>'
help = 'Closes the specified poll for voting'
def handle(self, *args, **options):
for poll_id in args:
try:
poll = Poll.objects.get(pk=int(poll_id))
except Poll.DoesNotExist:
raise CommandError('Poll "%s" does not exist' % poll_id)
poll.opened = False
poll.save()
self.stdout.write('Successfully closed poll "%s"\n' % poll_id)
3.對session的使用:request.session['test'] = 'test',設定;request.session[‘test’]或request.session.get('test',None),獲取;session是一個類似於字典的結構;HttpRequest物件中除session以外,其他屬性都應該當做只讀屬性用這個專案做完以後推出進階篇.....
1.django模板的html自動轉義
在django裡預設情況下,每一個模板自動轉意每一個變數標籤的輸出。 尤其是這五個字元。
< 被轉意為 <
> 被轉意為 >
' (single quote) 被轉意為 '
" (double quote) 被轉意為 "
& 被轉意為 &
另外,我強調一下這個行為預設是開啟的。 如果你正在使用django的模板系統,那麼你是被保護的。
關閉自動轉義
對於單獨變數:
This will not be escaped:{{ data|safe }}
對於模板,autoescape 標籤有兩個引數on和off 有時,你可能想阻止一部分自動轉意,對另一部分自動轉意Auto-escaping is on bydefault.Hello{{ name }} {% autoescape off %}#預設轉義 This will not be auto-escaped:{{ data }}. Northis:{{ other_data }} {% autoescape on %}#關閉預設轉義 Auto-escaping applies again:{{ name }} {% endautoescape %} {% endautoescape %}
DATABASES = {
'default': {
'NAME': 'app_data',
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres_user',
'PASSWORD': 's3krit'
},
'users': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
}
$ ./manage.py syncdb --database=users
syncdb會把所有的model都同步到users資料庫,所以不一定是我們想要的(可以用router控制入庫到user資料庫的app)
syncdb也會檢索對應app的<appname>/sql/<modelname>.sql(modelname小寫),並會在正常syncdb後執行這些sql語句
[[email protected] boosencms]$ vpython manage.py sqlall app_user | vpython manage.py dbshell --database=users
這個會把名為app_user的app下面的model都同步到資料庫users(這個是setting.py的DATABASES中定義的資料庫key,不是實際資料庫的名字)中去sqlall會把models.py中定義的model都轉換成sql語句、同時會把<appname>/sql/<modelname>.sql中的sql語句也打印出來
sqlcustom只打印<appname>/sql/<modelname>.sql中的sql語句
<appname>/sql/<modelname>.sql中的sql語句主要用來對models.py不能做到的東西做補充,比如在models中定義的資料表無法對column設定資料庫層面的default value,models.py中定義的default只是django層面的default value,如果不用django程式寫庫就無法用到預設值;再比如v1.5之前的組合索引(1.5開始可以在meta calss中這是組合索引index_together)
WARNING:在models.py中設定column屬性時max_length,null,db_index,premary_key,unique,unique_together這些都是可以通過syncdb等命令作用到資料庫層面的,defalt則不行
操作資料表的時候在正常語法中間加上using(dbname)
Author.objects.using('default').all()
3.資料庫路由 database router
1>setting.py的DATABASES加入資料庫配置就不說了
2>建立myapp/myrouter.py檔案,並寫入如下程式碼(每個函式的具體含義可看官網說明)
class MyAppRouter(object):
"""A router to control all database operations on models in
the myapp application"""
def db_for_read(self, model, **hints):
"Point all operations on myapp models to 'other'"
if model._meta.app_label == 'myapp':
return 'other'
return None
def db_for_write(self, model, **hints):
"Point all operations on myapp models to 'other'"
if model._meta.app_label == 'myapp':
return 'other'
return None
def allow_relation(self, obj1, obj2, **hints):
"Allow any relation if a model in myapp is involved"
if obj1._meta.app_label == 'myapp' or obj2._meta.app_label == 'myapp':
return True
return None
def allow_syncdb(self, db, model):
"Make sure the myapp app only appears on the 'other' db"
if db == 'other':
return model._meta.app_label == 'myapp'
elif model._meta.app_label == 'myapp':
return False
return None
3>修改setting.py,加入下面這行DATABASE_ROUTERS = ['myapp.myrouter.MyAppRouter']
4>為每個model的類新增app標籤app_labelclass Info(models.Model):
id = models.IntegerField(primary_key = True)
name = models.CharField("站", max_length = 100, db_index = True)
info = models.CharField("板", max_length = 100)
class Meta:
db_table = 't_info'
app_label = 'content'
5>同步資料庫,不制定database的時候用的預設default資料庫,由路由控制需要在其他資料庫建立的資料表會被忽略vpython manage.py syncdb --database=default
vpython manage.py syncdb --database=content
....
6>資料表建立完成就可以正式工作了,具體哪些model跟哪些資料庫對應,由router決定
附加:討論一個問題,如果model的app_label不存在會怎樣?
a.要想通過syncdb建立資料表就必須保證app_label跟實際存在及setting.py中註冊的app名稱(INSTALLED_APPS)相對應,可以不等於當前所在app的名字,但必須是存在和註冊的app名,否則syncdb的時候會把該model忽略掉(syncdb --database=dbName,django會根據router找跟dbName對應的app,並安裝,但是app如果沒有註冊則失敗),結果所有資料庫中都找不到該model對應的資料表結構
b.而如果所有model的app_label都對應著實際存在的app名,加上資料庫路由是由app_label和database_name(setting.py中DATABASES的key)來作控制的,那麼就出現了一個問題:每個app_label必須跟同一個資料庫對應
c.這個也不是什麼大問題,一般都滿足需求!萬一有變態需求呢?好吧,事實上我們可以定義不存在和沒註冊的app名作為app_label,然後在路由器上根據該app_label來控制其訪問哪個資料庫,這樣我們付出的代價就是a的問題不得不手動建立資料表了....還有一種處理辦法是在router根據表名制定更細的規則,只是這樣不便於修改(一個installed_app對應一個app_label,一個app_label對應一個database最好控制了)
4.關於自增和聯合索引
class SiteBbsInfo(models.Model):
id = models.AutoField(primary_key=True)
v1 = models.CharField("v1", max_length = 100, db_index = True)
v2 = models.CharField("v2", max_length = 100)
class Meta:
unique_together = ("v1", "v2")
db_table = 't_v1_v2'
app_label = 'content'
5.django的DateTimeField列型別指定了auto_now=True結果生成的資料表還是沒有預設當前更新時間的性質,google結果只說到如何在django的模型層實現這個功能(下面是兩個方案),但是這樣在資料庫層面還是沒有預設當前更新時間的性質,如果用其他程式往該資料表寫資料或者更新資料就會出現問題!!!這個問題如何從資料庫層面解決呢???!!!(見2標紅<---2012.7.17 12:23)
import datetime
class User(models.Model):
created = models.DateTimeField(editable=False)
modified = models.DateTimeField()
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.id:
self.created = datetime.datetime.today()
self.modified = datetime.datetime.today()
super(User, self).save(*args, **kwargs)
created_datetime = models.DateTimeField(default=datetime.datetime.now)
6.model一些語法
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
等價於SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
等價於SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'
filter的上述用法就不贅述了
對於__get還有其他類似的東東
__gt 大於
__gte 大於等於
__lt 小於
__lte 小於等於
__contains 包含
__startswith 以XX開頭
distinct的用法:
Entry.objects.order_by('pub_date').distinct('pub_date')
Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')
values()的用法:
>>> Blog.objects.values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}],
>>> Blog.objects.values('id', 'name')
[{'id': 1, 'name': 'Beatles Blog'}]
count()的用法:
Entry.objects.count()
Entry.objects.filter(headline__contains='Lennon').count()
Avg的用法:無記錄時34.35這個值是None
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
sum()的用法:
from django.db.models import Sum
ModelName.objects.aggregate(Sum('field_name'))
7.django的QuerySet支援以數字索引取值,以及[num1:num2],但是不支援負數索引(不同於list)
>>> snapshots = WomFollowerSnapshot.objects.filter(uid = 2124561663, time__lte = datetime.now()).order_by("time")[0:2]
>>> type(snapshots)
<class 'django.db.models.query.QuerySet'>
>>> snapshots[0]
<WomFollowerSnapshot: WomFollowerSnapshot object>
>>> snapshots[1]
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/db/models/query.py", line 207, in __getitem__
return list(qs)[0]
IndexError: list index out of range
>>> snapshots[-1]
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/db/models/query.py", line 174, in __getitem__
"Negative indexing is not supported."
AssertionError: Negative indexing is not supported.
>>> snapshots[0:2]
[<WomFollowerSnapshot: WomFollowerSnapshot object>]
>>> len(snapshots)
1
>>> len(snapshots[0:2])
1
8.select xxx from xxx where x in (y,yy,yyy);
result = Test.objects.filter(test_id__in=test_ids)
9.獲取表名
XX._meta.db_table
10.如何指定查詢某些列?如果全部列都構造出來可能會太佔記憶體、網路,也可能有延時的問題
# This will defer all fields except the headline.
Entry.objects.only("body", "rating").only("headline")
參考:http://stackoverflow.com/questions/7071352/django-objects-values-select-only-some-fields
https://docs.djangoproject.com/en/dev/ref/models/querysets/#only
用only返回的物件modelObj,django只從資料查詢和構造用only指定的列;如果你用該modelObj取其他列,則django會立即去資料庫獲取(warning:此時獲取的可能是已經被其他程序或者執行緒修改過的資料哦,要小心)
用only指定某些列獲取值並修改了這些列,save()的時候會把相應的列更新到資料庫,其他列不變(不修改、不覆蓋);如果修改了only指定以外的列,save()的時候會把only制定的和這些被修改的列都更新到資料庫
更新還可以用這種方式實現:
>>> WomKeyWord.objects.filter(wordId=1).update(wordStr=u"中國人")
1L
views.py
from django.core.urlresolvers import reverse
def redirect(request):
return HttpResponseRedirect(reverse('mysite.polls.views.detail',args=(1,)))
“很容易明白,第一個引數就直接添入要使用的view方法,第二個args裡邊順序填入方法的引數,(extra_context也從這裡傳入)然後剩下的就全部交給django去完成拉。於是我們就可以放心的修改url.py裡的url配置,不必再擔心有什麼地方沒修改網站出錯啦”
說白了,reverse()就是屌絲views的逆襲,從views裡面的方法反解url,該函式返回的字串就是一個url串。用它的好處是修改urls.py裡面的url可以不用修改views檔案中的HttpResponseRedirect()引數。
12.djang多程序和多執行緒的問題
django單程序多執行緒:每個執行緒的資料庫操作會用不同的連線,如果某程序有60個執行緒,每個執行緒都有對同一個資料庫的操作,那麼該程序會有60個對該資料庫的連線(小心mysql最大連線數達到上限)。就測試結果來看,資料庫操作結束後(執行緒還沒退出)幾分鐘,連線會自動斷開(說個跟本話題無關的,tmpThread.setDaemon(True)考慮清楚再用,今天執行緒拋異常就跟這個有關Exception in thread Thread-6 (most likely raised during interpreter shutdown))
django多程序:如果在某個django的程序裡面用multiprocessing建立新的程序,則子程序會繼承父程序的資料庫連線socket,那麼父子程序同時做資料庫操作時會出錯(資料庫socket連線會丟擲異常“資料庫已不在”/"查詢過程中出錯")
如果在某個django的程序裡面用os.popen()或者subprocess.Popen()建立新的django程序(比如啟動一個django的command),則,子程序雖然會繼承父程序的資料庫連線socket,但也會自己構建屬於自己的資料庫連線(跟從bash下啟動程序一樣嘛,可以預料到的),不會有上述問題(順便說一句,subprocess才是正道,什麼os.popen()、popen2.*已不是官方倡導的多程序用法;multiprocessing倒不至於被人遺棄,因為它是類似於多執行緒threading模組的多程序模組,和前面的那些多程序模組適用性不一樣)
13.日誌
#settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters':{
'simple':{
'format':'%(threadName)s %(asctime)s %(levelname)s [%(filename)s:%(lineno)d]%(message)s'
},
},
'handlers': {
'log_to_stdout':{
'level':'INFO',
'class':'logging.StreamHandler',
'formatter':'simple',
},
},
'loggers': {
'main':{
'handlers':['log_to_stdout'],
'level':'INFO',
'propagate':True,
}
}
}
#.../commands/mycommand.py
logger = logging.getLogger('main')
logger.debug("entered %s" % __name__)
14.關於django自動新增的primary key(id, AutoField),如果指定了其他列primary_key = True則不會自動新增這個id列;每個model必須有一個primary_key(這個真是彆扭啊)
15.關於django事務操作
兩種官方支援的方式:
1>在MIDDLEWARE_CLASSES中配置中介軟體TransactionMiddleware,這種基於http服務的request和response決定何時提交(具體見文件,我沒細看),對於deamon process不靠譜兒
2>用裝飾器@transaction.commit_on_success、@transaction.commit_manually
還用一種方式大家應該想到了,直接編寫事務的sql語句,通過執行raw sql來實現事務,我要這個!
sql = '''
BEGIN;
INSERT INTO wom_repost_path (retweetedId,uid,provider,parentStatusId,parentUid,childStatusId,childUid) values (1,1,1,1,1,3,1);
UPDATE wom_repost_report set repostCount = repostCount + 1 where weiboId = 1 and provider = 1 and repostUid = 1;
COMMIT;
'''
print sql
from django.db import connections, transaction
cursor = connections['wom'].cursor()
cursor.execute(sql)
#transaction.commit_manually(using = 'wom')
16.或者,OR語法
select * from xx where a = 1 or a = 2;
在django中的實現:
rts = XX.objects.filter(a = 1) | XX.objects.filter(a = 2)
或者
from django.db.models import Q
rts = XX.objects.filter(Q(a = 1) | Q(a = 2))
或者| 並且& 都可以用
17.QuerySet分析(2013.3.6 14:56)
>>> rts = WomAccount.objects.only('id').filter(provider = 1) #no db operate
>>> type(rts) #no db operate
<class 'django.db.models.query.QuerySet'>
>>> fRts = rts.exclude(id__lt=1000) #no db operate
>>> len(fRts)
#execute SQL:SELECT `wom_account`.`id` FROM `wom_account` WHERE (`wom_account`.`provider` = 1 AND NOT (`wom_account`.`id` < 1000 ))
24176
>>>
>>>
>>> rts = WomAccount.objects.only('id').filter(provider = 1) #no db operate
>>> len(rts)
#execute SQL: SELECT `wom_account`.`id` FROM `wom_account` WHERE `wom_account`.`provider` = 1
25175
>>> type(rts) #no db operate
<class 'django.db.models.query.QuerySet'>
>>> fRts = rts.exclude(id__lt=1000) #no db operate
>>> len(fRts)
#execute SQL: SELECT `wom_account`.`id` FROM `wom_account` WHERE (`wom_account`.`provider` = 1 AND NOT (`wom_account`.`id` < 1000 ))
24176
>>>
>>> rts = WomAccount.objects.only('id').filter(provider = 1)[0:10000] #no db operate
>>> type(rts) #no db operate
<class 'django.db.models.query.QuerySet'>
>>> fRts = rts.exclude(id__lt=1000) #先有了limit語法,再做過濾是不可行的!
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/db/models/query.py", line 628, in exclude
return self._filter_or_exclude(True, *args, **kwargs)
File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/db/models/query.py", line 633, in _filter_or_exclude
"Cannot filter a query once a slice has been taken."
AssertionError: Cannot filter a query once a slice has been taken.
我在專案中選用的是timelog模組,veasy_install django-timelog
19.為apache安裝mod_python模組:
鳥人用的第一種方法,這樣mod_python貌似(反正我沒找到方法)只能使用系統預設路徑下的python,真是很不爽呀...第二種方式編譯mod_python時可指定使用我自己的python,可惜失敗了
sudo yum install mod_python -y
--------------------------------------------------------------------------------------------------
wget http://archive.apache.org/dist/httpd/modpython/mod_python-2.7.10.tgz
tar zvxf mod_python-2.7.10.tgz
sudo yum install httpd-devel -y #安裝apxs(apache編譯和安裝擴充套件模組的工具,http://httpd.apache.org/docs/2.2/programs/apxs.html)
cd mod_python-2.7.10
./configure --with-apxs=/usr/sbin/apxs --with-python=/home/dongsong/venv/bin/
make #報錯!!還是yum install mod_python比較方便!
make install
20.部署Django程式到執行mod_python的apache環境:
1>告訴apache要載入mod_python模組(mod_python 是一個 Apache 模組,它將對 Python 程式語言的支援整合到 Web 伺服器中。與用傳統的 CGI 方法執行 Python 指令碼相比,這種方法要快得多),在httpd.conf或/var/etc/httpd/conf.d/python.conf中新增:
LoadModule python_module modules/mod_python.so
2>告訴apache講我們的Django程式關聯到那個URL,在httpd.conf或者python.conf中新增:(如需要使用virtualenv下的python環境,可參考http://blog.csdn.net/xiarendeniao/article/details/6774520item47)<Location "/apps/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE boosencms.settings
PythonPath "['/var/www/html/apps/','/var/www/html/apps/boosencms/'] + sys.path"
PythonDebug On
</Location>
<LocationMatch "\.(png|gif|jpg|mov|mp3|avi|wav)$">
SetHandler None
</LocationMatch>
Location是url匹配,<Location "/apps/">表示只要url請求符合/apps/就用mod_python來處理,同一臺機器上的不同virtualhost只要url符合這個規則其url請求都會交由mod_python來處理,就算某個virtualhost的root目錄下沒有apps目錄也是這樣
那麼就會有個問題:如果我們在同一臺機器上起了多個virtual host,一個指定8088埠、root路徑是/var/www/html/,另一個指定9000埠、root路徑是/var/www/app/,那麼"http://localhost:8088/apps/"和"http://localhost:9000/apps/"請求是等效的,都交由mod_python處理去了。如果我們實際要做的是隻對9000埠來的請求交由/var/www/app/apps下面的django程式處理呢?答案是Direcctor!
Directory是本地目錄匹配,上述配置改成<Directory "/var/www/app/apps/">就可以把針對”/var/www/app/apps“的訪問交給mod_python來處理!這時候請求"http://localhost:8088/apps/"則要看對應root目錄/var/www/html/下面是否有apps了,沒有就是非法訪問!
所以在
3>告訴apache不對媒體檔案使用mod_python,在httpd.conf或python.conf中新增:
<LocationMatch "\.(png|gif|jpg|mov|mp3|avi|wav)$">
SetHandler None
</LocationMatch>
4>重啟httpd21.部署Django程式到共享Web宿主環境(httpd.conf不可修改;使用執行FastCGI程式的Web伺服器衍生程序):
1>建立.htaccess檔案,並放到Django程式所在目錄cat .htaccess
AddHandler fastcgi-script .fcgi
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ testproject.fcgi/$1 [QSA,L]
2>建立Python指令碼(與前述RewriteRule配置的檔名相同)告訴Apache我們Django專案的不同設定並執行FastCGI程式。cat testproject.fcgi
#!/usr/bin/python
import sys, os
sys.path.insert(0, "/home/joelennon/python")
os.environ['DJANGO_SETTINGS_MODULE'] = "testproject.settings"
from django.core.servers.fastcgi import runfastcgi
runfastcgi(method="threaded", daemonize="false")
3>chmod 755 testproject.fcgi 或者 用FTP客戶端修改檔案屬性(如不能通過shell訪問伺服器)
4>每次更改程式程式碼之後都需要更改該fcgi檔案的時間戳,告訴apache應用程式已經更新,然後它會重啟Django程式。
22.部署Django程式到執行mod_wsgi的apache環境:
[[email protected] conf.d]# cat wsgi.conf
LoadModule wsgi_module modules/mod_wsgi.so
WSGIScriptAlias / "/var/www/html/apps/wsgi.py"
<Directory "/var/www/html/apps">
Order Deny,Allow
Allow from all
</Directory>
[[email protected] boosencms]$ cat wsgi.py
# -*- coding: utf-8 -*-
import os
import sys
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
os.environ['PYTHON_EGG_CACHE'] = '/tmp/.python-eggs'
current_dir = os.path.dirname(__file__)
if current_dir not in sys.path: sys.path.append(current_dir)
parent_dir = '/home/dongsong/boosencms/src/'
if parent_dir not in sys.path: sys.path.append(parent_dir)
activate_this = '/home/dongsong/venv/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
23.model.save()貌似會觸發兩次sql操作,一個SELECT (1) AS `a` FROM a_table where provimaryId = xx; 一個update a_table set ...; 這個如何規避?
如果model的例項設定了有效的主鍵值,save會先查資料庫該主鍵對應記錄是否存在,是就update,否就insert
如果model的例項沒有設定有效的主鍵值,save會直接insert
save的引數force_insert就是直接insert;save的引數force_update是直接根據主鍵update
record.save(force_update=True)的前提是record的primary_key列有值
>>> rt.save(force_update=True)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/db/models/base.py", line 463, in save
self.save_base(using=using, force_insert=force_insert, force_update=force_update)
File "/home/dongsong/venv/lib/python2.6/site-packages/Django-1.4-py2.6.egg/django/db/models/base.py", line 545, in save_base
raise ValueError("Cannot force an update in save() with no primary key.")
ValueError: Cannot force an update in save() with no primary key.
24.settings.py中設定DEBUG = True,同時logging的級別設定為logging.DEBUG則每次資料庫操作都會列印到日誌裡面(sql+args+time)
25.用django給其他應用提供http資料介面,當收到post請求的時候會返回403錯誤,原因為請求方沒有提供csrf_token,解決辦法是對views.py中對應的方法新增@csrf_exempt裝飾(記得要 from django.views.decorators.csrf import csrf_exempt)