Django 初步完善图书管理系统

604 阅读8分钟

这是我参与更文挑战的第20天,活动详情查看: 更文挑战


引言

Django 模型设计 中我们只设计了一个 BookInfo 模型类,内容好单调,接下来我们初步完善一下BMSTes图书管理系统。


模型设计

我们写项目写东西的时候都要养成良好的习惯,不要一来就上手写代码,我们先要进行初步分析和设计,让大脑有整体的概念,需要用到什么技术实现什么效果。这个习惯也不能学的太死,我们要活学活用,学会变通。有时候只是简单的学习一下,或者简单的实现一个小功能。大脑已经可以大概掌握,就无须设计与分析。


初步分析、设计

我们图书管理系统主要实现的是 图书与图书英雄的关联、图书与图书类型的关联管理 ,因此分为图书信息模型、英雄信息模型、图书类型模型,三个模型。对应关系设计如下

一种图书类型,对应多本图书,例如:小说类型的图书,有射雕英雄传、神雕侠侣、倚天屠龙记小说图书等。

一本图书,对应多个英雄,例如:射雕英雄传图书,对应有着郭靖、黄蓉、洪七公等、射雕侠侣则对应杨过、小龙女等。

  • 图书类型模型与图书信息模型关联的是1对多的关系
  • 图书信息模型与英雄信息模型关联的是1对多的关系
实体模型1实体模型2对应关系
图书类型(BookType)图书信息(BookInfo)1 : N
图书信息(BookInfo)英雄信息(HeroInfo)1 : N

具体实现设计

BookType 图书类型模型类

类属性数据类型备注
idIntegerField(整型)主键、自增
type_nameCharField(字符型)图书类型

BookInfo 图书模型类

类属性数据类型备注
idIntegerField(整型)主键、自增
book_typeIntegerField(整型)ForeignKey图书类型,外键
titleCharField(字符型)图书名称
authorCharField(字符型)图书作者
pub_dateCharField(字符型)图书出版日期
isbnCharField(字符型)图书ISBN
book_descCharField(字符型)图书描述
book_detailTextField(文本类型)图书详情
book_priceDecimalField (数值类型)图书价格
book_like_numIntegerField(整型)图书喜欢数
book_collect_numIntegerField(整型)图书收藏数

HeroInfo 图书英雄模型类

类属性数据类型备注
idIntegerField(整型)主键自增
nameCharField(字符型)英雄名称
genderCharField(字符型)英雄性别
skillCharField(字符型)英雄技能(武功)
bookInterField(整型)Foreignkey英雄所属图书,外键

Django 模型代码

# -*- coding:utf-8 -*-
"""
@Author   :Hui
@Desc     :{模型设计模块}
"""
from django.db import models


class BookType(models.Model):
    """图书类别类"""

    type_name = models.CharField(verbose_name=u'图书类型', max_length=20)

    def __str__(self):
        return self.type_name

    class Meta:
        
        db_table = 'BookType'       # 表名称

        verbose_name = '图书类型'   # 表备注

        # 表名复数形式,如果不设置末尾会多一个s
        verbose_name_plural = verbose_name
        
        ordering = ['id']          # 排序字段


class BookInfo(models.Model):
    """图书模型类"""

    title = models.CharField(verbose_name=u'图书名称', max_length=20)

    author = models.CharField(verbose_name=u'图书作者', max_length=20)

    pub_date = models.DateField(verbose_name=u'出版日期')

    book_type = models.ForeignKey(BookType, on_delete=models.CASCADE, verbose_name=u'图书类型')

    isbn = models.CharField(verbose_name='ISBN', max_length=20)

    book_desc = models.CharField(verbose_name=u'图书描述', max_length=128)

    book_detail = models.TextField(verbose_name=u'图书详情')

    book_price = models.DecimalField(verbose_name=u'图书价钱', max_digits=5, decimal_places=2)

    book_like_num = models.IntegerField(verbose_name=u'图书喜欢量')

    book_collect_num = models.IntegerField(verbose_name=u'图书收藏量')

    def __str__(self):
        return self.title

    class Meta:
        db_table = 'BookInfo'
        verbose_name = u'图书信息'
        verbose_name_plural = verbose_name
        ordering = ['id']


class HeroInfo(models.Model):
    """英雄模型类"""
    name = models.CharField(verbose_name=u'英雄姓名', max_length=20)

    # 英雄性别
    gender = models.CharField(verbose_name=u'英雄性别', max_length=3)

    # 英雄技能(武功)
    skill = models.CharField(verbose_name=u'英雄武功', max_length=128)

    # 英雄所属图书
    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name=u'图书')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'HeroInfo'
        verbose_name = u'人物信息'
        verbose_name_plural = verbose_name
        ordering = ['id']

设计模型的时候出现了几个之前没用过的字段类型,在这里简单介绍一下。

  • TextField: 大文本字段,一般超过4000个字符时使用。
  • IntegerField: 整数。
  • DecimalField(max_digits=None, decimal_places=None): 十进制浮点数。
    • 参数max_digits表示总位数。
    • 参数decimal_places表示小数位数。

Meta类中的属性

  • db_table: 设置创建数据库表的名称,默认的是 应用名_模型类名
  • verbose_name: 设置数据库表的详细信息(表备注)
  • verbose_name_plural: 设置数据表的复数显示形式
  • ordering: 设置数据表的排序字段

字段、和Meta类中属性,以后再做详细介绍。


自定义后台管理

模型设计好了,接下来就是在 admin.py 下注册模型了,在注册模型中,我新增了 inlineslist_per_pagesearch_fields属性

  • inlines: 用于嵌入编辑关联的对象,属性设置在 一对多 模型(表)关系中的 中。

  • admin.TabularInline: 嵌入编辑关联的对象的编辑样式为表格式类

  • admin.StackedInline: 嵌入编辑关联的对象的编辑样式为堆放式类

  • model: 关联的子对象

  • extra: 嵌入编辑子对象的个数


  • list_per_page: 数据分页,每页的数据个数
  • search_fields: 查询字段

# -*- coding:utf-8 -*-
"""
@Author   :Hui
@Desc     :{后台管理模块}
"""
from django.contrib import admin
from book.models import BookType, BookInfo, HeroInfo


class BookTypeInline(admin.StackedInline):
    model = BookInfo    # 关联的子对象
    extra = 1           # 嵌入编辑子对象的个数


class BookInfoInline(admin.TabularInline):
    model = HeroInfo
    extra = 2


class BookTypeAdmin(admin.ModelAdmin):
    """图书类型模型管理类"""

    # 数据分页,每页10条
    list_per_page = 10

    # 后台显示的属性(字段)
    list_display = ['id', 'type_name']

    # 查询字段
    search_fields = ['type_name']

    # 嵌入编辑关联的对象(表格式)
    inlines = [BookTypeInline]


class BookInfoAdmin(admin.ModelAdmin):
    """图书信息模型管理类"""

    list_per_page = 20

    list_display = [
        'id', 'title', 'author', 'pub_date', 'book_desc', 'book_like_num', 'book_collect_num', 'book_type'
    ]

    search_fields = ['id', 'title', 'author', 'book_desc']

    # 设置图书作者、类型为过滤字段
    list_filter = ['author', 'book_type']

    # 嵌入编辑关联的对象(堆放式)
    inlines = [BookInfoInline]


class HeroInfoAdmin(admin.ModelAdmin):
    """英雄信息模型管理类"""
    list_display = ['id', 'name', 'skill', 'book']

    # 设置图书查询字段
    search_fields = ['name', 'skill', 'book']

    # 设置英雄图书过滤字段
    list_filter = ['book']


# 注册模型类
admin.site.register(BookType, BookTypeAdmin)
admin.site.register(BookInfo, BookInfoAdmin)
admin.site.register(HeroInfo, HeroInfoAdmin)


配置MySQL数据库

注册完了模型类,接下来就配置数据库,之前用的是 Django 自带的 sqlite3 数据库,现在我们换成 MySQL 数据库。

首先在 PyCharmTerminal 中输入命令安装 pymysql 驱动

pip install pymysql

然后在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',	# MySQL数据库
        # 'NAME': BASE_DIR / 'db.sqlite3',	# sqlite3数据库
        'NAME': 'BMSTest',          # 数据库名称
        'HOST': '127.0.0.1',        # 数据库地址,本机 ip 地址 127.0.0.1
        'PORT': 3306,               # 数据库端口
        'USER': 'root',             # 数据库用户名
        'PASSWORD': '123456',       # 数据库密码
    }
}

最后在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置

import pymysql

pymysql.version_info = (1, 4, 13, "final", 0)
pymysql.install_as_MySQLdb()

pymysql.version_info = (1, 4, 13, "final", 0) 这行代码

解决 django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required; you have 0.10.1. 这个错 搜索了下网上的解决办法要么升级要么降级 但是都觉得麻烦于是到处找能不能用几行代码解决的方法 最后成功在stackflow上找到一个办法在 settings.py 同级目录下的 __init__.py 插入这行代码就可以了。


运行服务器

  • 创建数据库迁移文件 python manage.py makemigrations
  • 执行数据库迁移文件 python manage.py migrate
  • 运行服务器 python manage.py runserver

如果在运行 python manage.py makemigrations 命令时出现了如下类似的情况

(py_django) D:\Hui\Code\Python\DjangoProject\BMSTest>python manage.py makemigrations
You are trying to add a non-nullable field 'book_type' to bookinfo without a default; we can't do that (the database needs something to populate ex
isting rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 

说明表结构与之前的表结构发生了变化,之前 BookInfo 的只有 titleauthorpub_date,现在的则多了 book_typeisbnbook_descbook_detail等属性。且这些属性没有 default 默认值,存在 null 值。


解决方案:

  • 1)选择1,则自己提供 default 值,输入 '' 空字符即可。

  • 2)选择2,在自己新增的属性中设置 default 值、或者允许属性(字段)为空 null,例如:

    book_desc = models.CharField(verbose_name=u'图书描述', max_length=128, null=True)
    
    book_detail = models.TextField(verbose_name=u'图书详情', null=True)
    
    book_price = models.DecimalField(verbose_name=u'图书价钱', max_digits=5, decimal_places=2, null=True)
    
    book_like_num = models.IntegerField(verbose_name=u'图书喜欢量', default=0)
    
    book_collect_num = models.IntegerField(verbose_name=u'图书收藏量', default=0)
    

    新增的属性则为默认值 default 、或者空值 null

  • 如果要 切换到其他数据库 时可以在应用下的 migrations 目录下删除之前创建的迁移文件然后在创建迁移文件即可。

    Django 数据库迁移文件目录


数据库结构

BMSTest 数据库结构


注意:如果切换了数据库,数据不会迁移过来,记得自己手动添加或者导入。


图书类型的分页效果图

图书类型的分页效果


图书信息的过滤效果图

图书信息的过滤属性效果图


嵌入编辑关联的对象(堆放式)

在编辑图书类型的时候,会在下面根据 extra 属性值内嵌入关联的对象(图书信息)的个数

嵌入编辑子对象(堆放式)


嵌入编辑关联的对象(表格式)

嵌入编辑关联的对象(表格式)


建议:

  • 如果关联的模型(表)属性较多,建议使用 堆放式(admin.StackedInline),且不要放置太多,建议1个或者不设置。
  • 如果关联的模型(表)属性较少,放置多些时建议 表格式(admin.TabularInline),少些时任意格式都无伤大雅。

这样就不会导致网页,从上到下滑动的太长、或者是从左到右滑动的太长,而不好编辑,不太美观。


公众号

新建文件夹X

大自然用数百亿年创造出我们现实世界,而程序员用几百年创造出一个完全不同的虚拟世界。我们用键盘敲出一砖一瓦,用大脑构建一切。人们把1000视为权威,我们反其道行之,捍卫1024的地位。我们不是键盘侠,我们只是平凡世界中不凡的缔造者 。