Django 版本為1.9以上
Django框架結構:
對django框架架構和request/response處理流程的分析
Blog搭建參考:
用Django搭建個人博客
Django 搭建簡易博客教程
代碼:
https://github.com/threegirl2014/blog
命令行常用命令:
python manage.py rumserver xx.xx.xx.xx:yyyy
運行Django Project唆迁,需要啟動服務器肆氓。默認情況下不用指定IP地址和端口號,默認為本地IP地址+8000端口號即:127.0.0.1:8000畦粮。
也可單獨指定端口號粪牲,如8080胀屿。
如果要監(jiān)聽所有外網(wǎng)IP(即Django Project與運行此Project的機器IP使用同一IP)帕胆,使用0.0.0.0轴捎。這樣可以在同一網(wǎng)絡的另外機器上與之建立連接召耘。
一些操作不需要重新啟動服務器百炬,而另一些,比如文件添加污它,需要手動重啟剖踊。django-admin startproject project_name
創(chuàng)建一個項目庶弃。一個項目可以包含多個應用。python manage.py startapp app_name
創(chuàng)建一個應用德澈。一個應用可以用于多個項目歇攻。python manage.py migrate
創(chuàng)建數(shù)據(jù)庫和表。對于Sqlite梆造,事先不需要創(chuàng)建任何東西缴守。對于MySQL或PostgreSQL,需要提前創(chuàng)建一個空的數(shù)據(jù)庫(與Project同名)镇辉。python manage.py makemigrations
如果對Models做了更改屡穗,比如增刪表中的項,比如新增一個app(需要在setting.py文件中的INSTALLED_APPS表中增加app名)忽肛,運行該命令可以產(chǎn)生對應的遷移命令村砂,接著運行以上第4條命令,就可以將遷移命令執(zhí)行屹逛。
在老版本中础废,并沒有4和5這兩條命令,數(shù)據(jù)庫的遷移要復雜的多罕模。python manage.py createsuperuser
登陸project的admin后臺時需要提前創(chuàng)建超級用戶色迂。python manage.py shell
和普通的python shell環(huán)境相比,此命令導入了setting.py中的設置手销。
如果在IDE(如Eclipse)中集成了開發(fā)環(huán)境,那么點擊右鍵彈出菜單中有與以上命令行等效的選項图张。
Model中的__str__()
和__unicode__()
稍加了解后就會知道__str__()
是用于Python3锋拖,__unicode__()
是用于Python2。
Python 2 had two string types: Unicode strings and non-Unicode strings.
Python 3 has one string type: Unicode strings.
由于以上區(qū)別祸轮,在Python3中兽埃,使用__str__()
直接返回Model中的字段就可以了。
但是在Python2中适袜,兩者的返回類型是不同的:
def __str__(self):
return self.title.encode('utf-8')
def __unicode__(self):
return self.title
按規(guī)矩柄错,在Python2中使用__unicode__()
就不會出現(xiàn)什么問題了。
但是如果選擇__str__()
的話苦酱,平常情況也沒啥問題售貌,因為它本質上是override了基類django.db.models.Model中的同名函數(shù)。
不過使用中文時疫萤,又會遇到老生常談的encode和decode問題颂跨。
當模型中有外鍵時(如Blog模型中使用taggit.manager.TaggableManager類型作為tag外鍵時,ManytoMany),刪除模型記錄時(某一篇Blog)扯饶,就會報錯:
DjangoUnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128). You passed in <TaggedItem: [Bad Unicode data]> (<class 'taggit.models.TaggedItem'>)
錯誤報告的大意就是傳入的字符恒削,無法decode池颈。
我猜測是在程序中調用了__str__()
,傳入的utf-8類型的字符程序無法正確decode钓丰,從而導致了錯誤躯砰。(雖然我找了大半天都沒找到在哪調用的,網(wǎng)上也沒有相關解答携丁。琢歇。。/(ㄒoㄒ)/~~)
結論就是则北,在Python2中矿微,使用__unicode__()
就對了。
(20160925補充)在Django文檔中也有介紹:
__str__
還是__unicode__
?
對于Python 3來說尚揣,這很簡單涌矢,只需使用__str__()
。
對于Python 2來說快骗,你應該定義__unicode__()
方法并返回unicode
值娜庇。
Django 模型具有一個默認的__str__()
方法,它會調用__unicode__()
并將結果轉換為UTF-8 字節(jié)字符串方篮。
這意味著unicode(p)
將返回一個Unicode 字符串名秀,而str(p)
將返回一個字節(jié)字符串,其字符以UTF-8編碼藕溅。
Python 的行為則相反:對象
的__unicode__
方法調用__str__
方法并將結果理解為ASCII 字節(jié)字符串匕得。
這個不同點可能會產(chǎn)生困惑。
defaultdict相關
時間設置
#pub_date = models.DateTimeField('date published', auto_now_add=True)
#if set auto_now=True or auto_now_add=True, the time variable is read-only.
#default=timezone.now(), can auto set the time and also give the choice to change it
#to support this function, we should set USE_TZ=False
pub_date = models.DateTimeField('date published', default=timezone.now())
last_edit_date = models.DateTimeField('last edited', auto_now=True)
auto_now=True是每次修改都會更新時間巾表,是“最后一次修改的時間”汁掠。 auto_now_add=True是自動添加時間,是“創(chuàng)建的時間”集币。二者設置之后考阱,DateTimeField就變成只讀模式。
若要可以自動設置為創(chuàng)建時間鞠苟,還能夠在之后進行修改乞榨,則使用default=timezone.now(),前提是在setting.py中設置USE_TZ=False,防止沖突報錯当娱。
報錯local variable 'xxx' referenced before assignment
categorys = Category.objects.all()
def archive(request,name=''):
args = dict()
args['data'] = []
blogs = Blog.objects.exclude(title__in=exclude_blog)
if name != '':
categorys_filtered = categorys.filter(short_name=name)
else:
categorys_filtered = categorys
for category in categorys_filtered:
bloglist = get_sorted_bloglist(blogs,category)
if len(bloglist) > 0:#to make sure the category have related blogs
args['data'].append((category,bloglist))
args['categorys'] = categorys
return render(request, 'css3two_blog/archive.html', args)
如果沒有新建變量categories_filtered吃既,那么就會報此錯誤。
即如下所示情況下跨细,categorys將會被認為是函數(shù)內的變量态秧,而不是global變量:
if name != '':
categorys = categorys.filter(short_name=name)
轉義
參考:django的轉義總結:escape,autoescape扼鞋,safe申鱼,mark_safe
何謂轉義愤诱?就是把html語言的關鍵字過濾掉。例如捐友,<div>就是html的關鍵字淫半,如果要在html頁面上呈現(xiàn)<div>,其源代碼就必須是<div> PS:轉義其實就是把HTML代碼給轉換成HTML實體了匣砖!
也就是說科吭,如果我們要返回一個HTML格式的文本,一定要將轉義開關設置為關閉猴鲫,否則類似<div>這種格式就無法正確返回对人。
常見的幾種方法:
- filter
@register.filter(is_safe=True),設置為safe拂共,不用自動轉義牺弄。 - template
使用{% autoescape off %} ...{% endautoescape %} 即可關閉自動轉義。 - mark_safe
from django.utils.safestring import mark_safe
return mark_safe(str)
標記為safe宜狐,不用自動轉義势告。
MEDIA_ROOT和MEDIA_URL,以及類似的STATIC抚恒、TEMPLATE
MEDIA_ROOT表示路徑咱台,MEDIA_URL表示目錄。
需要在setting.py中設置:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
同時俭驮,為了能夠獲取url回溺,還需要在urls.py中設置:
urlpatterns = [
url(r'^admin/', admin.site.urls),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
表示,如果遇到了MEDIA_URL混萝,要到MEDIA_ROOT路徑下去尋找馅而。
STATIC也是同樣的道理。
STATIC_URL = '/static/'
在DEBUG=True時譬圣,Django會到Project中的各個app中尋找對應的目錄。例如在Aproject中有Bapp雄坪,那么Django會到Bapp中尋找static目錄下是否有Bapp目錄厘熟,也就是說目錄為:./Aproject/Bapp/static/Bapp/xxx。
在DEBUG=False時维哈,有另外的處理方式绳姨。
TEMPLATE則需要在setting.py中的TEMPALTES列表中的’DIR'中加上templates所在的目錄,Django會到各個app下尋找阔挠。
字段Field
普通的有CharField飘庄,DateTimeField,TextField购撼。
有點特殊的見下跪削。
models.SlugField
Slug 是一個新聞術語(通常叫做短標題)谴仙。一個slug只能包含字母、數(shù)字碾盐、下劃線或者是連字符晃跺,通常用來作為短標簽。通常它們是用來放在URL里的毫玖。
URL中不能有中文掀虎,還有一些特殊字符,如果一篇Blog需要靠title來生成URL付枫,則需要用到Slug烹玉。
用法如下,其中unidecode將一個Unicode編碼的對象音譯成一個ASCII對象(在實際中若沒有對應的ASCII碼阐滩,則需要將原始Unicode碼對應的字符進行音譯二打,然后再轉化為ASCII碼,對應關系如:“你好”和“nihao”):
from unidecode import unidecode
self.slug = slugify(unidecode(self.title))
taggit.managers.TaggableManager
用于Blog的標簽叶眉。
需要到setting.py中的INSTALLED_APPS增加'taggit’址儒。
models.FileField
如果有文件相關操作,就會用到models.FileField衅疙。models.ImageField繼承自它莲趣。
FileField.upload_to是一個路徑,它將附加到MEDIA_ROOT后面來確定url屬性的值饱溢,也就是說喧伞,它實際上是MEDIA_ROOT下的一個子路徑。數(shù)據(jù)庫中存儲該值绩郎,而實際上的文件本體存儲在該路徑下潘鲫。
除直接給出路徑外,還可以設置成一個可調用對象如函數(shù)肋杖,這個可調用對象必須有兩個參數(shù):FileField所在的模型實例溉仑,filename(帶有前向/)。
如:
def get_upload_md_name(obj,filename):
if obj.pub_date:
year = obj.pub_date.year
else:
year = datetime.now().year
upload_to = mdfile_upload_dir % (year, obj.slug + '.markdown')
return upload_to
class Blog(models.Model):
md_file = models.FileField(upload_to=get_upload_md_name,blank=True)
若需要使用url屬性状植,以上述的md_file為例浊竟,則是object.md_file.url。
FieldFile.
save
(name, content, save=True)津畸,其中content應該是django.core.files.File的一個實例振定,而不是Python內建File對象。save參數(shù)表示關聯(lián)的文件被修改時是否保存肉拓,默認True后频。
self.md_file.save(self.slug + '.markdown', ContentFile(self.body.encode('utf-8')), save=False)
ModelForm
model表示Form和Model的關聯(lián)。
widgets表示admin界面顯示效果暖途。
exclude表示不顯示哪些參數(shù)卑惜,fields表示顯示哪些參數(shù)膏执。二者必須有其一。
class BlogAdminForm(forms.ModelForm):
class Meta:
model = Blog
widgets = {
'body' : Textarea(attrs={'cols':100, 'rows':100}),
}
exclude = ()
最后残揉,在Model對應的Admin類中進行form的關聯(lián)胧后。
class BlogAdmin(admin.ModelAdmin):
form = BlogAdminForm
保存操作
model需要自定義保存,可實現(xiàn)如下函數(shù):
def save(self, *args, **kwargs):
do_something()
super(Blog,self).save(*args,**kwargs)
do_something()
同時抱环,在model相關的admin中也有保存函數(shù):
class BlogAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.save()
print obj.slug, "save successfully"
obj即為該model的實例對象壳快,form就是上述提到的BlogAdminForm,change表示類型镇草。
分頁
分頁是一個很常見的功能眶痰。可以手工實現(xiàn)梯啤,Django也提供了更方便的方法竖伯。
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def home(request,page='1'):
raw_blogs = Blog.objects.filter(status='p')
paginator = Paginator(raw_blogs,5)
page = int(page)
try:
blog_list = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
blog_list = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
blog_list = paginator.page(paginator.num_pages)
...
首先獲得原始blogs列表。根據(jù)此列表因宇,設定每頁最多顯示五條七婴,生成一個Paginator對象。page是傳入對象察滑,表示在請求哪一頁的blogs打厘。根據(jù)page的輸入返回結果。
markdown
markdown編輯器會將寫好的markdown文本贺辰,轉換為html文本户盯,然后在瀏覽器上我們就可以看到非常漂亮的結果。
我使用python中的markdown庫來完成編輯器的作用饲化。Using Markdown as a Python Library莽鸭。
見下方的代碼,mark_safe表示返回的不需要轉義的字符串吃靠。
markdown.markdown(text [, **kwargs])
硫眨。text為傳入值,必須是Unicode類型巢块。extensions參數(shù)是一系列擴展礁阁,fenced_code是識別代碼用的;codehilite是代碼高亮夕冲,具體使用哪種css需要在template中寫明。safe_mode見上述轉義章節(jié)裂逐。enable_attributes默認為True歹鱼,在safe_mode為True時,默認為False卜高,即將attributes的轉換打開或關閉弥姻,具體什么是attributes未找到南片,存疑。還有其他一些參數(shù)庭敦。
templatetag
在Django的Template中可以使用過濾器來對內容進行過濾疼进。內置過濾器參考
也可以自定義。自定義模板標簽和過濾器
自定義的templatetag必須包含在某個app中秧廉,在這個app中需要建立一個templatetags目錄伞广,在目錄中需要有一個__init__.py
文件來使得該目錄可以作為Python的包。
在template文件中使用該過濾器時疼电,需要使用{% load xxx %}來導入嚼锄,其中xxx為templatetags目錄下的某個模塊名字。
register是template.Library()的一個實例蔽豺,所有的標簽和過濾器都是在其中進行注冊的区丑。
custom_markdown(value, ...)就是自定義的過濾器函數(shù),當然在未注冊前它只是一個普通的函數(shù)修陡。其中value表示輸入的變量沧侥,后面還可以設定有其他參數(shù)。
為了能夠使用它魄鸦,需要在register中將其注冊為過濾器宴杀。
使用裝飾器方法@register.filter(),filter的意思就是過濾器号杏。如果filter中對name參數(shù)進行了設置婴氮,那么Django就是用name值來作為過濾器的名字;如果沒有盾致,則使用函數(shù)的名字來作為過濾器主经。is_safe參數(shù)詳見上述轉義章節(jié)。由于返回的是經(jīng)過markdown處理后的html庭惜,所以此處不轉義罩驻。
另一個裝飾器@stringfilter表示該模板過濾器只希望用一個字符串來作為第一個參數(shù),那么在被傳入過濾器函數(shù)前护赊,將會把value值轉化為字符串值惠遏。
import markdown
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter(is_safe=True)
@stringfilter
def custom_markdown(value):
# print type(value)
return mark_safe(markdown.markdown(value,
extensions = ['markdown.extensions.fenced_code', 'markdown.extensions.codehilite'],
safe_mode=True,
enable_attributes=False))
RSS
RSS需要設置好返回的item的值具體是model中的什么。詳情可直接搜索獲得骏啰。
發(fā)送郵件需要指定SMTP主機和發(fā)送端口节吮,在setting.py中使用EMAIL_HOST和EMAIL_PORT來給二者賦值。給EMAIL_HOST_USER和EMAIL_HOST_PASSOWRD賦值用來驗證SMTP主機判耕。如果需要使用加密鏈接透绩,則需要給EMAIL_USE_TSL或EMAIL_USE_SSL賦值。
send_mail
send_mail
(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None,connection=None, html_message=None)
subject是郵件的標題;message是郵件的正文帚豪;from_email是發(fā)送者郵箱地址碳竟,若賦值為None,則使用在setting.py中設置的DEFAULT_FROM_EMAIL來賦值狸臣;recipient_list是接收者的郵箱地址列表莹桅。
需要注意的是from_email設置的郵箱地址,必須和SMTP主機相匹配烛亦,否則無法使用诈泼。
以上是開發(fā)者手動發(fā)送郵件。有些情況下此洲,Django有自動發(fā)送郵件的場景厂汗。比如在DEBUG=False時,無法直接看到錯誤報告呜师,Django會為ADMINS設置中的用戶發(fā)送郵件娶桦。
所以,為了發(fā)送郵件汁汗,除了上述提到的SMTP配置之外衷畦,還需要在ADMINS元組中添加接收者的相關信息(格式是(name,email)),設置SERVER_EMAIL來確定發(fā)送者的郵件地址知牌。
如果有啟用BrokenEmailLinksMiddleware祈争,那么就需要設置MANAGERS,它的格式與ADMINS相同角寸,用來接收死鏈報告菩混。
form
在Template中使用<form action="xx" method="yy">...</form>來構建表單。
action屬性指定的URL用來指出將表單數(shù)據(jù)發(fā)送到何處扁藕,若為空則是表單所在頁面來處理沮峡。method指定是GET還是POST方法(只能是二者其一),通常會更改系統(tǒng)狀態(tài)的請求需要使用POST方法亿柑。
Django 會處理表單工作中的三個顯著不同的部分:
準備數(shù)據(jù)邢疙、重構數(shù)據(jù),以便下一步提交望薄。
為數(shù)據(jù)創(chuàng)建HTML 表單
接收并處理客戶端提交的表單和數(shù)據(jù)
以下是一個用來發(fā)送郵件的例子疟游,由于包含一些敏感信息,使用POST方法痕支。
forms.py如下:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(label='Subject:', max_length=100)
message = forms.CharField(label = 'Message:', widget=forms.Textarea)
email = forms.EmailField(label='E-mail:')
name = forms.CharField(label='Name:',max_length=50,required=False)
views.py如下:
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
sender = form.cleaned_data['email']
message = form.cleaned_data['message']
recipients = [settings.DEFAULT_FROM_EMAIL,sender]
send_mail(subject=subject, message=message, recipient_list=recipients,from_email=None)
return HttpResponseRedirect('/thanks/')
else:
form = ContactForm()
return render(request, 'css3two_blog/contact.html', {'form' : form, 'categorys' : categorys})
首先在forms.py中定義一個Form類颁虐。
如果訪問的視圖是GET請求,那么Form類將創(chuàng)建一個空的表單實例然后放置到要渲染的模板的上下文中卧须。
如果是POST請求另绩,則使用request.POST對新創(chuàng)建的Form實例進行填充瞬痘,這叫做“綁定數(shù)據(jù)到表單”。
然后調用is_valid()方法板熊,它會為所有的表單數(shù)據(jù)進行驗證。如果為False察绷,那么就會將現(xiàn)有數(shù)據(jù)進行返回干签。如果為True,那么驗證后的表單數(shù)據(jù)將會被放入cleaned_data屬性中拆撼。
Admin Action
自定義django的admin后臺action
Admin 界面上的Action
method_splitter
Django ------ 高級 view 和 URLconf 配置 額外URLconf參數(shù)技術應用到自己的工程
locals()函數(shù)
Django:locals()小技巧
django-using-locals
Celery庫
暫無