去病率兵十万深入敌后痛击匈奴,经此一役,胡虏元气甚伤,单于伊治斜弃河西平原而迁王庭至漠北。汉乃设酒泉、张掖、敦煌、武威河西四郡。众将凯旋回师,帝召大将军卫青、骠骑将军霍去病、飞将军李广、苏建、公孙敖、公孙贺等将议于宣室殿。众将皆以需涉荒漠为困而欲弃之。然武帝废胡之心愈坚,怒曰:汝皆以为不可,而胡虏亦曰寡人不可为之,然朕必举全国之力誓击之,永绝胡虏之患!

      遂点精骑十万,以大将军五万兵出定襄,骠骑将军五万出代郡,辎重步卒五十万,挥师远征漠北!

 

一、兵马未动,辎重先行。——表结构的设计

(参照抽屉新热榜)

在论坛中文章包含的属性(数据库中的字段)有标题、简介、板块、内容、作者、发布时间、最后一次修改时间、优先级(数字权重)。

model里的字段类型

auto_now和auto_now_add区别:

auto_now:记录字段每次被修改的时间

auto_now_add:记录字段被创建时的时间

若字段被设置为这两种类型的一种,则字段不可再更改。

models.py

#! _*_ coding:utf-8 _*_

from
__future__ import unicode_literals from django.db import models from django.contrib.auth.models import User #倒入django-admin自带的User表 from django.core.exceptions import ValidationError #页面提示错误信息 import datetime # Create your models here. #文章 class Article(models.Model): title = models.CharField(max_length=255) #标题 brief = models.CharField(blank=True,null=True,max_length=255)#文章简介,可以为空 category = models.ForeignKey('Category')#板块,需要关联Cotegory类(若要关联尚未定义的model,需要使用引号) content = models.TextField(u'文章内容')#文章内容 author = models.ForeignKey('UserInfo')#作者 pub_date = models.DateTimeField(blank=True,null=True)#发布日期,若是草稿,则没有发布时间 last_modify = models.DateTimeField(auto_now=True)#最后一此修改时间 priority = models.IntegerField(u'优先级',default=1000)#优先级,默认1000
head_img = models.ImageField('文章标题图片',upload_to='uploads')#存储标题图片,图片上传到uploads目录下,该目录在app下会自动创建
    status_choices = (
        ('draft','草稿'),
        ('published','已发布'),
        ('hidden','隐藏'),
    )
    status = models.CharField(choices=status_choices,default='published',max_length=32)#文章状态

    #发布时间的判断
    def clean(self):
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError('草稿没有发布时间')
        #如果已经发布,则发布时间设为当前时间
        if self.status == 'published' and self.pub_date is None:
            self.pub_date = datetime.date.today()

    def __unicode__(self):
        return self.title

    class Meta:
        verbose_name_plural = '文章'


#评论
class Comment(models.Model):
    article = models.ForeignKey(Article,verbose_name='所属文章')#评论所属文章
    parent_comment = models.ForeignKey('self',related_name='my_children',blank=True,null=True)#存储父评论,与自己关联,relate_name获取自己的子评论
    comment_choices = (
        (1,'评论'),
        (2,'点赞'),
    )
    comment_type = models.IntegerField(choices=comment_choices,default=1)#选择评论还是点赞,默认是评论
    user = models.ForeignKey('UserInfo')#发表评论或点赞的用户
    date = models.DateTimeField(auto_now_add=True)#评论时间
    comment = models.TextField(blank=True,null=True)#评论内容

    #判断评论是否为空
    def clean(self):
        if self.comment_type == 1 and len(self.comment) == 0:
            raise ValidationError('评论不能为空')

    def __unicode__(self):
        return '%s,P:%s,%s' %(self.article,self.parent_comment,self.comment)
    class Meta:
        verbose_name_plural = '评论'

#板块
class Category(models.Model):
    name = models.CharField(max_length=64)
    brief = models.CharField(blank=True,null=True,max_length=255)#板块简介
    set_as_top_menu = models.BooleanField(default=False)#是否将该模块添加到顶部菜单栏
    position = models.SmallIntegerField()#若在顶部菜单栏显示,则需要设置显示的顺序
    admin = models.ManyToManyField('UserInfo',blank=True)#板块管理员

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name_plural = '板块'
#用户
class UserInfo(models.Model):
    user = models.OneToOneField(User)#关联到django-admin自带的user表
    name = models.CharField(max_length=32)
    signature = models.CharField(max_length=255,blank=True,null=True)#个性签名
    head_img = models.ImageField(height_field=150,width_field=150,blank=True,null=True)#头像

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name_plural = '用户'

 

设置settings.py 

#注册app
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
]

#设置数据库
DATABASES = {
    'default': {
        #'ENGINE': 'django.db.backends.sqlite3',
        #'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'blog',
        'HOST':'',
        'PORT':'',
        'USER':'root',
        'PASSWORD':'0711'
    }
}

#模版路径
'DIRS': [
            os.path.join(BASE_DIR,'templates'),
        ],

#静态文件
STATICFILES_DIRS=(
    os.path.join(BASE_DIR,'statics'),     #该定义要求statics目录在project目录下
    )

 

一切前期准备就绪,在数据库中创建blog库(注意字符编码):

mysql> create database blog character set utf8;

 

同步数据库,创建表:

python manage.py makemigrations

python manage.py migrate

 

注册admin后台

admin.py

from django.contrib import admin

from blog import models

# Register your models here.

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title','category','author','pub_date','last_modify','status')  #每个表在页面上展示的字段


class CommentAdmin(admin.ModelAdmin):
    list_display = ('article','parent_comment','comment_type','comment','user','date')

class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name','set_as_top_menu','position')

admin.site.register(models.Article,ArticleAdmin)
admin.site.register(models.Category,CategoryAdmin)
admin.site.register(models.Comment,CommentAdmin)
admin.site.register(models.UserInfo)

 

二、华丽铠甲,折戟断箭。——前端展示模版的选择

 Bootstrap中挑选合适的页面模版,全部下载到本地。

设置模版和静态文件:

在project目录下创建templates和statics目录分别存放模版文件和静态文件

settings.py

#templates

    'DIRS': [
        os.path.join(BASE_DIR,'templates'),
    ],


#statics STATIC_URL
= '/static/' #该设置对static目录在app目录下有效 STATICFILES_DIRS =( os.path.join(BASE_DIR,'statics'), #该设置对statics目录在project目录下有效 )

 

在statics目录中添加静态文件:

一个论坛引发的血案

在templates目录中添加模版文件:

将下载的html文件修改为base.html作为父模版,和下载的目录一起放在templates下创建的blog目录下,然后创建index.html作为项目首页。

一个论坛引发的血案

index.html:

{% extends 'blog/base.html' %}    #继承父模版base.html

将base.html中js和css部分的url修改为本地的文件资源,如:

    <!-- Bootstrap core CSS -->
    <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="/static/bootstrap/css/navbar-fixed-top.css" rel="stylesheet">

 

urls.py

url采用根据不同项目做分发的规则,project目录下的urls.py:

from django.conf.urls import url,include
from django.contrib import admin


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

 

在app中新创建urls.py,然后设置如下:

from blog import views


urlpatterns = [
    url(r'^$',views.index),
]

 

url设置完毕,创建试图函数index,views.py:

def index(request):
    return render(request,'blog/index.html')

 一个简单的页面套用完毕。

 一个论坛引发的血案

 

三、临阵布局,指挥若定。——动态导航栏及登录

对页面进行改造,将导航栏设为动态,并添加登陆与退出功能。

1、动态导航

根据表结构的设计,在category表中,若set_as_top_menu字段为True,则该板块名称即可显示在导航栏中。即每个现实在导航栏中的category,其set_as_top_menu字段必须为Ture。并且,每个category都有一个position标示,这样就可以根据前端请求的url中的id来现实对应的目录名称。

blog/urls.py:

from blog import views


urlpatterns = [
    url(r'category/(\d+)',views.category),
]

views中,根据url中的id,查询category表,并将查询到的category名字返回给前端。

views.py:

category_list = models.Category.objects.filter(set_as_top_menu=True).order_by('position')      #将其定义为全局变量,便于多个函数引用

def index(request):
    return render(request,'blog/index.html',{'category_list':category_list})

def category(request,id):
    category_obj = models.Category.objects.get(id = id)
    return render(request,'blog/index.html',{'category_list':category_list,'category_obj':category_obj})

前端页面base.html中,循环所有在导航栏中显示的category,并和当前请求的category比较(根据id值),若两者相等,则跳转url并加底色。

base.html:

{% for category in category_list %}
{% if category.id == category_obj.id %} #当前请求id等于导航栏中某一个category的id值
<li class="active"><a href="/blog/category/{{ category.id }}">{{ category.name }}</a></li>
{% else %}
<li class=""><a href="/blog/category/{{ category.id }}">{{ category.name }}</a></li>
{% endif %}
{% endfor %}

这样,若点击导航栏中id为3的category,则url跳转至 http://localhost:8000/blog/category/3

2、登录及退出

若未登录,主页面右上角显示登录/注册按钮,可跳转至登录或注册页面;若已登录,则在该处显示用户名。

全局urls.py的设置:

from blog import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/',include('blog.urls')),
    url(r'^login/$',views.user_login,name='login'),
    url(r'^logout/$',views.user_logout,name='logout'),
]

 分别对登录和退出添加别名,方便前端页面中调用。

views.py中,引用django框架中的用户登录验证及退出功能。

from django.shortcuts import render,HttpResponseRedirect

# Create your views here.

from blog import models
from django.contrib.auth import login,logout,authenticate
from django.contrib.auth.decorators import login_required

category_list = models.Category.objects.filter(set_as_top_menu=True).order_by('position')

def index(request):
    return render(request,'blog/index.html',{'category_list':category_list})

def category(request,id):
    category_obj = models.Category.objects.get(id = id)
    return render(request,'blog/index.html',{'category_list':category_list,'category_obj':category_obj})

def user_login(request):
    if request.method == 'POST':
        user = authenticate(username = request.POST.get('username'),password = request.POST.get('password'))
        if user is not None:     #登录成功
            login(request,user)
            return HttpResponseRedirect('/blog')  
        else:
            login_error = 'wrong username or password!'
            return render(request,'blog/login.html',{'login_error':login_error})
    return render(request,'blog/login.html')

def user_logout(request):
    logout(request)
    return HttpResponseRedirect('/blog')

登录页面login.html:

{% extends 'blog/base.html' %}

{% block page-container %}
    <form action="" method="post"> {% csrf_token %}
        <div>
            <input type="text" name="username">
        </div>
        <div>
            <input type="password" name="password">
        </div>
        <div>
            <input type="submit" value="login">
        </div>
    </form>
    <div>
        {% if login_error %}
            <p style="color: red">{{ login_error }}</p>
        {% endif %}
    </div>
{% endblock %}

继承base.html 父模版,自定内容主题。

base.html中,在右上角添加登陆/注册按钮


<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li class="active"><a href="#">{{ request.user.userinfo.name }}</a></li> #反向查询,查询 UserInfo表中的name字段,这里需要将表名小写
<li class="active"><a href="{% url 'logout' %}">注销</a></li>
{% else %}
<li class=""><a href="{% url 'login' %}">登陆/注册</a></li>
{% endif %}
</ul>
 

效果:

一个论坛引发的血案 

 

三、左迂回,右包抄,稳坐中军帐。——内容排版展示

显示文章及图片

主页及各板块下显示相应文章及图片

views.py

category_list = models.Category.objects.filter(set_as_top_menu=True).order_by('position')

def index(request):
    #获取position为1的category,category_obj.id即position为1的categoryid值
    category_obj = models.Category.objects.get(position = 1)
    '''
    返回position为1的category
    前端请求主页时,首先从position=1开始循环所有category列表,此时'全部'板块的position为1,将该category加底色
    '''
    article_list = models.Article.objects.all()   #所有文章
    return render(request,'blog/index.html',{'category_list':category_list,'article_list':article_list,'category_obj':category_obj})

def category(request,id):
    category_obj = models.Category.objects.get(id = id)
    if  category_obj.position == 1:      #如果是全部,则显示全部文章
        article_list = models.Article.objects.all()
    else:
        article_list = models.Article.objects.filter(category_id=category_obj.id)   #article表中category_id字段
    return render(request,'blog/index.html',{'category_list':category_list,'category_obj':category_obj,'article_list':article_list})

 

前端页面展示设置中关于图片展示的问题:

'''
图片上传到uploads目录,前端请求的url为'/static/uploads/3_DDWq8O3.jpg',
uploads目录不在django静态文件的配置中,前端不能访问,因此要在setting中添加该目录,
这样实际图片位置为'/static/3_DDWq8O3.jpg',
自定义模版,将'/static/uploads/3_DDWq8O3.jpg'变为/static/3_DDWq8O3.jpg'
前端中图片src为'<img src="/static/{{ article.head_img }}">',
这样,只需要将'uploads/3_DDWq8O3.jpg'变为'3_DDWq8O3.jpg'即可
'''

settings.py:

STATICFILES_DIRS =(
    os.path.join(BASE_DIR,'statics'),
    os.path.join(BASE_DIR,'uploads'),
    )

自定义django模版:

blog目录下创建templatetags/custom.py文件,并在该目录下创建__init__.py文件。

customer.py:

#! _*_coding:utf-8 _*_
from django import template

register = template.Library()

@register.filter
def truncate_url(img_obj):
    return img_obj.name.split('/',1)[-1]   
  #
return img_obj.name.split('/')[1]
  #截取结果:['uploads','xxx.jpg']

#即将uploads/xxx.jpg 截为xxx.jpg,传给前端之后,就是static/xx.jpg #split(
'/',1)表示只截取一次,如'1/2/3'截取后为['1','2/3']

index.html页面:

{% extends 'blog/base.html' %}
{% load custom %}

{% block page-container %}
    {% for article in article_list %}
        <div class="article-box">
            <div class="article-head-img">
                <img src="/static/{{ article.head_img | truncate_url}}">   #自定义模版,将articel.head_img交给truncate_url函数处理
 </div> <div class="article-bruff"> {{ article.title }} </div> </div> {% endfor %} {% endblock %}

 

显示评论及点赞数

(样式参考虎嗅)

statics/bootstrap/css/目录下创建custom.css,用来定义图片及标题显示样式

custom.css

.page-container{
    border: 1px dashed darkcyan;
    padding-right: 150px;
    padding-left: 150px;
}


/*去除漂浮*/
.clear-both{
    clear: both;
}


/*左漂浮*/
.wrap-left{
    width: 75%;
    float: left;

}


/*右漂浮*/
.wrap-right{
    width: 25%;
    float: right;
    background-color: #9d9d9d;
}

.footer{
    height: 300px;
    background-color: #ac2925;
}

/*压缩图片*/
.article-head-img img{
    height: 150px;
    width: 200px;
}

/*每条新闻间距*/
.article-box{
    padding-bottom: 10px;
}

/*图片和标题之间距离*/
.article-brief{
    margin-left: -10px;
}


/*文章标题*/
.article-title{
    font-size: 20px;
}


/*a标签去掉下划线*/
a:hover{
    text-decoration: none;
}


/*字体样式*/
.body{
    color: rgb(51, 51, 51);
    font-family: Arial, 微软雅黑, "Microsoft yahei", "Hiragino Sans GB", "冬青黑体简体中文 w3", "Microsoft Yahei", "Hiragino Sans GB", "冬青黑体简体中文 w3", STXihei, 华文细黑, SimSun, 宋体, Heiti, 黑体, sans-serif;
}


/*简介样式*/
.article-brief-text{
    margin-top: 8px;
    color: #999;
}
View Code

相关文章:

  • 2022-01-14
  • 2022-01-14
  • 2019-03-25
  • 2021-11-05
  • 2021-11-27
  • 2022-12-23
  • 2021-04-18
猜你喜欢
  • 2021-05-29
  • 2021-06-02
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-07
相关资源
相似解决方案