python 全棧開發,Day80(部落格系統分析,部落格主頁展示)
一、部落格系統分析
資料庫的構建
首先,我們分析一個部落格系統的功能:
- 一個部落格可以有多個標籤(多對多)
- 一個部落格可以有多條評論(一對多)
- 一個部落格只可以有一個類別(多對一)
接下來,我們分析關係的屬性:
部落格:標題,作者,內容,釋出時間,分類(外來鍵),標籤(多對多)等
標籤:標籤名
類別:分類名
評論:作者,部落格(外來鍵),郵箱,內容,釋出時間等。
有8張表,表關係如下:
圖中箭頭開始的英文字母表示關聯欄位
按照箭頭方向查詢,表示正向查詢,否則為反向查詢
新建專案cnblog,應用名為blog
修改models.py,必須匯入模組
from django.contrib.auth.models import AbstractUser
因為有一個表userinfo需要繼承它。django自帶的auth_user表也是繼承AbstractUser
表模型如下:
from django.db import models # Create your models here. from django.db import models # Create your models here. from django.contrib.auth.models importView CodeAbstractUser class UserInfo(AbstractUser): """ 使用者資訊 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="avatars/default.png") create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE) def __str__(self): return self.username class Blog(models.Model): """ 部落格資訊 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='個人部落格標題', max_length=64) site_name = models.CharField(verbose_name='站點名稱', max_length=64) theme = models.CharField(verbose_name='部落格主題', max_length=32) def __str__(self): return self.title class Category(models.Model): """ 博主個人文章分類表 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='分類標題', max_length=32) blog = models.ForeignKey(verbose_name='所屬部落格', to='Blog', to_field='nid', on_delete=models.CASCADE) def __str__(self): return self.title class Tag(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='標籤名稱', max_length=32) blog = models.ForeignKey(verbose_name='所屬部落格', to='Blog', to_field='nid', on_delete=models.CASCADE) def __str__(self): return self.title class Article(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=50, verbose_name='文章標題') desc = models.CharField(max_length=255, verbose_name='文章描述') create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True) content = models.TextField() comment_count = models.IntegerField(default=0) up_count = models.IntegerField(default=0) down_count = models.IntegerField(default=0) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE) tags = models.ManyToManyField( to="Tag", #through引數可以指定用作中介的中間模型 through='Article2Tag', ) def __str__(self): return self.title class Article2Tag(models.Model): nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE) tag = models.ForeignKey(verbose_name='標籤', to="Tag", to_field='nid', on_delete=models.CASCADE) class Meta: #組合唯一約束 unique_together = [ ('article', 'tag'), ] def __str__(self): v = self.article.title + "---" + self.tag.title return v class ArticleUpDown(models.Model): """ 點贊表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey('UserInfo', null=True, on_delete=models.CASCADE) article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE) is_up = models.BooleanField(default=True) class Meta: # 組合唯一約束 unique_together = [ ('article', 'user'), ] class Comment(models.Model): """ 評論表 """ nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='評論文章', to='Article', to_field='nid', on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='評論者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) content = models.CharField(verbose_name='評論內容', max_length=255) create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True) parent_comment = models.ForeignKey('Comment', null=True, on_delete=models.CASCADE) def __str__(self): return self.content
相關引數解釋:
through 表示orm,不要建立關係表。而制定一個表,這個表自己來建立!為什麼呢?因為orm建立多對多關係表時,只有3個欄位。那麼需要關係表需要擴充欄位時,就不行了!
所以設定through 欄位,是為了方便新增額外的欄位。使用through,那麼這個表,稱之為中間模型。
在Comment模型表中,有一個欄位parent_comment。它關聯的是本身表中的主鍵nid,它是一個父級評論id,用來展示誰評論誰!to='Comment'等同於to='self'
修改settings.py配置檔案,覆蓋預設的User模型。最後一行新增,否則執行建立表命令時會報錯!
AUTH_USER_MODEL="blog.UserInfo"
Django允許你通過修改setting.py檔案中的 AUTH_USER_MODEL 設定覆蓋預設的User模型,其值引用一個自定義的模型。
bolg,是應用名。
使用下面2個命令,生成表
python manage.py makemigrations
python manage.py migrate
生成的表如下:
注意:django自帶的auth_user表沒有了,取而代之的是blog_userinfo表。
查看錶欄位
它在auth_user表的基礎上,增加了5個欄位!
登入驗證
新增超級使用者,密碼必須是8位或者以上!
python manage.py createsuperuser
效果如下:
再建立2個使用者,zhang和lisi。用來測試多個使用者登入!
lisi使用者
修改urls.py,增加路徑
from blog import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), path('login/', views.login), ]View Code
修改views.py,增加檢視函式
from django.shortcuts import render,HttpResponse,redirect from django.contrib import auth from blog.models import Article,UserInfo # Create your views here. def login(request): if request.method=="POST": user=request.POST.get("user") pwd=request.POST.get("pwd") # 使用者驗證成功,返回user物件,否則返回None user=auth.authenticate(username=user,password=pwd) if user: # 登入,註冊session # 全域性變數 request.user=當前登陸物件(session中) auth.login(request,user) return redirect("/index/") return render(request,"login.html") def index(request): return render(request,"index.html")View Code
修改login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> {% csrf_token %} <lable>使用者名稱</lable><input type="text" name="user"> <lable>密碼</lable><input type="password" name="pwd"> <input type="submit"> </form> </body> </html>View Code
修改index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>INDEX</h3> </body> </html>View Code
訪問登入頁面
跳轉到首頁
首頁修飾
修改settings.py,設定靜態檔案目錄。最後一行新增:
STATICFILES_DIRS=[ os.path.join(BASE_DIR,"static") ]View Code
下載bootsrtap,將壓縮的包內容放到satic目錄
img和js是新建的
目錄如下:
修改urls.py,增加路徑
urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('logout/', views.logout), path('', views.index), ]View Code
修改views.py,增加註銷
def logout(request): # 登出 auth.logout(request) return redirect("/index/")View Code
修改index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/js/jquery.js"></script> <script src="/static/bootstrap/js/bootstrap.js"></script> <style> .desc{ text-align: justify; } .info{ margin-top: 10px; } </style> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">部落格園</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">新聞 <span class="sr-only">(current)</span></a></li> <li><a href="#">博問</a></li> </ul> <ul class="nav navbar-nav navbar-right"> {% if request.user.username %} <li><a href="#"><span class="glyphicon glyphicon-user"></span> {{ request.user.username }}</a> </li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">修改密碼</a></li> <li><a href="#">個人資訊</a></li> <li><a href="/logout/">登出</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> {% else %} <li><a href="/login/">登陸</a></li> <li><a href="#">註冊</a></li> {% endif %} </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-danger"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-6"> 222 </div> <div class="col-md-3"> <div class="panel panel-warning"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-info"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> </div> </div> </body> </html>View Code
重新登入,效果如下:
新增內容
使用django自帶的admin後臺,快速新增資料。
訪問後臺頁面,這裡必須是超級使用者。上面已經建立了超級使用者xiao
預設是空的
操作表,必須要註冊
修改views.py同級目錄下的admin.py
註冊所有的模型表
from django.contrib import admin # Register your models here. from blog import models admin.site.register(models.UserInfo) admin.site.register(models.Blog) admin.site.register(models.Category) admin.site.register(models.Tag) admin.site.register(models.Article2Tag) admin.site.register(models.Article) admin.site.register(models.ArticleUpDown) admin.site.register(models.Comment)View Code
再次重新整理頁面
點選Articles,新增文章
從http://www.py3study.com 上面copy一篇部落格
注意內容都是html程式碼
新增分類
新增站點
儲存
點選儲存
新增成功
多新增幾篇部落格
首頁文章展示
修改settings.py,更改時區
TIME_ZONE = 'Asia/Shanghai'
修改index檢視函式
def index(request): article_list=Article.objects.all() return render(request,"index.html",{"article_list":article_list})View Code
修改index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/js/jquery.js"></script> <script src="/static/bootstrap/js/bootstrap.js"></script> <style> .desc{ text-align: justify; } .info{ margin-top: 10px; } </style> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">部落格園</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">新聞 <span class="sr-only">(current)</span></a></li> <li><a href="#">博問</a></li> </ul> <ul class="nav navbar-nav navbar-right"> {% if request.user.username %} <li><a href="#"><span class="glyphicon glyphicon-user"></span> {{ request.user.username }}</a> </li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">修改密碼</a></li> <li><a href="#">個人資訊</a></li> <li><a href="/logout/">登出</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> {% else %} <li><a href="/login/">登陸</a></li> <li><a href="#">註冊</a></li> {% endif %} </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-danger"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-6"> <div class="article_list"> {% for article in article_list %} <div class="article_item"> <h5><a href="">{{ article.title }}</a></h5> <div> <span class="media-left"><a href=""><img width="60" height="60" src="https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1758343206,1224786249&fm=58&bpow=1024&bpoh=1536" alt=""></a></span> <span class="media-right small desc "> {{ article.desc }} </span> </div> <div class="info small"> <span><a href="">{{ article.user.username }}</a></span> 釋出於 <span>{{ article.create_time|date:'Y-m-d H:i' }}</span> <span class="glyphicon glyphicon-comment"></span><a href="">評論({{ article.comment_count }})</a> <span class="glyphicon glyphicon-thumbs-up"></span><a href="">點贊({{ article.up_count }})</a> </div> </div> <hr> {% endfor %} </div> </div> <div class="col-md-3"> <div class="panel panel-warning"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-info"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> </div> </div> </body> </html>View Code
訪問首頁,效果如下:
個人站點展示
比如部落格園範圍個人站點是域名加使用者名稱,就可以了,比如:
https://www.cnblogs.com/xiao987334176
如果使用者不存在,會提示404頁面
增加404頁面
修改urls.py,增加路徑。注意匯入re_path
from django.contrib import admin from django.urls import path,re_path from blog import views urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('logout/', views.logout), path('', views.index), re_path('(?P<username>\w+)', views.homesite), ]View Code
修改views.py,增加檢視函式
def homesite(request,username): """ 查詢 :param request: :param username: :return: """ # 查詢當前站點的使用者物件 user=UserInfo.objects.filter(username=username).first() if not user: return render(request,"not_found.html") return render(request,"homesite.html",{"user":user})View Code
在templates目錄建立檔案not_found.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> </head> <body> <div class="container" style="margin-top: 100px"> <div class="text-center"> <a href="http://www.cnblogs.com/"><img src="/static/img/logo_small.gif" alt="cnblogs"></a> <p><b>404.</b> 抱歉! 您訪問的資源不存在!</p> <p class="d">請確認您輸入的網址是否正確,如果問題持續存在,請發郵件至 [email protected] 與 <strong style="font-size: 28px">xiao</strong> 聯絡。</p> <p><a href="/">返回網站首頁</a></p> </div> </div> </body> </html>View Code
建立homesite.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>{{ user }}</h3> </body> </html>View Code
訪問網頁:http://127.0.0.1:8000/xiao
訪問一個不存在的使用者
http://127.0.0.1:8000/123
頁面提示404
展示文章列表
修改homesite檢視函式
def homesite(request,username): """ 查詢 :param request: :param username: :return: """ # 查詢當前站點的使用者物件 user=UserInfo.objects.filter(username=username).first() if not user: return render(request,"not_found.html") # 查詢當前站點物件 blog=user.blog # 查詢當前使用者釋出的所有文章 article_list=Article.objects.filter(user__username=username) return render(request,"homesite.html",{"blog":blog,"article_list":article_list})View Code
修改homesite.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ margin: 0; padding: 0; } .header{ width: 100%; height: 59px; background-color: #369; } .header .title{ line-height: 59px; color: white; font-weight: lighter; margin-left: 20px; font-size: 18px; } .left_region{ margin-top: 10px; } .info{ margin-top: 10px; color: darkgray; } </style> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/js/jquery.js"></script> <script src="/static/bootstrap/js/bootstrap.js"></script> </head> <body> <div class="header"> <p class="title">{{ blog.title }}</p> </div> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="left_region"> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-info"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div> </div> <div class="col-md-9"> <div class="article_list"> {% for article in article_list %} <div class="article_item clearfix"> <h5><a href="">{{ article.title }}</a></h5> <div> <span class="small desc "> {{ article.desc }} </span> </div> <div class="info small pull-right"> 釋出於 <span>{{ article.create_time|date:'Y-m-d H:i' }}</span> <span class="glyphicon glyphicon-comment"></span><a href="">評論({{ article.comment_count }})</a> <span class="glyphicon glyphicon-thumbs-up"></span><a href="">點贊({{ article.up_count }})</a> </div> </div> <hr> {% endfor %} </div> </div> </div> </div> </body> </html>View Code
訪問xiao的個人站點,效果如下:
這個時候發現,左上角沒有顯示部落格標題。那是因為使用