开发BBS论坛
一、涉及表结构
web\model.py(首选需要注意表结构的设计,如果表结构设计出来了,软件的架构也就基本出来了)
#!/usr/bin/env python
# _*_ coding:utf- _*_
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=,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=)
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=) #有些帖子长期可以置顶,通过优先级实现
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=) #评论的内容,可以有最大数量限制
date=models.DateTimeField(auto_now=True)
def __unicode__(self):
return "<%s,user:%s>" %(self.comment,self.user)
'''
parent self son
Null null
null
null
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=,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=)
groups=models.ManyToManyField('UserGroup')
def __unicode__(self):
return self.name class UserGroup(models.Model):
'''用户组'''
name =models.CharField(max_length=,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() | NO | PRI | NULL | auto_increment |
| hostname | varchar() | NO | UNI | NULL | |
| ip_addr | varchar() | NO | UNI | NULL | |
| port | int() | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
rows in set (0.00 sec) mysql> desc host_user;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int() | NO | PRI | NULL | auto_increment |
| host_id | int() | YES | MUL | NULL | |
| auth_type | varchar() | YES | | NULL | |
| username | varchar() | NO | UNI | NULL | |
| password | varchar() | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
rows in set (0.00 sec) 其中:PRI表示主键,MUL表示外键,UNI表示该值是唯一的;
向表中插入数据
mysql> select * from host;
+----+-----------+----------+------+
| id | hostname | ip_addr | port |
+----+-----------+----------+------+
| | h2 | 10.0.0.2 | |
| | localhost | 10.0.0.1 | |
| | h3 | 10.0.0.3 | |
| | h5 | 10.0.0.5 | |
+----+-----------+----------+------+
rows in set (0.00 sec) mysql> select * from host_user;
+----+---------+-----------+----------+----------+
| id | host_id | auth_type | username | password |
+----+---------+-----------+----------+----------+
| | | ssh | charles | |
| | | ssh | root | |
| | | ssh | ul | |
| | NULL | ssh | Rain | |
+----+---------+-----------+----------+----------+
rows in set (0.01 sec)
两张表通过外键做关联查询
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 |
+----+---------+-----------+----------+----------+----+-----------+----------+------+
| | | ssh | charles | | | h2 | 10.0.0.2 | |
| | | ssh | root | | | localhost | 10.0.0.1 | |
| | | ssh | ul | | | h3 | 10.0.0.3 | |
+----+---------+-----------+----------+----------+----+-----------+----------+------+
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 |
+----+---------+-----------+----------+----------+------+-----------+----------+------+
| | | ssh | charles | | | h2 | 10.0.0.2 | |
| | | ssh | root | | | localhost | 10.0.0.1 | |
| | | ssh | ul | | | h3 | 10.0.0.3 | |
| | NULL | ssh | Rain | | 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 |
+------+---------+-----------+----------+----------+----+-----------+----------+------+
| | | ssh | charles | | | h2 | 10.0.0.2 | |
| | | ssh | root | | | localhost | 10.0.0.1 | |
| | | ssh | ul | | | h3 | 10.0.0.3 | |
| NULL | NULL | NULL | NULL | NULL | | h5 | 10.0.0.5 | |
+------+---------+-----------+----------+----------+----+-----------+----------+------+
rows in set (0.00 sec)
二、利用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)
二、静态文件配置
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 }}']")[]; //返回的是列表,在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- _*_
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
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=""></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- -*-
from django import forms
import os
class ArticleForm(forms.Form):
title=forms.CharField(max_length=,min_length=)
summary=forms.CharField(max_length=,min_length=)
category_id=forms.ImageField()
content=forms.CharField(min_length=) 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- -*-
'''通过递归的方式将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- -*-
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+)
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 =
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+) 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=()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 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 }}']")[]; //返回的是列表,在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=""></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 = ;
config.toolbarCanCollapse = true;
};
</script>
{% endblock %}
八、图片文件上传之后无法根据上传的路径显示的问题
图片会上传到我们指定的upload_to路径下面;
class Article(models.Model):
'''文章帖子'''
title = models.CharField(u'文章标题',max_length=,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'),
)
开发BBS论坛的更多相关文章
- Python开发一个简单的BBS论坛
项目:开发一个简单的BBS论坛 需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可 ...
- Java,面试题,简历,Linux,大数据,常用开发工具类,API文档,电子书,各种思维导图资源,百度网盘资源,BBS论坛系统 ERP管理系统 OA办公自动化管理系统 车辆管理系统 各种后台管理系统
Java,面试题,简历,Linux,大数据,常用开发工具类,API文档,电子书,各种思维导图资源,百度网盘资源BBS论坛系统 ERP管理系统 OA办公自动化管理系统 车辆管理系统 家庭理财系统 各种后 ...
- Python之路【第十八篇】Django小项目简单BBS论坛部分内容知识点
开发一个简单的BBS论坛 项目需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可被 ...
- python 学习笔记二十 django项目bbs论坛
项目:开发一个简单的BBS论坛 需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可 ...
- Python之路,Day17 - 分分钟做个BBS论坛
Python之路,Day17 - 分分钟做个BBS论坛 本节内容: 项目:开发一个简单的BBS论坛 需求: 整体参考"抽屉新热榜" + "虎嗅网" 实现不同 ...
- Django小项目简单BBS论坛
开发一个简单的BBS论坛 项目需求: 1 整体参考"抽屉新热榜" + "虎嗅网" 2 实现不同论坛版块 3 帖子列表展示 4 帖子评论数.点赞数展示 5 在线用 ...
- 开源BBS论坛软件推荐
七款开源BBS论坛软件推荐(1) 本文介绍了七个开源的BBS论坛软件(在英文界一般叫做Forum).可能国内的朋友们比较熟悉Discuz!和PHPwind,但其实我们的选择还是很多的,而且下面介绍的这 ...
- 聚焦“云开发圆桌论坛”,大前端Serverless大佬们释放了这些讯号!
4月14日,由云加社区举办的TVP&腾讯云技术交流日云开发专场,暨"腾讯云-云开发圆桌论坛"在北京.深圳两地同步举行. 当天下午,一场主题为"基于大前端和node ...
- python第一百三十天 ---简单的BBS论坛
简单的BBS论坛 实现功能 git仓库地址:https://github.com/uge3/BBS 1.整体参考“抽屉新热榜” + “博客园” 2.实现不同论坛版块 3.帖子列表展示 4.个人博客主页 ...
随机推荐
- MySql数据库索引原理
写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将 ...
- 自动选中div中的文字
<html> <head> <title></title> <script type="text/javascript" de ...
- mysql系统库INFORMATION_SCHEMA,MySQL,TEST,mysql系统表的作用
本文简要说明了MySQL数据库安装好后自带的INFORMATION_SCHEMA,MySQL,TEST三个数据库的用途. 第一个数据库INFORMATION_SCHEMA: 提供了访问数据库元数据的方 ...
- [Spring MVC] - SpringMVC的各种参数绑定方式
SpringMVC的各种参数绑定方式 1. 基本数据类型(以int为例,其他类似):Controller代码: @RequestMapping("saysth.do") publi ...
- [Java] - 格式字符串替换方法
Java 字符串格式替换方法有两种,一种是使用String.format(...),另一种是使用MessageFormat.format(...) 如下: import java.text.Messa ...
- filter and listener
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层. 使用 Serv ...
- 解决Linux性能问题的前60秒
为了解决性能问题,你登入了一台Linux服务器,在最开始的一分钟内需要查看什么? 在Netflix我们有一个庞大的EC2 Linux集群,还有非常多的性能分析工具来监控和调查它的性能.其中包括用于云监 ...
- 53. Maximum Subarray
Find the contiguous subarray within an array (containing at least one number) which has the largest ...
- [HTML5]a标签禁止嵌套使用
a标签内部不可再写a标签,否则会与父a标签解析到同一级.
- Spring AOP 完成日志记录
Spring AOP 完成日志记录 http://hotstrong.iteye.com/blog/1330046