新建一個Django項目css
# 新建一個django項目 $ django-admin startproject mysite # 新建一個app $ django-admin startapp blog
項目的結構html
├── blog │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py └── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py
# mysite/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', 'markdown2' ]
$ python3 manage.py runserver $ python manage.py collectstatic
通常在urls.py中配置url,在models.py中配置model,在views.py中配置View。python
Function views
mysql
1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
nginx
1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
git
1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
# blog/urls.py from django.conf.urls import url from blog import views urlpatterns = [ url(r'^blog/$', views.IndexView.as_view(), name='index'), url(r'^blog/article/(?P<article_id>\d+)$', views.ArticleDetailView.as_view(), name='detail'), url(r'^blog/category/(?P<cate_id>\d+)$', views.CategoryView.as_view(), name='category'), url(r'^blog/tag/(?P<tag_id>\d+)$', views.TagView.as_view(), name='tag'), ]
使用(?P<>d+)的形式捕獲值給<>中得參數,好比(?P<article_id>d+),當訪問/blog/article/3時,將會將3捕獲給article_id,這個值會傳到views.ArticleDetailView。github
# mysite/urls.py from django.conf.urls import url, include from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'', include('blog.urls', namespace='blog', app_name='blog')) ]
其中namespace參數爲咱們指定了命名空間,這說明這個urls.py中的url是blog app下的,這樣即便不一樣的app下有相同url也不會衝突了。sql
假設用戶要訪問某篇文章,它會自動解析 blog:detail 這個視圖函數對應的 url,而且把 article.pk(文章的主鍵)傳遞給detail視圖函數,details
就是咱們在blog/urls.py
中指定的name
。數據庫
<a href="{% url 'blog:detail' article.pk %}">{{ article.title }}</a>
若是要訪問某個目錄django
<a href="{% url 'blog:category' category.pk %}">{{ category.name }}</a>
django.db.models
是orm框架的基礎,在blog/models.py
中新建Article
, Category
, Tag
三個model。
class Article(models.Model): STATUS_CHOICES = ( ('d', 'Draft'), ('p', 'Published'), ) # 仍然使用默認的 objects 做爲 manager 的名字 objects = ArticleManager() title = models.CharField('標題', max_length=70) body = models.TextField('正文') created_time = models.DateTimeField('建立時間', auto_now_add=True) last_modified_time = models.DateTimeField('修改時間', auto_now=True) status = models.CharField('文章狀態', max_length=1, choices=STATUS_CHOICES) # blank和null要同時設置爲null,詳情參考官方文檔 abstract = models.CharField('摘要', max_length=54, blank=True, null=True, help_text="可選,如若爲空將摘取正文的前54個字符") views = models.PositiveIntegerField('瀏覽量', default=0) likes = models.PositiveIntegerField('點贊數', default=0) topped = models.BooleanField('置頂', default=False) category = models.ForeignKey('Category', verbose_name='分類', null=True, on_delete=models.SET_NULL) tags = models.ManyToManyField('Tag', verbose_name='標籤集合', blank=True) def __str__(self): return self.title class Meta: ordering = ['-last_modified_time'] # 新增 get_absolute_url 方法 def get_absolute_url(self): # 這裏 reverse 解析 blog:detail 視圖函數對應的 url return reverse('blog:detail', kwargs={'article_id': self.pk})
Django給咱們提供了不少有用的字段,好比上面提到的CharFiled
, TestField
, DateTimeFiled
等等,詳情請參考官方文檔。
Django中的一對可能是在一中進行設置,這裏對應於文章的分類,ForeignKey即數據庫中的外鍵。on_delete=models.SET_NULL表示刪除某個分類(category)後該分類下全部的Article的外鍵設爲null(空),因此咱們同時設置了null=True。多對多就不一樣,兩邊都要進行配置。詳情請參考官方文檔。
class Category(models.Model): name = models.CharField('類名', max_length=20) created_time = models.DateTimeField('建立時間', auto_now_add=True) last_modified_time = models.DateTimeField('修改時間', auto_now=True) def __str__(self): return self.name
class Tag(models.Model): name = models.CharField('標籤名', max_length=20) created_time = models.DateTimeField('建立時間', auto_now_add=True) last_modified_time = models.DateTimeField('修改時間', auto_now=True) def __str__(self): return self.name
評論功能的實現
class BlogComment(models.Model): user_name = models.CharField('評論者名字', max_length=100) user_email = models.EmailField('評論者郵箱', max_length=255) body = models.TextField('評論內容') created_time = models.DateTimeField('評論發表時間', auto_now_add=True) article = models.ForeignKey('Article', verbose_name='評論所屬文章', on_delete=models.CASCADE) def __str__(self): return self.body[:20]
class ArticleManage(models.Manager): """ 繼承自默認的 Manager ,爲其添加一個自定義的 archive 方法 """ def archive(self): date_list = Article.objects.datetimes('created_time', 'month', order='DESC') # 獲取到降序排列的精確到月份且已去重的文章發表時間列表 # 並把列表轉爲一個字典,字典的鍵爲年份,值爲該年份下對應的月份列表 date_dict = defaultdict(list) for d in date_list: date_dict[d.year].append(d.month) # 模板不支持defaultdict,所以咱們把它轉換成一個二級列表,因爲字典轉換後無序,所以從新降序排序 return sorted(date_dict.items(), reverse=True)
咱們首先要在project_name/settings.py
中配置好相應的配置文件
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'DB_NAME', 'USER': 'DB_USER', 'PASSWORD': 'DB_PASSWORD', 'HOST': 'localhost', # Or an IP Address that your DB is hosted on 'PORT': '3306', } }
定義完畢後,咱們執行下面的命令就在數據庫中能夠生成相應的數據表:
$ python manage.py makemigrations $ python manage.py migrate
參考Mozila的教程以及結合官方文檔。
下面要使用markdown2,因此在INSTALLED_APP裏面要添加markdown2,不過這個mardown解析很是的很差,而且弄完還要去下載相應的markdown的css文件,有個專門的網站。
from blog.models import Article, Tag, Category from django.views.generic import ListView, DetailView import markdown2 class IndexView(ListView): # template_name屬性用於指定使用哪一個模板進行渲染 template_name = "blog/index.html" # context_object_name屬性用於給上下文變量取名(在模板中使用該名字) context_object_name = "article_list" def get_queryset(self): article_list = Article.objects.filter(status='p') for article in article_list: article.body = markdown2.markdown(article.body, ) return article_list def get_context_data(self, **kwargs): kwargs['category_list'] = Category.objects.all().order_by('name') # 調用 archive 方法,把獲取的時間列表插入到 context 上下文中以便在模板中渲染 kwargs['date_archive'] = Article.objects.archive() kwargs['tag_list'] = Tag.objects.all().order_by('name') return super(IndexView, self).get_context_data(**kwargs)
上面由於咱們要進行markdown處理,因此從新自定義了get_queryset
,若是不要進行相應的處理,直接制定model
就好了,get_context_data
能夠添加一些額外的字段,好比之後咱們要在首頁的側邊欄顯示目錄和標籤,因此這裏要添加一個category_list
和tag_list
。
class ArticleDetailView(DetailView): model = Article template_name = "blog/detail.html" context_object_name = "article" # pk_url_kwarg會自動和model中相應的主鍵對應,aritlce_id就是下面配置的URLCONF pk_url_kwarg = 'article_id' # 爲了讓文章以markdown形式展示,咱們重寫get_object()方法 def get_object(self): obj = super(ArticleDetailView, self).get_object() obj.body = markdown2.markdown(obj.body) return obj # 新增 form 到 context def get_context_data(self, **kwargs): kwargs['comment_list'] = self.object.blogcomment_set.all() kwargs['form'] = BlogCommentForm() return super(ArticleDetailView, self).get_context_data(**kwargs)
class CategoryView(ListView): template_name = "blog/index.html" context_object_name = "article_list" def get_queryset(self): # url裏的cate_id傳遞給CategoryView,傳遞的參數在kwargs屬性中獲取 article_list = Article.objects.filter(category=self.kwargs['cate_id'],status='p') for article in article_list: article.body = markdown2.markdown(article.body, ) return article_list def get_context_data(self, **kwargs): # 增長一個category_list,用於在頁面顯示全部分類,按照名字排序 kwargs['category_list'] = Category.objects.all().order_by('name') return super(CategoryView, self).get_context_data(**kwargs)
class TagView(ListView): template_name = "blog/index.html" context_object_name = "article_list" def get_queryset(self): """ 根據指定的標籤獲取該標籤下的所有文章 """ article_list = Article.objects.filter(tags=self.kwargs['tag_id'], status='p') for article in article_list: article.body = markdown2.markdown(article.body, extras=['fenced-code-blocks'], ) return article_list def get_context_data(self, **kwargs): kwargs['tag_list'] = Tag.objects.all().order_by('name') return super(TagView, self).get_context_data(**kwargs)
from django.views.generic.edit import FormView class CommentPostView(FormView): form_class = BlogCommentForm template_name = 'blog/detail.html' def form_valid(self, form): target_article = get_object_or_404(Article, pk=self.kwargs['article_id']) # 調用ModelForm的save方法保存評論,設置commit=False則先不保存到數據庫, # 而是返回生成的comment實例,直到真正調用save方法時才保存到數據庫。 comment = form.save(commit=False) # 把評論和文章關聯 comment.article = target_article comment.save() # 評論生成成功,重定向到被評論的文章頁面,get_absolute_url 請看下面的講解。 self.success_url = target_article.get_absolute_url() return HttpResponseRedirect(self.success_url) def form_invalid(self, form): target_article = get_object_or_404(Article, pk=self.kwargs['article_id']) # 不保存評論,回到原來提交評論的文章詳情頁面 return render(self.request, 'blog/detail.html', { 'form': form, 'article': target_article, 'comment_list': target_article.blogcomment_set.all(), })
{% for %}
循環標籤,{% if %}
判斷標籤. {{ variable }}
是一些很是經常使用的標籤
在模板文件中咱們能夠這樣使用,views.py中已經指定了context_object_name = "article_list"
,而且已經在get_queryset()
中進行了markdown處理
{% for article in article_list %} {{article.title}}
一般都會設置一個通用的父模板:
{% extends "base_generic.html" %} {% block content %} ... {% endblock %}
好像要這麼這麼設置:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'blog/templates')] , 'APP_DIRS': True, ... ]
因爲源代碼丟失,具體狀況記得不太清晰,靜態文件路徑要設置好,若是js文件加載異常,多是加載順序的問題。
base_generic.html大概就是下面這樣的格式:
<!DOCTYPE html> {% load staticfiles %} <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Myblog</title> <link rel="stylesheet" href="{% static 'lib/css/bootstrap.min.css' %}"> <link rel="stylesheet" href="{% static 'blog/css/style.css' %}"> <link rel="stylesheet" href="{% static 'blog/css/pygments/github.css' %}"> </head> ...
下面這樣設置貌似有點問題:
# mysite/settings.py STATIC_URL = '/static/' STATICFILES = os.path.join(BASE_DIR, 'blog/static')
具體參考官方文檔
使用uwsgi+nginx
/etc/nginx/sites-available/mysite.conf,blog是app名字,static文件放在了下面,建議直接放在mysite下面,template也是同樣:
server { listen 80; location /static/ { alias /home/omrsf/mysite/blog/static/; } location / { uwsgi_pass 127.0.0.1:8001; include /etc/nginx/uwsgi_params; } }
uwsgi -i uwsgi.ini
來啓動uwsgi進程,結合nohup &
:
[uwsgi] socket = 127.0.0.1:8001 chdir=/home/ormsf/mysite/ wsgi-file = mysite/wsgi.py processes = 2 threads = 4 chmod-socket = 664
目前文章是直接在admin.py中註冊model,而後去admin後臺發佈的,能夠作成api接口,作一個在線的編輯器。增長基本的用戶認證功能。
null 是針對數據庫而言,若是 null=True, 表示數據庫的該字段能夠爲空。
blank 是針對表單的,若是 blank=True,表示你的表單填寫該字段的時候能夠不填,好比 admin 界面下增長 model 一條記錄的時候。直觀的看到就是該字段不是粗體。
優先採用render。
model有一個get_absolute_url,它能夠與reverse結合起來。