开发BBS论坛

一、涉及表结构

webmodel.py(首选需要注意表结构的设计,如果表结构设计出来了,软件的架构也就基本出来了)

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
from django.db import models

# Create your models here.
from django.contrib.auth.models import User   #导入django自带的用户认证表

class Article(models.Model):
    '''文章帖子'''
    title = models.CharField(u'文章标题',max_length=255,unique=True)    #帖子不可以重名,可以加注释,在admin中展示内容为'文章标题'
    category=models.ForeignKey("Category",verbose_name=u'板块')    #发布板块,必须加引号,因为Category是在下面的,加引号可以通过反射的方式进行查找,不会找不到;如果注释不是放在第一个位置,那么就需要使用verbose_name
    head_img = models.ImageField(upload_to="uploads")   #传文件,默认会传到当前项目的根目录下面,通过upload_to指定传送到的目录
    summary=models.CharField(max_length=255)
    content=models.TextField(u'内容')  #因为文章中会存很多的内容,所以不能够使用charfiled规定最大长度,使用TextField可以不指定长度
    author=models.ForeignKey("UserProfile")
    public_date=models.DateTimeField(auto_now=True)    #自动创建日期,auto_now_add表示每一次更新的时间,auto_now表示每一次创建的时间
    hidden=models.BooleanField(default=True)   #是否影藏
    priority=models.IntegerField(u'优先级',default=1000)   #有些帖子长期可以置顶,通过优先级实现
    def __unicode__(self):    #设置默认返回值,在admin中对于增加的每一条数据就会显示return的结果
        return "<%s,author>%s" %(self.title,self.author)

class Comment(models.Model):
    '''评论'''
    article=models.ForeignKey("Article")
    user = models.ForeignKey("UserProfile")
    parent_comment=models.ForeignKey('self',related_name='p_comment',blank=True,null=True)  #以自己的表为外键关联的表,可以为自己的表名或者为self,django会在自己的表中多创建一个字段,related_name必须给出,否则会报错;
    # blank=True表示admin中可以为空,null=True表示在数据库表中可以为空
    comment=models.TextField(max_length=1000)    #评论的内容,可以有最大数量限制
    date=models.DateTimeField(auto_now=True)
    def __unicode__(self):
        return "<%s,user:%s>" %(self.comment,self.user)
    '''
    parent  self    son
    Null    1       null
    1       2       null
    1       3       null
    2       4       null
    每一条评论只需要关注自己和自己的第一条子评论,就是多了一个字段,SQL不支持,是通过代码级别实现的
    '''
    '''
    python manage.py migrate 只会创建django自己的数据库表
    第二次 会创建用户自定义的数据库表
    '''
class ThumbUp(models.Model):
    '''点赞'''
    article=models.ForeignKey("Article")
    user=models.ForeignKey("UserProfile")
    date=models.DateTimeField(auto_now=True)
    def __unicode__(self):
        return "<user:%s>" %self.user

class Category(models.Model):
    '''板块'''
    name=models.CharField(max_length=64,unique=True)
    admin=models.ManyToManyField("UserProfile")
    def __unicode__(self):
        return self.name

class UserProfile(models.Model):
    '''账户信息表'''
    user=models.OneToOneField(User)   #继承自带的User表,但是原生的user表中的字段较少,可以继承之后可以扩展字段;
只能使用onetoone,否则就会使得多个用户同时关联一个账户onetoone是在代码层面进行限制的,其实就是将两张表进行拼接了 name
= models.CharField(max_length=32) groups=models.ManyToManyField('UserGroup') def __unicode__(self): return self.name class UserGroup(models.Model): '''用户组''' name =models.CharField(max_length=64,unique=True) def __unicode__(self): return self.name

 使用admin管理数据库

from django.contrib import admin

# Register your models here.
class CategroyAdmin(admin.ModelAdmin):
    list_display = ('id','name')
class CommentAdmin(admin.ModelAdmin):
    list_display = ('id','parent_comment','comment','date')
class ArticleAdmin(admin.ModelAdmin):
    list_display = ('id','title','author','hidden','public_date')   #注意list_display中的字段。一定需要和数据库张的匹配,否则会报错
import models
admin.site.register(models.Article,ArticleAdmin)
admin.site.register(models.Category,CategroyAdmin)
admin.site.register(models.Comment,CommentAdmin)
admin.site.register(models.ThumbUp)
admin.site.register(models.UserProfile)
admin.site.register(models.UserGroup)

显示结果是这样的

创建两张表

mysql> show tables;
+-----------------------------+
| Tables_in_stupid_jumpserver |
+-----------------------------+
| host                        |
| host_user                   |
+-----------------------------+
rows in set (0.00 sec)

mysql> desc host;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int(11)     | NO   | PRI | NULL    | auto_increment |
| hostname | varchar(64) | NO   | UNI | NULL    |                |
| ip_addr  | varchar(64) | NO   | UNI | NULL    |                |
| port     | int(11)     | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
rows in set (0.00 sec)

mysql> desc host_user;
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| host_id   | int(11)      | YES  | MUL | NULL    |                |
| auth_type | varchar(255) | YES  |     | NULL    |                |
| username  | varchar(64)  | NO   | UNI | NULL    |                |
| password  | varchar(255) | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+
rows in set (0.00 sec)

其中:PRI表示主键,MUL表示外键,UNI表示该值是唯一的;
View Code

向表中插入数据

mysql> select * from host;
+----+-----------+----------+------+
| id | hostname  | ip_addr  | port |
+----+-----------+----------+------+
|  7 | h2        | 10.0.0.2 |   22 |
|  6 | localhost | 10.0.0.1 |   22 |
|  8 | h3        | 10.0.0.3 |   22 |
|  9 | h5        | 10.0.0.5 |   22 |
+----+-----------+----------+------+
4 rows in set (0.00 sec)

mysql> select * from host_user;
+----+---------+-----------+----------+----------+
| id | host_id | auth_type | username | password |
+----+---------+-----------+----------+----------+
|  5 |       7 | ssh       | charles  | 123      |
|  4 |       6 | ssh       | root     | 123      |
|  6 |       8 | ssh       | ul       | 123      |
|  7 |    NULL | ssh       | Rain     | 123      |
+----+---------+-----------+----------+----------+
4 rows in set (0.01 sec)
View Code

两张表通过外键做关联查询

inner join表示取两者的交集
mysql> select * from host_user inner join host on host_user.host_id=host.id;
+----+---------+-----------+----------+----------+----+-----------+----------+------+
| id | host_id | auth_type | username | password | id | hostname  | ip_addr  | port |
+----+---------+-----------+----------+----------+----+-----------+----------+------+
|  5 |       7 | ssh       | charles  | 123      |  7 | h2        | 10.0.0.2 |   22 |
|  4 |       6 | ssh       | root     | 123      |  6 | localhost | 10.0.0.1 |   22 |
|  6 |       8 | ssh       | ul       | 123      |  8 | h3        | 10.0.0.3 |   22 |
+----+---------+-----------+----------+----------+----+-----------+----------+------+
rows in set (0.00 sec)

left join
mysql> select * from host_user left join host on host_user.host_id=host.id; 
+----+---------+-----------+----------+----------+------+-----------+----------+------+
| id | host_id | auth_type | username | password | id   | hostname  | ip_addr  | port |
+----+---------+-----------+----------+----------+------+-----------+----------+------+
|  5 |       7 | ssh       | charles  | 123      |    7 | h2        | 10.0.0.2 |   22 |
|  4 |       6 | ssh       | root     | 123      |    6 | localhost | 10.0.0.1 |   22 |
|  6 |       8 | ssh       | ul       | 123      |    8 | h3        | 10.0.0.3 |   22 |
|  7 |    NULL | ssh       | Rain     | 123      | NULL | NULL      | NULL     | NULL |
+----+---------+-----------+----------+----------+------+-----------+----------+------+
rows in set (0.00 sec)

right join
mysql> select * from host_user right join host on host_user.host_id=host.id;    
+------+---------+-----------+----------+----------+----+-----------+----------+------+
| id   | host_id | auth_type | username | password | id | hostname  | ip_addr  | port |
+------+---------+-----------+----------+----------+----+-----------+----------+------+
|    5 |       7 | ssh       | charles  | 123      |  7 | h2        | 10.0.0.2 |   22 |
|    4 |       6 | ssh       | root     | 123      |  6 | localhost | 10.0.0.1 |   22 |
|    6 |       8 | ssh       | ul       | 123      |  8 | h3        | 10.0.0.3 |   22 |
| NULL |    NULL | NULL      | NULL     | NULL     |  9 | h5        | 10.0.0.5 |   22 |
+------+---------+-----------+----------+----------+----+-----------+----------+------+
rows in set (0.00 sec)
View Code

 二、利用admin来管理数据库以及创建数据库表的字段

from django.contrib import admin
import models
# Register your models here.
class CategroyAdmin(admin.ModelAdmin):
    list_display = ('id','name')
class ArticleAdmin(admin.ModelAdmin):
    list_display = ('id','title','author','hidden','publish_date')   #创建表字段

class CommentAdmin(admin.ModelAdmin):
    list_display = ('id','parent_comment','comment','date')
admin.site.register(models.Article,ArticleAdmin)
admin.site.register(models.Category,CategroyAdmin)
admin.site.register(models.Comment,CommentAdmin)
admin.site.register(models.ThumbUp)
admin.site.register(models.UserProfile)
admin.site.register(models.UserGroup)
View Code

二、静态文件配置

settings.py

STATIC_URL = '/static/'    #静态文件的前缀,相当于一个别名,是一个入口的存在,通过这个入口可以找到所有STATICFILES_DIRS中的静态文件
STATICFILES_DIRS=(               #可以存多个静态文件的路径,在调用静态路径的时候,静态文件都会在指定的各个目录下面找
    "%s%s" %(BASE_DIR,'statics'),
    #os.path.join(BASE_DIR,'static'),
)

index.html

    <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">
    <link href="/static/bootstrap/css/custom.css" rel="stylesheet">

三、利用url传递参数页面的自动切换

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

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^$',views.index, name="index" ),
    url(r'^category/(d+)/$',views.category,name="category" ),     #通过不同的数字引用不同的urls
    url(r'^article/(d+)/$',views.article_detail,name="article_detail"),      
    url(r'^article/new/$',views.new_article,name="new_article"),
    url(r'account/logout/',views.acc_logout,name='logout'),
    url(r'account/login/',views.acc_login,name='login'),
]
urls
          <ul class="nav navbar-nav">
            <li ><a href="{% url 'index' %}">综合区</a></li>
            <li><a href="{% url 'category' 1 %}">欧美专区</a></li>
            <li><a href="{% url 'category' 2 %}">日韩专区</a></li>
            <li><a href="{% url 'category' 3 %}">河北区</a></li>

          </ul>
index.html

 对于点击的标签,增加active属性

index.html   对于每一个标签,被选中的时候,应该添加active属性,

        $(document).ready(function(){
            var menus = $("#navbar a[href='{{ request.path }}']")[0];    //返回的是列表,在js中引用template的变量,request.path表示请求的路径,因为在点击的时候回切换页面,如果页面不切换,可以直接找到 #nvabar li来实现
            $(menus).parent().addClass("active");
            $(menus).parent().siblings().removeClass("active");
            //console.log(menus);
        });

三、实现用户登录和注销

      <ul class="nav navbar-nav navbar-right">

               {% if request.user.is_authenticated %}
                <li class="dropdown">
                  <a href="http://v3.bootcss.com/examples/navbar-fixed-top/#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.user.userprofile.name }} <span class="caret"></span></a>
                  <ul class="dropdown-menu">
                    <li><a href="{% url 'new_article' %}">发贴</a></li>
                    <li><a href="http://v3.bootcss.com/examples/navbar-fixed-top/#">Another action</a></li>
                    <li><a href="http://v3.bootcss.com/examples/navbar-fixed-top/#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li class="dropdown-header">Nav header</li>
                    <li><a href="http://v3.bootcss.com/examples/navbar-fixed-top/#">Separated link</a></li>
                    <li><a href="{% url 'logout' %}">注销</a></li>
                  </ul>
                </li>
               {% else %}
                 <li><a href="{% url 'login'%}">注册登录</a></li>
               {% endif %}
          </ul>
index.html
from django.conf.urls import include, url
from django.contrib import admin
from web import views

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^$',views.index, name="index" ),
    url(r'^category/(d+)/$',views.category,name="category" ),
    url(r'^article/(d+)/$',views.article_detail,name="article_detail"),
    url(r'^article/new/$',views.new_article,name="new_article"),
    url(r'account/logout/',views.acc_logout,name='logout'),
    url(r'account/login/',views.acc_login,name='login'),
]
urls
from django.contrib.auth import authenticate,login,logout
def acc_logout(request):
    logout(request)     #直接调用django自己的logout函数
    return HttpResponseRedirect('/')     #跳转到首页
def acc_login(request):
    print(request.POST)
    err_msg =''
    if request.method == "POST":
        print('user authention...')
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = authenticate(username=username,password=password)
        if user is not None:
            login(request,user)
            return HttpResponseRedirect('/')
        else:
            err_msg = "Wrong username or password!"
    return render(request,'login.html',{'err_msg':err_msg})
views

views.py

#!/usr/bin/env python
#! _*_ coding:utf-8 _*_
from django.shortcuts import render
from web import models
from django.core.exceptions import ObjectDoesNotExist
from web import forms
# Create your views here.

def index(request):
    articles=models.Article.objects.all()
    return render(request,'index.html',{'articles':articles})

def category(request,category_id):
    print "-->",category_id
    articles=models.Article.objects.filter(category_id=category_id)    #根据各个板块的id找到各个板块下面的对应的数据
    return render(request,'index.html',{'articles':articles})

def article_detail(request,article_id):
    try:
        article_obj=models.Article.objects.get(id=article_id)
    except ObjectDoesNotExist as e:
        return render(request,'404.html',{'err_msg':u'xxxxx'})
    return render(request,'article.html',{'article_obj':article_obj})

def new_article(request):
    if request.method=='POST':
        print request.POST
        form=forms.ArticleForm(request.POST,request.FILES)
        if form.is_valid():
            print "--form data",form.cleaned_data
            form_data = form.cleaned_data
            form_data['author_id']=request.user.userprofile.id    #author_id必须写,否则数据库会报错,request.user表示当前请求登录的用户
            new_img_path=forms.handle_uploaded_file(request,request.FILES.id)
            form_data['head_img']=new_img_path
            new_article_obj=models.Article(**form_data)
            return render(request,'new_article.html',{'new_article_obj':new_article_obj})
        else:
            print {'err':form.errors}
    category_list=models.Category.objects.all()
    return render(request,'new_article.html',{'category_list':category_list})
def acc_logout(request):
    pass

def acc_login(request):
    pass

四、CSRF

{% extends 'index.html' %}


{% block page-container %}

<div class="col-md-4">
    <form class="form-signin" action="{% url 'login' %}" method="post">{% csrf_token %}     //必须加{% csrf_token %}
        <h2 class="form-signin-heading">Please sign in</h2>
        <label for="inputEmail" class="sr-only">用户名</label>
        <input type="text" id="" name="username" class="form-control" placeholder="username" required="" autofocus="">
        <label for="inputPassword" class="sr-only">Password</label>
        <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required="">
        <div class="checkbox">
          <label>
            <input type="checkbox" value="remember-me"> Remember me
          </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
        <p style="color:red;">{{ err_msg }}</p>
    </form>
</div>


{% endblock %}
login.html
def acc_login(request):
    print(request.POST)
    err_msg =''
    if request.method == "POST":
        print('user authention...')
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = authenticate(username=username,password=password)
        if user is not None:
            login(request,user)
            return HttpResponseRedirect('/')
        else:
            err_msg = "Wrong username or password!"
    return render(request,'login.html',{'err_msg':err_msg})   #使用csrf,视图中必须使用render
views.py

五、实现用户登录

def acc_login(request):
    print(request.POST)
    err_msg =''
    if request.method == "POST":
        print('user authention...')
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = authenticate(username=username,password=password)   #authenticate函数会自动到数据库取用户数据进行验证
        if user is not None:      #函数没有取到值就会返回None
            login(request,user)
            return HttpResponseRedirect('/')
        else:
            err_msg = "Wrong username or password!"
    return render(request,'login.html',{'err_msg':err_msg})   #使用csrf,视图中必须使用render
View Code

 http://ueditor.baidu.com/website/  百度插件,但是不支持python,网站已经有人通过修改支持django了,可以try一下;

http://ckeditor.com/demo   非常好用的插件,支持python,使用full featured版本,比较全

 六、后台通过forms对表单进行验证

def new_article(request):
    if request.method == 'POST':
        print(request.POST)
        form = ArticleForm(request.POST,request.FILES)
        if form.is_valid():
            print("--form data:",form.cleaned_data)
            form_data = form.cleaned_data
            form_data['author_id'] = request.user.userprofile.id

            new_img_path = handle_uploaded_file(request,request.FILES['head_img'])
            form_data['head_img'] = new_img_path
            new_article_obj = models.Article(**form_data)     #将数据插入到数据库中,new_article_obj表示文章是否创建成功
            new_article_obj.save()    #这样比model.Article.object.all(**form_data)创建有好处,可以之间通过new_article_obj.元素在html中显示内容是否创建成功
            return  render(request,'new_article.html',{'new_article_obj':new_article_obj})
        else:
            print('err:',form.errors)
    category_list = models.Category.objects.all()
    return render(request,'new_article.html', {'categroy_list':category_list})
views.py
{% block page-container %}
   <div class="new-article">
    {% if new_article_obj %}
        <h3>文章<{{ new_article_obj.title }}>已发布,<a href="{% url 'article_detail' new_article_obj.id %}"> 点我查看</a></h3>
    {% else %}
       <form  enctype="multipart/form-data" method="post" action="{% url 'new_article' %}">{% csrf_token %}
        <input name="title" type="text" class="form-control" placeholder="文章标题">
        <select name="categroy_id" class="form-control">
          {% for category in categroy_list %}
            <option value="{{ category.id }}">{{ category.name }}</option>
          {% endfor %}
        </select>
        <input name="summary" type="text" class="form-control" placeholder="一句话文章中心思想...">
        <input type="file" name="head_img">必选文章标题图片
        <textarea id="ck-editor" name="content" class="form-control" rows="3"></textarea>

        <br/>
       <button type="submit" class="btn btn-success pull-right">发贴</button>

    </form>
    {% endif %}
   </div>
{% endblock %}
new_article.html

 forms.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from django import forms
import os
class ArticleForm(forms.Form):
    title=forms.CharField(max_length=255,min_length=5)
    summary=forms.CharField(max_length=255,min_length=5)
    category_id=forms.ImageField()
    content=forms.CharField(min_length=10)

def handle_uploaded_file(request,f):    #将request.FILES传送进来
    base_img_upload_path='static/img'
    user_path="%s/%s" %(base_img_upload_path,request.user.userprofile.id)
    if not os.path.exists(user_path):
        os.mkdir(user_path)
    with open("%s/%s"%(user_path,f.name),'wb+') as destination:
        for chunk in f.chunks():    #chunk内部其实就是yield方法
            destination.write(chunk)
    return "/static/imgs/%s/%s" %(request.user.userprofile.id,f.name)

七、评论树的实现

#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''通过递归的方式将data中的数据按照层级的关系存入新的字典中'''
data = [
    (None,'A'),
    ('A','A1'),
    ('A','A1-1'),
    ('A1','A2'),
    ('A1-1','A2-3'),
    ('A2-3','A3-4'),
    ('A1','A2-2'),
    ('A2','A3'),
    ('A2-2','A3-3'),
    ('A3','A4'),
    (None,'B'),
    ('B','B1'),
    ('B1','B2'),
    ('B1','B2-2'),
    ('B2','B3'),
    (None,'C'),
    ('C','C1'),

]
def tree_search(d_dic,parent,son):
    for k,v_dic in d_dic.items():
        if k == parent: #find your parent    #判断第一层是否为父亲
            d_dic[k][son] = {}
            print("find parent of :", son)
            return
        else: # might in the deeper layer   #否则往更深了找
            print("going to furhter layer...")
            tree_search(d_dic[k],parent,son)


data_dic = {}

for item in data:   #循环将每一个元素取出来,放入到函数中进行递归处理
    parent,son = item    #item代表两个值
    if parent is None:# has no parent    #判断parents是否为空
        data_dic[son] ={}
    else: #  looking for its parent
        tree_search(data_dic,parent,son)   #递归判断字典中,父亲和儿子

for k,v in data_dic.items():
    print(k,v )

'''
data_dic = {
    'A': {
        'A1': {
            'A2':{
                'A3':{
                    'A4':{}
                }
            },
            'A2-2':{
                'A3-3':{}
            }
        }
    },
    'B':{
        'B1':{
            'B2':{
                'B3':{}
            },
            'B2-2':{}
        }
    },
    'C':{
        'C1':{}
    }

}'''
'''多级评论树的实现,是将data转换为下面类型的字典'''

 对于前段template来说,只能有for循环,无法实现上述的递归,只能通过simple_tag返回html字符串

custom_tags.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from django import template

register = template.Library()

def tree_search(d_dic,comment_obj):
    for k,v_dic in d_dic.items():
        if k == comment_obj.parent_comment: #find parent
            d_dic[k][comment_obj] = {}
            return
        else: #going deeper....;
            tree_search(d_dic[k],comment_obj)


def generate_comment_html(sub_comment_dic,margin_left_val):
    html = ""
    for k,v_dic in sub_comment_dic.items():
        html += "<div style='margin-left:%spx' class='comment-node'>" % margin_left_val + k.comment + "</div>"
        if v_dic:
            html += generate_comment_html(v_dic,margin_left_val+15)
    return html
@register.simple_tag
def build_comment_tree(comment_list):
    #print("commment_list:",comment_list)

    comment_dic = {}
    for comment_obj in comment_list:
        if comment_obj.parent_comment is None:#no parent
            comment_dic[comment_obj] ={}
        else: #has farther ,
            tree_search(comment_dic,comment_obj)

    # tree is built

    # pin html str
    html = "<div class='comment-box'>"
    margin_left = 0
    for k,v in comment_dic.items():
        print(k,v )
        html  += "<div class='comment-node'>" + k.comment + "</div>"
        html += generate_comment_html(v,margin_left+15)

    html += "</div>"
    return html

aticle.html

{% extends 'index.html' %}
{% load custom_tags %}

{% block page-container %}
   <div class="article-detail">
        <h4>{{ article_obj.title }}</h4>

        <p>{{ article_obj.content|safe }}</p>

        <hr/>
        {%  build_comment_tree article_obj.comment_set.select_related %}   //通过article找到评论表的的评论
   </div>
{% endblock %}

index.html

<!DOCTYPE html>
<!-- saved from url=(0048)http://v3.bootcss.com/examples/navbar-fixed-top/ -->
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="http://v3.bootcss.com/favicon.ico">

    <title>Oldboy BBS</title>

    <!-- 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">
    <link href="/static/bootstrap/css/custom.css" rel="stylesheet">
    {% block head-js %}
    {% endblock %}
  </head>

  <body>

    <!-- Fixed navbar -->
    <nav class="navbar navbar-default navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <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="http://v3.bootcss.com/examples/navbar-fixed-top/#">电影</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li ><a href="{% url 'index' %}">综合区</a></li>
            <li><a href="{% url 'category' 1 %}">电视剧专区</a></li>
            <li><a href="{% url 'category' 2 %}">日韩专区</a></li>
            <li><a href="{% url 'category' 3 %}">河北区</a></li>

          </ul>
          <ul class="nav navbar-nav navbar-right">

               {% if request.user.is_authenticated %}
                <li class="dropdown">
                  <a href="http://v3.bootcss.com/examples/navbar-fixed-top/#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.user.userprofile.name }} <span class="caret"></span></a>
                  <ul class="dropdown-menu">
                    <li><a href="{% url 'new_article' %}">发贴</a></li>
                    <li><a href="http://v3.bootcss.com/examples/navbar-fixed-top/#">Another action</a></li>
                    <li><a href="http://v3.bootcss.com/examples/navbar-fixed-top/#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li class="dropdown-header">Nav header</li>
                    <li><a href="http://v3.bootcss.com/examples/navbar-fixed-top/#">Separated link</a></li>
                    <li><a href="{% url 'logout' %}">注销</a></li>
                  </ul>
                </li>
               {% else %}
                 <li><a href="{% url 'login'%}">注册登录</a></li>
               {% endif %}
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>

    <div class="container">
     {% block page-container %}
        <div class="row">
            <div class="col-md-8 left-content-panel">
                <div class="content-box">
                    {% for article in articles reversed %}
                        <div class="article-box row">
                            <div class="article-head-img col-md-3">
                                <img src="{{ article.head_img }}">
                            </div>
                            <div class="article-summary col-md-8">
                                <h4><a href="{% url 'article_detail' article.id %}">{{ article.title }}</a></h4>
                                <div class="article-attr">
                                    <ul  class="list-inline">
                                        <li>{{ article.author.name }}</li>
                                        <li>{{ article.publish_date }}</li>
                                        <li>thumbup:{{ article.thumbup_set.select_related.count }}</li>    //thumbup的article的字段的外键关联到article表,通过
article.thumbup_set.select_related来反向找到原表的字段(一对多),为列表类型, 
<li>comments:{{ article.comment_set.select_related.count }}</li> </ul> </div> <p>{{ article.summary }}</p> </div> </div> <hr > {% endfor %} </div> </div> <div class="col-md-4 right-sidebar"> bar </div> </div> {% endblock %} </div> <!-- /container --> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="/static/bootstrap/js/jquery-2.1.4.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> <!-- IE10 viewport hack for Surface/desktop Windows 8 bug --> <script src="/static/bootstrap/js/ie10-viewport-bug-workaround.js"></script> <script type="text/javascript"> $(document).ready(function(){ var menus = $("#navbar a[href='{{ request.path }}']")[0]; //返回的是列表,在js中引用template的变量,request.path表示请求的路径,因为在点击的时候回切换页面,如果页面不切换,可以直接找到 #nvabar li来实现 $(menus).parent().addClass("active"); $(menus).parent().siblings().removeClass("active"); //console.log(menus);  }); </script> {% block bottom-js %} {% endblock %} </body></html>

new_article.html

{% extends 'index.html' %}
{% block head-js %}
   <script src="/static/plugins/ckeditor/ckeditor.js"></script>
{% endblock %}

{% block page-container %}
   <div class="new-article">
    {% if new_article_obj %}
        <h3>文章<{{ new_article_obj.title }}>已发布,<a href="{% url 'article_detail' new_article_obj.id %}"> 点我查看</a></h3>
    {% else %}
       <form  enctype="multipart/form-data" method="post" action="{% url 'new_article' %}">{% csrf_token %}  //跨站请求伪造
        <input name="title" type="text" class="form-control" placeholder="文章标题">
        <select name="categroy_id" class="form-control">
          {% for category in categroy_list %}
            <option value="{{ category.id }}">{{ category.name }}</option>
          {% endfor %}
        </select>
        <input name="summary" type="text" class="form-control" placeholder="一句话文章中心思想...">
        <input type="file" name="head_img">必选文章标题图片
        <textarea id="ck-editor" name="content" class="form-control" rows="3"></textarea>

        <br/>
       <button type="submit" class="btn btn-success pull-right">发贴</button>

    </form>
    {% endif %}
   </div>
{% endblock %}

{% block bottom-js %}
    <script>
         CKEDITOR.replace( 'ck-editor' );
        CKEDITOR.editorConfig = function( config ) {
            //config.language = 'es';
            config.uiColor = '#F7B42C';
            config.height = 500;
            config.toolbarCanCollapse = true;
        };
    </script>
{% endblock %}

 八、图片文件上传之后无法根据上传的路径显示的问题

图片会上传到我们指定的upload_to路径下面;

class Article(models.Model):
    '''文章帖子'''
    title = models.CharField(u'文章标题',max_length=255,unique=True)    #帖子不可以重名,可以加注释,在admin中展示内容为'文章标题'
    category=models.ForeignKey("Category",verbose_name=u'板块')    #发布板块,必须加引号,因为Category是在下面的,加引号可以通过反射的方式进行查找,不会找不到;如果注释不是放在第一个位置,那么就需要使用verbose_name
    head_img = models.ImageField(upload_to="uploads") 

但是该路径html无法找到,看下图:

如果在settings中指定该静态文件路径,依然找不到,因为会去/static/uploads/uploads去找,使用下面的路径就可以找到:

STATICFILES_DIRS=(               #可以存多个静态文件的路径,在调用静态路径的时候,静态文件都会在指定的各个目录下面找
    "%s%s" %(BASE_DIR,'statics'),
    'uploads'
    #os.path.join(BASE_DIR,'static'),
)

原文地址:https://www.cnblogs.com/cqq-20151202/p/5631651.html