Django开发博客系统1——settings、model、url、admin

涉及知识点:

  1. 项目配置
  2. 后台管理

一.拆分settings.py以适应不同的运行环境

1.新建settings文件夹,添加__init__.py文件,将原settings.py移入并改为base.py,新建local.py对应开发配置文件,product.py对应部署时的配置

from .base import *    # NOQA

SECRET_KEY = 'o1j3$v+3@j@6k=6123(^_f-b&7s_k40nu86vu12udu8543j*86d'

DEBUG = True

ALLOWED_HOSTS = ['*']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

2.修改manage.py,对应python manage.py的运行方式

if __name__ == "__main__":
    # os.environ.setdefault("DJANGO_SETTINGS_MODULE", "OfferHelp.settings")
    profile = os.environ.get('OfferHelp_PROFILE', 'local')
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'OfferHelp.settings.%s' % profile)

3.修改wsgi.py,对应uwsgi运行项目时的配置

import os

from django.core.wsgi import get_wsgi_application

# uwsgi启动时加载product配置
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'OfferHelp.settings.product')

application = get_wsgi_application()

4.修改 BASE_DIR 配置项

# BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

5.git设置需要忽略的文件配置
本地仓库目录下新建文件:.gitignore,内容参考如下:

*.pyc
*.swp
*.sqlite3

二.模型层

from django.contrib.auth.models import User
from django.db import models

class Post(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_DRAFT = 2
    STATUS_ITEMS = (
        (STATUS_NORMAL, '正常'),
        (STATUS_DELETE, '删除'),
        (STATUS_DRAFT, '草稿'),
    )

    title = models.CharField(max_length=255, verbose_name="标题")
    desc = models.CharField(max_length=1024, blank=True, verbose_name="摘要")
    # help_text:后台管理中编辑页上的提示内容
    content = models.TextField(verbose_name="正文", help_text="正文必须为MarkDown格式")
    # 字段类型:正整数或0;choices:显示为一个下拉选择框
    status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS, verbose_name="状态")
    category = models.ForeignKey(Category, verbose_name="分类")
    tag = models.ManyToManyField(Tag, verbose_name="标签")
    owner = models.ForeignKey(User, verbose_name="作者")
    # 设置自动写入时间
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    pv = models.PositiveIntegerField(default=1)
    uv = models.PositiveIntegerField(default=1)

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = verbose_name_plural = '文章'
        # 根据id进行降序排列,通过模型查询时先显示最新的数据
        ordering = ['-id']
    
    # 在模型中定义数据查询的方法,项目初期尽量简化View层的逻辑
    # 避免后期维护麻烦,因为后续业务需求的调整大多发生在View层
    @classmethod
    def hot_posts(cls):
        return cls.objects.filter(status=cls.STATUS_NORMAL).order_by('-pv')

    @staticmethod
    def get_by_tag(tag_id):
        try:
            # get得到的Tag的实例对象,而filter得到的是查询结果集queryset(懒加载)
            tag = Tag.objects.get(id=tag_id)
        except Tag.DoesNotExist:
            tag = None
            post_list = []
        else:
            # 外键关联时(一对多或多对多),主表查从表,此方法必须由主表的实例调用,因此上方要用get方法
            # select_related()解决N+1问题,查询时会一并查出post中外键关联的owner和category字段,适用一对多
            # 多对多为prefetch_related()
            post_list = tag.post_set.filter(status=Post.STATUS_NORMAL).select_related('owner', 'category')
        return post_list, tag

    @staticmethod
    def get_by_category(category_id):
        try:
            category = Category.objects.get(id=category_id)
        except Category.DoesNotExist:
            post_list = []
            category = None
        else:
            post_list = category.post_set.filter(status=Post.STATUS_NORMAL)
        return post_list, category

在侧边栏模型中定义属性方法:根据类型在Model层直接渲染数据,最终返回渲染好的数据

from django.db import models
from django.template.loader import render_to_string

class SideBar(models.Model):
	# 省略其余代码
	
    @property
    # 直接渲染成模板交给前端,property:将一个方法封装成属性,使用.语法调用,参数只有self
    def content_html(self):
        from blog.models import Post    # 避免循环引用
        from comment.models import Comment
        result = ''
        if self.display_type == self.DISPLAY_HTML:
            result = self.content
        elif self.display_type == self.DISPLAY_LATEST:
            context = {
                'posts': Post.latest_posts(),
            }
            result = render_to_string('config/blocks/sidebar_posts.html', context)
        elif self.display_type == self.DISPLAY_HOT:
            context = {
                'posts': Post.hot_posts(),
            }
            result = render_to_string('config/blocks/sidebar_posts.html', context)
        elif self.display_type == self.DISPLAY_COMMENT:
            context = {
                'comments': Comment.objects.filter(status=Comment.STATUS_NORMAL),
            }
            result = render_to_string('config/blocks/sidebar_comments.html', context)
        return result

三.后台管理

在主应用文件夹下新建custom_site.py和base_admin.py(方便所有app调用)
1.custom_site.py:自定义站点,区分业务板块和用户管理板块

from django.contrib.admin import AdminSite

# 自定义site,让文章板块的功能指向这个站点:custom_site.urls
# 而用户管理的后台还是使用之前的site:admin.site.urls
class CustomSite(AdminSite):
    # 页面的展示信息
    site_header = 'Blogtype'    # body中显示的标题
    site_title = 'Blogtype管理后台'     # head中显示的标题
    index_title = '首页'        # head中显示的标题

# 创建自定义站点对象,name属性用于reverse反向解析url的地方
custom_site = CustomSite(name='cus_admin')

2.改写url

from blogidea.custom_site import custom_site
from django.conf.urls import url
from django.contrib import admin
from blog.views import *

urlpatterns = [
    url(r'^$', IndexView.as_view(), name='index'),
    url(r'post/(?P<post_id>\d+)/$', PostDetailView.as_view(), name='post-detail'),    # pk为查询过滤参数
    url(r'category/(?P<category_id>\d+)/$', CategoryView.as_view(), name='category-list'),
    url(r'tag/(?P<tag_id>\d+)/$', TagView.as_view(), name='tag-list'),
    url(r'^super_admin/', admin.site.urls, name='super-admin'),    # 管理用户,使用jango自带的site
    url(r'^admin/', custom_site.urls, name=admin),         # 管理业务,使用自定义的站点

base_admin.py:抽取Admin基类,封装重复的功能(自动保存owner字段和显示过滤)

from django.contrib import admin

class BaseOwnerAdmin(admin.ModelAdmin):
    """
    1.自动补充文章,分类,标签等编辑页面保存时自动保存owner字段
    2.针对queryset过滤当前用户的数据
    """
    exclude = ('owner', )    # 设置编辑页中不显示owner字段
    
    # 保存数据到数据库中时自动给owner字段赋值
    def save_model(self, request, obj, form, change):
        obj.owner = request.user
        return super(BaseOwnerAdmin, self).save_model(request, obj, form, change)
    
    # 设置列表页仅显示当前登陆用户的信息
    def get_queryset(self, request):
        qs = super(BaseOwnerAdmin, self).get_queryset(request)
        return qs.filter(owner=request.user)

admin.py
1.定义文章列表页的过滤器

# 自定义过滤器
class CategoryOwnerFilter(admin.SimpleListFilter):
    """自定义过滤器:在文章列表页右侧的过滤器只显示当前用户的分类(category)"""
    title = '分类过滤器'    # 过滤器名称,页面显示:以 分类过滤器
    parameter_name = 'owner_category'    # 过滤查询时URL的参数名

    # 返回过滤器要展示的内容(分类名)和查询用的id,此方法关乎过滤器的显示
    def lookups(self, request, model_admin):
        return Category.objects.filter(owner=request.user).values_list('id', 'name')

    # 根据选择的过滤参数过滤列表页的显示结果,此方法关乎列表页的显示
    def queryset(self, request, queryset):
        category_id = self.value()    # 获取过滤参数,category的id
        if category_id:
            # 过滤参数为category_id,不能是点语法
            return queryset.filter(category_id=category_id)
        return queryset

定义后台管理类,继承自BaseOwnerAdmin

# 装饰器参数:注册的实体类以及所属的站点
@admin.register(Post, site=custom_site)
class PostAdmin(BaseOwnerAdmin):
    # 修改编辑页面中form表单元素的样式
    form = PostAdminForm
    # operator 为自定义的显示字段
    list_display = ('title', 'status', 'category', 'owner', 'created_time', 'operator')
    # 定义可点击进入编辑页面的字段,不写的话默认为第一个
    list_display_links = []
    # 定义右侧过滤器的参考字段
    list_filter = [CategoryOwnerFilter]
    # 定义搜索字段,此处category为外键,双下划线指定关联Category类中的name字段
    search_fields = ['title', 'category__name']
    
	# date_hierarchy    列表页中增加一个时间过滤功能,必须是时间字段
	# list_editable     列表页中可以直接修改的字段
	
    # 动作相关的配置,是否展示在顶部和底部
    actions_on_top = True
    actions_on_bottom = True

    # 开启顶部的编辑按钮
    save_on_top = True

    # 编辑页显示的字段,与fields不可共存
    fieldsets = (
        ('基础配置', {
            'description': '基础配置',    # 标题下的描述
            'fields': (
                ('title', 'category'),
                'status',
            )
        }),
        ('内容', {
            'fields': (
                'desc',
                'content',
            )
        }),
        ('额外信息', {
            'classes': ('collapse',),    # 控制显示和隐藏
            'fields': ('tag',)
        })
    )
    # 针对多对多字段展示的配置,设置字段横向或纵向展示
    filter_horizontal = ('tag',)

    # 定义 自定义显示字段的方法,返回值显示在列表页中
    def operator(self, obj):
        return format_html(
            '<a href="{}">编辑</a>', reverse('cus_admin:blog_post_change', args=(obj.id,))
        )
    # 显示自定义显示字段的名称
    operator.short_description = '操作'

    # 向页面中添加css和js,如果是项目本身的静态资源,直接写名称即可
    # class Media:
    #     css = {
    #         'all': ('http://cdn.bootcss.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css',),
    #     }
        # js = ('https://cdn.jsdelivr.net/gh/bootcdn/BootCDN/ajax/libs/bootstrap/4.0.0-beta.2/js/bootstrap.bundle.js',)


# 注册后台管理,指定要注册的实体类和对应的站点,后台管理的内容会通过url后指定的站点进行区分
@admin.register(Category, site=custom_site)
class CategoryAdmin(BaseOwnerAdmin):
    # 设置列表页显示的字段,其中post_count为自定义字段
    list_display = ('name', 'status', 'is_nav', 'owner', 'created_time', 'post_count')
    # 编辑页显示的字段
    fields = ('name', 'status', 'is_nav')

    # 自定义列表页显示字段:该分类下的博客数量
    def post_count(self, obj):
        # 一对多关系时,主表查询关联子表的数据,查询语法:主表对象.从表小写_set.过滤器方法
        return obj.post_set.count()
    # 列表页自定义字段名
    post_count.short_description = '文章数量'

自定义Form,设置编辑页面的展示效果
在对应app下新建adminforms.py,在PostAdmin中进行引用

from django import forms

# 自定义编辑页面中表单元素的样式,将输入框设置显示为多行文本域
class PostAdminForm(forms.ModelForm):
    desc = forms.CharField(widget=forms.Textarea, label='摘要', required=False)

参考:《Django企业开发实战》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值