python -m venv venv_vcard
cd venv_vcard\scripts\activate
安装固定版本’pip install django==1.11.4’
项目创建
django-admin startproject vcard
配置settings.py
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
python manage.py runserver
python manage.py startapp guestbook
setting.py
里注册appINSTALLED_APPS = [...,'guestbook','blog',]
python manage.py migrate
python manage.py createsuperuser
添加 apps 目录
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
import reprlib
from django.db import models
class Message(models.Model):
"""留言消息类"""
name = models.CharField('用户名', max_length=20)
email = models.EmailField('邮箱',max_length=200)
message = models.TextField('留言')
active = models.BooleanField('有效', default=True)
posted = models.DateTimeField('发布时间', auto_now_add=True)
def __str__(self):
return f'{self.name} {reprlib.repr(self.message)}'
使用标准库
reprlib
中的repr()
方法进行格式化显示:长内容留住头和尾,中间用省略号表示
from django.contrib import admin
from .models import Message
class MessageAdmin(admin.ModelAdmin):
"""留言消息管理"""
list_display = ('name', 'email', 'active', 'posted') #显示
list_filter = ('active', 'posted') #过滤条件
search_fields = ('name', 'message') #搜索
ordering = ('-posted',) #排序
#注册留言管理
admin.site.register(Message, MessageAdmin)
python manage.py makemigrations guestbook
python manage.py migrate guestbook 0001
views.py
视图中编写视图from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from .models import Message
def post_message(request):
"""首页留言提交处理"""
if request.method == 'POST':
name = request.POST.get('name','')
email = request.POST.get('email','')
content = request.POST.get('message','')
if name and content:
msg = Message(name=name, email=email, message=content)
msg.save()
return redirect(reverse('home'))
else:
return HttpResponse('用户名及留言必须填写!')
return redirect(reverse('home'))
...
from guestbook import views as gb_views
urlpatterns = [
url(r'^post-message/$', view=gb_views.post_message, name='post_message'),
...
]
templates
文件夹,将前台模板复制在此路径下setting.py
下进行配置TEMPLATES = [ {'DIRS': [os.path.join(BASE_DIR, 'templates'),],..}]
from django.shortcuts import render, redirect, reverse
def home(request):
"""vCard首页"""
return render(request, 'index.html')
urls.py
里添加url规则
from . import views
urlpatterns = [ url(r'^$', view=views.home, name='home'),..]
默认从app目录下查找static作为静态资源文件目录
static
文件夹,将 静态资源复制在此路径下setting.py
里手动配置静态资源目录,指定使用项目下的staticSTATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),]
/static/
引用资源位置{% load static %}
# 载入静态资源
由static指令自动转换呈现资源相对位置block
占位{% block content %} {% endblock content %}
{% extends "base.html" %}
{% block content %} 正文内容 {% endblock content %}
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Category(models.Model):
"""日志分类"""
name = models.CharField('分类名称', max_length=50)
slug = models.SlugField('URL缩写', max_length=100)
description = models.CharField('备注', max_length=200, null=True)
def __str__(self):
return self.name
class Post(models.Model):
"""日志文章"""
STATUS_CHOICES= (
('draft', '草稿'),
('published', '发布'),
)
title = models.CharField('标题', max_length=100)
slug = models.SlugField('URL缩写', max_length=100, unique_for_date='publish') #相同日期不能重复(根据发布时间)
Category = models.ForeignKey(Category, related_name='blog_posts')
author = models.ForeignKey(User, related_name='blog_posts')
image = models.ImageField('图片', null=True, blank=True)
body = models.TextField('正文')
publish = models.DateTimeField('发布时间', default=timezone.now)
created = models.DateTimeField('创建时间', auto_now_add=True)
updated = models.DateTimeField('更新时间', auto_now=True)
status = models.CharField('发布状态', max_length=50, choices=STATUS_CHOICES, default='draft')
def __str__(self):
return self.title
class Meta:
ordering = ['-publish']
class Comment(models.Model):
"""日志评论"""
post = models.ForeignKey(Post, related_name='comments')
name = models.CharField('用户名', max_length=20)
email = models.EmailField('邮箱', max_length=200)
content = models.TextField('评论')
active = models.BooleanField('有效状态', default=True)
created = models.DateTimeField('提交时间', auto_now_add=True)
class Meta:
ordering = ('-created',)
def __str__(self):
return f'{self.name} 评论至 {self.post}'
使用
ImageField
需要安装Pillow
库编写之后使用
makemigrations
与migrate
进行迁移
django-taggit
pip install django-taggit
INSTALLED_APPS = ['taggit',..]
from taggit.managers import TaggableManager
tags = TaggableManager()
makemigrations
与migrate
.tags.all()
.tags.add()
>>> from blog.models import *
>>> import random
>>> tags = ['Python', 'Flask', 'Django', 'PHP','HTML','Java','MySQL','CSS','MongDB']
>>> for p in Post.objects.all():
... p.tags.add(*tuple(random.sample(tags, 4)))
... p.save()
random.sample(列表, 数量)
随机查找
*tuple()
变成元祖再解包
from taggit.models import Tag
@register.assignment_tag
def get_all_tags():
"""所有的标签项"""
return Tag.objects.all()
html显示所有标签
html显示相关数据
views.py
from django.db.models import Count
...
post_tags_ids =post.tags.values_list('id', flat=True) # 获取所有标签的id
similar_posts =Post.published_objects.filter(tags__in=post_tags_ids).exclude(id=post.id) #相似的日志
similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags','-publish')[:4] #显示相关程度最高的
return render(request, 'blog-detail.html', { 'post':post, 'similar_posts':similar_posts})
flat
扁平,为True得到列表,为False得到字典表
Post.published_objects.filter(tags__in=post_tags_ids)
从日志中找出所有文章,要求找出的标签在本篇文章的标签中
exclude(id=post.id)
找出标签但是排除掉这篇文章的id
similar_posts.annotate(same_tags=Count('tags'))
添加字段same_tags(相同标签的数量)
呈现
from django.contrib import admin
from .models import Category, Post, Comment
class CategoryAdmin(admin.ModelAdmin):
"""日志分类管理"""
list_display = ('name', 'slug', 'description')
class PostAdmin(admin.ModelAdmin):
"""日志管理"""
list_display = ('title', 'slug', 'Category', 'status', 'publish')
list_editable = ('status',)
search_fields = ('title', 'body')
date_hierarchy = 'publish'
list_filter = ('Category', 'status', 'publish')
list_per_page = 20
class CommentAdmin(admin.ModelAdmin):
"""日志评论管理"""
list_display = ('name', 'post', 'email', 'active', 'created')
list_per_page = 20
#注册博客模块模型管理
admin.site.register(Category, CategoryAdmin)
admin.site.register(Post, PostAdmin)
admin.site.register(Comment, CommentAdmin)
确保当前的app已经注册到
setting.py
中的INSTALLED_APPS
里
setting.py
中自定义媒体根目录MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
models.py
中指定图片目录image = models.ImageField('图片', null=True, blank=True, upload_to="uploads/")
from django.shortcuts import render
from django.http import HttpResponse
from .models import Category, Post, Comment
def blog_list(request):
"""博客列表页"""
posts = Post.objects.all()
return render(request, 'blog-list.html', {'posts':posts})
使用extends
与block
继承模板
配置url地址规则
urlpatterns =[url(r'^$', view=views.blog_list, name='blog_list'),..]
from django.conf.urls import include
urlpatterns = [ url(r'^blog/', include('blog.urls', namespace='blog', app_name='blog')),...]
在前台页面使用url反向解析地址
博客
{% for post in posts %}..{% endfor %}
遍历出单条数据{{post.title}}
获取标题{{ post.author.username }}
获取作者名称{{ post.Category.name }}
获取分类名称{post.publish|date:'m'
通过date过滤器显示月份{post.publish|date:'d'
通过date过滤器显示日{{ post.comments.count }}
显示出评论数{{ post.body|truncatewords:20 }}
显示正文前20个字符truncatewords
:截取{% if post.image %}
{% url 'blog:blog_detail' %}
绑定地址正常情况下我们过滤可以用filter进行操作如posts = Post.objects.filter(static='published')
,但在需要重复相同过滤时就不太方便,所以面对这种情况市我们可以使用Manager封装修改对象管理器
class PostPublishedManager(models.Manager):
"""
博客日志管理器
过滤状态为 published 日志
"""
def get_queryset(self):
return super(PostPublishedManager, self).get_queryset().filter(status='published')
class Post(models.Model)
......
published_objects = PostPublishedManager()
get_queryset()
获取查询集- 用
super(自己的类名, self)
调用基类,再进行过滤
之后再去要进行相同过滤时就可以直接使用,如posts = Post.published_objects.all()
from django.shortcuts import render, get_object_or_404
from .models import Category, Post, Comment
def blog_detail(request, slug):
"""博客日志详情页"""
post = get_object_or_404(Post, slug=slug)
return render(request, 'blog-detail.html', { 'post':post})
urlpatterns =[ url(r'^(?P[-\w]+)/$', view=views.blog_detail, name='blog_detail'),..]
\w
表示字符串
{% url 'blog:blog_detail' post.slug %}
跳转到正文def blog_detail(request, year, month, day, slug):
"""博客日志详情页"""
post = get_object_or_404(Post,
slug=slug,
publish__year=year,
publish__month=month,
publish__day=day,
status='published')
return render(request, 'blog-detail.html', { 'post':post})
publish__year
:为publish发布时间的年份
URL修改
url(r'^(?P
\d
:为数字
(?P
传入月份的值,值为1至2位的数字\d{1,2}
前台列表页面转跳显示
{% url 'blog:blog_detail' post.pblish|date:'Y' post.pblish|date:'m' post.pblish|date:'d' post.slug %}
from django.shortcuts import reverse
....
class Post(models.Model):
"""日志文章"""
.....
def get_absolute_url(self):
"""获取反向解析当前实例的URL"""
return reverse('blog:blog_detail', args=[
self.publish.year,
self.publish.month,
self.publish.day,
self.slug,
])
get_absolute_url()
获取当前实例类型的绝对地址
{{ post.get_absolute_url }}
在setting.py中配置MEDIA_URL
(访问用的)
MEDIA_URL ='/media/'
在项目urls.py中配置映射,如果有media的请求时当作静态文件处理
from django.conf import settings #导入项目下的setting.py
from django.conf.urls import static #导入把静态文件当作静态资源的函数
urlpatterns += static.static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) #setting(访问的地址, document_root=访问的路径)
对已有的urlpatterns进行更新,使用static通过加等于将新规则拼接到urlpatterns中
前台呈现
当请求/media/路径时就按urls设置呈现
tinymce
pip install django-tinymce
INSTALLED_APPS = ['tinymce',...]
urlpatterns = [..., url(r'^tinymce/', include('tinymce.urls')),]
from tinymce.models import HTMLField
body = HTMLField(‘正文’)
class PostAdmin(admin.ModelAdmin):
....
class Media():
js = (
'/static/js/tinymce/tinymce.min.js',
'/static/js/tinymce/custom.js',
)
8.前台显示
{{post.body|safe}}
{{ post.body|striptags }}
在shell中插入数据
>>>from django.contrib.auth.models import User
>>> from blog.models import *
>>> import random
>>> u = User.objects.first()
>>> img_p = Post.objects.get(pk=4)
>>> for i in range(1,33):
... p = Post()
... p.title = f'测试日志标题-{i}'
... p.slug = f'test-slut-{i}'
... p.category = Category.objects.get(pk=random.randint(1,3))
... p.author =u
... p.image = img_p.image
... p.body = f'测试日志文章正文{i}。'*20
... p.status = 'published'
... p.save()
random.randint(1,3)
生成1至3的随机数
from django.core.paginator import Paginator
p = Paginator(分页的列表,每页几条数据)
p.num_pages
分页的数量p.page_range
分页的序列如range(1,5)p.number
当前页p.page(1)
获取第一页p.page(1).object_list
第一页的数据列表p.page(1).has_next()
是否有下一页p.page(2).has_previous()
是否有前一页p.page(2).previous_page_number()
前一页的页码p.page(2).next_page_number()
下一页的页码p.page(2).start_index()
从第二页往后的信息从3开始p.page(2).end_index()
从第二页往后的信息以几结束from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def blog_list(request):
"""博客列表页"""
object_list = Post.published_objects.all()
paginator = Paginator(object_list, 5)
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.poge(paginator.num_pages)
return render(request, 'blog-list.html', {'page': page, 'posts':posts 'paginator':paginator})
EmptyPage
:空的页面
PageNotAnInteger
:页码不符合整数要求
html
多页面引用
{% include "pagination.html" with objests = posts ... %}
with 为新页面赋予新值
侧边栏日志分类链接呈现
示例
categories = Category.objects.all()
return render(request, 'blog-detail.html', { 'post':post, 'categories':categories})
html
simple_tag
from django import template
from blog.models import Post
register = template.Library()
@register.simple_tag
def total_posts():
"""自定义简单标签"""
return Post.published_objects.count()
{% load blog_tags %}
日志分类{% total_posts %}
inclusion_tag
@register.inclusion_tag('latest_posts.html')
def show_latest_posts(count=5):
"""自定义包含标签"""
latest_posts = Post.published_objects.order_by('-publish')[:count]
return {'latest_posts':latest_posts}
latest_posts.html
{% load blog_tags %}
{% show_latest_posts 3 %}
assignment_tag
@register.assignment_tag
def get_all_categories():
"""赋值标签"""
return Category.objects.all()
{% load blog_tags %}