django项目实战vCard博客

文章目录

    • 准备
      • 项目创建
      • app 程序准备
    • 留言簿
      • 留言簿后台管理
      • 留言簿前台提交
    • 前台HTML与视图整合
      • templates 模板呈现
      • static 静态资源文件管理
      • 模板继承
    • 博客功能实现
      • 模型编写
        • 标签`django-taggit`
        • 标签实现相关日志功能
      • 启用数据模型的后台管理
        • 图片上传
      • 前端页面数据绑定
        • 日志列表视图配置
        • 数据绑定
        • 自定义Manager封装
        • 日志详情页
        • URL地址多样化配置
        • 日志图片显示
        • tinymce富文本编辑器整合
        • 分页
        • 侧边栏
      • 自定义标签及过滤器
        • 简单标签`simple_tag`
        • 包含标签`inclusion_tag`
        • 赋值标签`assignment_tag`


准备

项目创建

  1. 创建虚拟环境
    python -m venv venv_vcard
  2. 启动虚拟环境
    cd venv_vcard\scripts\activate
  3. 安装django模块(1.11.4)
    `pip install django’

安装固定版本’pip install django==1.11.4’

  1. 项目创建
    django-admin startproject vcard

  2. 配置settings.py

  • LANGUAGE_CODE = 'zh-hans'
  • TIME_ZONE = 'Asia/Shanghai'
  1. 启动服务器
    python manage.py runserver

app 程序准备

  1. 创建app
    python manage.py startapp guestbook
  2. setting.py里注册app
    INSTALLED_APPS = [...,'guestbook','blog',]
  3. 进行数据库迁移
  • python manage.py migrate
  1. 创建超级用户
    python manage.py createsuperuser

添加 apps 目录
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

留言簿

留言簿后台管理

  • 定义留言消息模型Massage
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)
  • 数据迁移
    1. 创建迁移
      python manage.py makemigrations guestbook
    2. 执行迁移
      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'))
  • 在主模块下添加url
...
from guestbook import views as gb_views
urlpatterns = [
    url(r'^post-message/$', view=gb_views.post_message, name='post_message'),
    ...
]

  • 在前台form表单中的添加提交地址与请求方式,并添加防止跨站点伪造
    注意首页提交留言的字段id与name要保持与视图中获取的一致
{% csrf_token %} ....

前台HTML与视图整合

templates 模板呈现

  1. 在项目下创建templates文件夹,将前台模板复制在此路径下
  2. setting.py下进行配置
    TEMPLATES = [ {'DIRS': [os.path.join(BASE_DIR, 'templates'),],..}]
  • 在项目下的’views.py’创建视图
from django.shortcuts import render, redirect, reverse

def home(request):
    """vCard首页"""
    return render(request, 'index.html')
  • urls.py里添加url规则
    1. 导入视图from . import views
    2. 添加url规则 urlpatterns = [ url(r'^$', view=views.home, name='home'),..]

static 静态资源文件管理

默认从app目录下查找static作为静态资源文件目录

  1. 在项目下创建static文件夹,将 静态资源复制在此路径下
  2. setting.py里手动配置静态资源目录,指定使用项目下的static
    STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),]
  3. 模板页呈现静态资源
  • 方法一:在所有静态资源链接前添加/static/ 引用资源位置
  • 方法二:使用static指令
    1. 在网页前端添加{% load static %} # 载入静态资源
    2. 将静态资源链接改为 由static指令自动转换呈现资源相对位置

模板继承

  1. 创建模板页’base.html’,在标题、正文部分使用block占位
    {% block content %} {% endblock content %}
  2. 在继承页继承
  3. {% extends "base.html" %}
  4. {% 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

编写之后使用makemigrationsmigrate进行迁移

标签django-taggit

  1. 安装pip install django-taggit
  2. 在setting.py中注册INSTALLED_APPS = ['taggit',..]
  3. 在models中增加tags字段
    from taggit.managers import TaggableManager
    tags = TaggableManager()
  4. 迁移:使用makemigrationsmigrate
  5. 使用tags
  • 查询所有:.tags.all()
  • 添加标签:.tags.add()
  1. 添加测试数据
>>> 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()变成元祖再解包

  • 示例-使用赋值标签呈现
    blog_tags.py
from taggit.models import Tag

@register.assignment_tag
def get_all_tags():
    """所有的标签项"""
    return Tag.objects.all()

html显示所有标签

标签云集

{% get_all_tags as all_tags %}
{% for tag in all_tags %} {{tag}} {% endfor %}

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/")

前端页面数据绑定

日志列表视图配置

  • 添加views视图
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})
  • 使用extendsblock继承模板

  • 配置url地址规则

    • 在app下创建urls并配置
      urlpatterns =[url(r'^$', view=views.blog_list, name='blog_list'),..]
      • 在项目的urls里注册
        from django.conf.urls import include
        urlpatterns = [ url(r'^blog/', include('blog.urls', namespace='blog', app_name='blog')),...]
  • 在前台页面使用url反向解析地址
    博客

数据绑定

  1. 通过{% for post in posts %}..{% endfor %}遍历出单条数据
  2. 通过{{post.title}}获取标题
  3. 通过{{ post.author.username }}获取作者名称
  4. 通过{{ post.Category.name }}获取分类名称
  5. 通过{post.publish|date:'m'通过date过滤器显示月份
  6. 通过{post.publish|date:'d'通过date过滤器显示日
  7. 通过{{ post.comments.count }}显示出评论数
  8. 通过{{ post.body|truncatewords:20 }}显示正文前20个字符
  • truncatewords:截取
  1. {% if post.image %}
{% endif %} 判断有图片时显示
  • 使用{% url 'blog:blog_detail' %}绑定地址
  • 自定义Manager封装

    正常情况下我们过滤可以用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()
    
    1. get_queryset()获取查询集
    2. super(自己的类名, self)调用基类,再进行过滤

    之后再去要进行相同过滤时就可以直接使用,如posts = Post.published_objects.all()

    日志详情页

    • view视图
    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})
    
    • urlurlpatterns =[ url(r'^(?P[-\w]+)/$', view=views.blog_detail, name='blog_detail'),..]

    \w表示字符串

    • 前台:与列表页类似
      1. {% url 'blog:blog_detail' post.slug %}跳转到正文

    URL地址多样化配置

    • 视图修改,将日期传入
    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{4})/(?P\d{1,2})/(?P\d{1,2})/(?P[-\w]+)/$', view=views.blog_detail, name='blog_detail'),

      \d :为数字
      (?P\d{1,2}传入月份的值,值为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 }}

    日志图片显示

    1. 在setting.py中配置MEDIA_URL(访问用的)
      MEDIA_URL ='/media/'

    2. 在项目urls.py中配置映射,如果有media的请求时当作静态文件处理

    3. from django.conf import settings #导入项目下的setting.py

    4. from django.conf.urls import static #导入把静态文件当作静态资源的函数

    5. urlpatterns += static.static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) #setting(访问的地址, document_root=访问的路径) 对已有的urlpatterns进行更新,使用static通过加等于将新规则拼接到urlpatterns中

    6. 前台呈现{{post.title}} 当请求/media/路径时就按urls设置呈现

    tinymce富文本编辑器整合

    tinymce

    1. 安装pip install django-tinymce
    2. 在setting.py中注册INSTALLED_APPS = ['tinymce',...]
    3. 在项目urls.py中配置地址urlpatterns = [..., url(r'^tinymce/', include('tinymce.urls')),]
    4. 在models.py中修改为HTMLField字段
      from tinymce.models import HTMLField
      body = HTMLField(‘正文’)
    5. 下载离线包或引用在线包
    6. 下载样式
    7. 引用静态资源
    class PostAdmin(admin.ModelAdmin):
        ....
    
        class Media():
            js = (
                '/static/js/tinymce/tinymce.min.js',
                '/static/js/tinymce/custom.js',
            )
    

    8.前台显示

    • 增加过滤器标注为安全显示html渲染{{post.body|safe}}
    • 除去html标签{{ 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的随机数

    1. 使用Paginator分页器
      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()从第二页往后的信息以几结束
    1. 示例
      views.py
    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

        
      {% if posts.has_previous %}
    • 上一页
    • {% endif %} {% for i in paginator.page_range %} {% if i == posts.number %}
    • {{i}}
    • {% else %}
    • {{i}}
    • {% endif %} {% endfor %} {% if posts.has_next %}
    • 上一页
    • {% endif %}

    多页面引用

    • 创建一个pagination.html文件将分页截切到此
    • 在列表页创建引用{% include "pagination.html" with objests = posts ... %}

      with 为新页面赋予新值

    侧边栏

    侧边栏日志分类链接呈现
    示例

      categories = Category.objects.all()
      return render(request, 'blog-detail.html', { 'post':post, 'categories':categories})
    

    html

    日志分类

    自定义标签及过滤器

    1. 代码布局
      app/templatetags/custom_tags.py

    简单标签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()
    
    1. 载入到前台网页{% load blog_tags %}
    2. 调用

      日志分类{% 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

    最新日志

    1. 载入到前台网页{% load blog_tags %}
    2. 调用{% show_latest_posts 3 %}

    赋值标签assignment_tag

    • 定义标签
    @register.assignment_tag
    def get_all_categories():
        """赋值标签"""
        return Category.objects.all()
    
    1. 载入到前台网页{% load blog_tags %}
    2. 调用

    日志分类

    {% get_all_categories as categories %}

    你可能感兴趣的:(Python,Web)