django之深入模型

Django是基于MVC架构的Web框架,MVC架构追求的是“模型”和“视图”的解耦合。所谓“模型”说得更直白一些就是数据(的表示),所以通常也被称作“数据模型”。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在过去和当下都是持久化的首选方案,下面我们通过完成一个投票项目来讲解和模型相关的知识点。投票项目的首页会展示某在线教育平台所有的学科;点击学科可以查看到该学科的老师及其信息;用户登录后在查看老师的页面为老师投票,可以投赞成票和反对票;未登录的用户可以通过登录页进行登录;尚未注册的用户可以通过注册页输入个人信息进行注册。在这个项目中,我们使用MySQL数据库来实现数据持久化操作。

创建项目和应用

我们首先创建Django项目vote并为其添加虚拟环境和依赖项。接下来,在项目下创建名为polls的应用和保存模板页的文件夹tempaltes。

根据上面描述的项目需求,我们准备了四个静态页面,分别是展示学科的页面subjects.html,显示学科老师的页面teachers.html,登录页面login.html,注册页面register.html,稍后我们会将静态页修改为Django项目所需的模板页。

配置关系型数据库MySQL

1.在MySQL中创建数据库,创建用户,授权用户访问该数据。

create database vote default charset utf8;
create user 'hellokitty'@'%' identified by 'Hellokitty.618';
grant all privileges on vote.* to 'hellokitty'@'%';
flush privileges;

为了将这些命令放在一个执行文件中而不是一行一行地执行,可以创建一个SQL脚本文件。以下是步骤:

  1. 创建一个新的SQL文件,比如 setup_database.sql
  2. 将这些SQL命令写入文件中。
  3. 使用MySQL命令行工具或其他MySQL客户端执行该脚本文件。

使用MySQL命令行工具来执行这个脚本文件:mysql -u root -p < setup_database.sql

2.在MySQL中创建保存学科和老师信息的二维表(保存用户信息的表稍后处理)。

use vote;

-- 创建学科表
create table `tb_subject`
(
	`no` integer auto_increment comment '学科编号',
    `name` varchar(50) not null comment '学科名称',
    `intro` varchar(1000) not null default '' comment '学科介绍',
    `is_hot` boolean not null default 0 comment '是不是热门学科',
    primary key (`no`)
);
-- 创建老师表
create table `tb_teacher`
(
    `no` integer auto_increment comment '老师编号',
    `name` varchar(20) not null comment '老师姓名',
    `sex` boolean not null default 1 comment '老师性别',
    `birth` date not null comment '出生日期',
    `intro` varchar(1000) not null default '' comment '老师介绍',
    `photo` varchar(255) not null default '' comment '老师照片',
    `gcount` integer not null default 0 comment '好评数',
    `bcount` integer not null default 0 comment '差评数',
    `sno` integer not null comment '所属学科',
    primary key (`no`),
    foreign key (`sno`) references `tb_subject` (`no`)
);

这种情况也可以写在一个sql文件中一次性执行

 3.在虚拟环境中安装连接MySQL数据库所需的依赖项。

pip install mysqlclient

4.修改项目的settings.py文件,首先将我们创建的应用polls添加已安装的项目(INSTALLED_APPS)中,然后配置MySQL作为持久化方案。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls',
]

DATABASES = {
    'default': {
        # 数据库引擎配置
        'ENGINE': 'django.db.backends.mysql',
        # 数据库的名字
        'NAME': 'vote',
        # 数据库服务器的IP地址(本机可以写localhost或127.0.0.1)
        'HOST': 'localhost',
        # 启动MySQL服务的端口号
        'PORT': 3306,
        # 数据库用户名和口令
        'USER': 'hellokitty',
        'PASSWORD': 'Hellokitty.618',
        # 数据库使用的字符集
        'CHARSET': 'utf8',
        # 数据库时间日期的时区设定
        'TIME_ZONE': 'Asia/Chongqing',
    }
}

5.Django框架提供了ORM来解决数据持久化问题,ORM翻译成中文叫“对象关系映射”。因为Python是面向对象的编程语言,我们在Python程序中使用对象模型来保存数据,而关系型数据库使用关系模型,用二维表来保存数据,这两种模型并不匹配。使用ORM是为了实现对象模型到关系模型的双向转换,这样就不用在Python代码中书写SQL语句和游标操作,因为这些都会由ORM自动完成。利用Django的ORM,我们可以直接将刚才创建的学科表和老师表变成Django中的模型类。

python manage.py inspectdb > polls/models.py

我们可以对自动生成的模型类稍作调整,代码如下所示。

from django.db import models


class Subject(models.Model):
    no = models.AutoField(primary_key=True, verbose_name='编号')
    name = models.CharField(max_length=50, verbose_name='名称')
    intro = models.CharField(max_length=1000, verbose_name='介绍')
    is_hot = models.BooleanField(verbose_name='是否热门')

    class Meta:
        managed = False
        db_table = 'tb_subject'


class Teacher(models.Model):
    no = models.AutoField(primary_key=True, verbose_name='编号')
    name = models.CharField(max_length=20, verbose_name='姓名')
    sex = models.BooleanField(default=True, verbose_name='性别')
    birth = models.DateField(verbose_name='出生日期')
    intro = models.CharField(max_length=1000, verbose_name='个人介绍')
    photo = models.ImageField(max_length=255, verbose_name='照片')
    good_count = models.IntegerField(default=0, db_column='gcount', verbose_name='好评数')
    bad_count = models.IntegerField(default=0, db_column='bcount', verbose_name='差评数')
    subject = models.ForeignKey(Subject, models.DO_NOTHING, db_column='sno')

    class Meta:
        managed = False
        db_table = 'tb_teacher'

说明:模型类都直接或间接继承自Model类,模型类跟关系型数据库的二维表对应,模型对象跟表中的记录对应,模型对象的属性跟表中的字段对应。如果对上面模型类的属性定义不是特别理解,可以看看本文后面提供的“模型定义参考”部分的内容。

 使用ORM完成模型的CRUD操作

有了Django框架的ORM,我们可以直接使用面向对象的方式来实现对数据的CRUD(增删改查)操作。我们可以在PyCharm的终端中输入下面的命令进入到Django项目的交互式环境,然后尝试对模型的操作。

python manage.py shell
新增
from polls.models import Subject

subject1 = Subject(name='Python全栈开发', intro='当下最热门的学科', is_hot=True)
subject1.save()
subject2 = Subject(name='全栈软件测试', intro='学习自动化测试的学科', is_hot=False)
subject2.save()
subject3 = Subject(name='JavaEE分布式开发', intro='基于Java语言的服务器应用开发', is_hot=True)
删除
subject = Subject.objects.get(no=2)
subject.delete()
更新
subject = Subject.objects.get(no=1)
subject.name = 'Python全栈+人工智能'
subject.save()
查询

高级查询。

# 查询编号为1的学科的老师
Teacher.objects.filter(subject__no=1)
Subject.objects.get(pk=1).teacher_set.all() 

# 查询学科名称有“全栈”二字的学科的老师
Teacher.objects.filter(subject__name__contains='全栈') 

说明1:由于老师与学科之间存在多对一外键关联,所以能通过学科反向查询到该学科的老师(从一对多关系中“一”的一方查询“多”的一方),反向查询属性默认的名字是类名小写_set(如上面例子中的teacher_set),当然也可以在创建模型时通过ForeingKeyrelated_name属性指定反向查询属性的名字。如果不希望执行反向查询可以将related_name属性设置为'+'或者以'+'开头的字符串。 

说明2:ORM查询多个对象时会返回QuerySet对象,QuerySet使用了惰性查询,即在创建QuerySet对象的过程中不涉及任何数据库活动,等真正用到对象时(对QuerySet求值)才向数据库发送SQL语句并获取对应的结果,这一点在实际开发中需要引起注意! 

 说明3:如果希望更新多条数据,不用先逐一获取模型对象再修改对象属性,可以直接使用QuerySet对象的update()方法一次性更新多条数据。

利用Django后台管理模型

在创建好模型类之后,可以通过Django框架自带的后台管理应用(admin应用)实现对模型的管理。虽然实际应用中,这个后台可能并不能满足我们的需求,但是在学习Django框架时,我们可以利用admin应用来管理我们的模型,同时也通过它来了解一个项目的后台管理系统需要哪些功能。使用Django自带的admin应用步骤如下所示。

1.将admin应用所需的表迁移到数据库中。admin应用本身也需要数据库的支持,而且在admin应用中已经定义好了相关的数据模型类,我们只需要通过模型迁移操作就能自动在数据库中生成所需的二维表。

python manage.py migrate

2.创建访问admin应用的超级用户账号,这里需要输入用户名、邮箱和口令。

python manage.py createsuperuser

3. 运行项目,在浏览器中访问http://127.0.0.1:8000/admin,输入刚才创建的超级用户账号和密码进行登录。登录后进入管理员操作平台。

注意,我们暂时还没能在admin应用中看到之前创建的模型类,为此需要在polls应用的admin.py文件中对需要管理的模型进行注册。

4.注册模型类。

from django.contrib import admin

from polls.models import Subject, Teacher

admin.site.register(Subject)
admin.site.register(Teacher)

注册模型类后,就可以在后台管理系统中看到它们。

5.对模型进行CRUD操作。

可以在管理员平台对模型进行C(新增)、R(查看)、U(更新)、D(删除)操作。

6.注册模型管理类。

在后台查看部门信息的时候,显示的部门信息并不直观,为此我们再修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。

from django.contrib import admin

from polls.models import Subject, Teacher


class SubjectModelAdmin(admin.ModelAdmin):
    list_display = ('no', 'name', 'intro', 'is_hot')
    search_fields = ('name', )
    ordering = ('no', )


class TeacherModelAdmin(admin.ModelAdmin):
    list_display = ('no', 'name', 'sex', 'birth', 'good_count', 'bad_count', 'subject')
    search_fields = ('name', )
    ordering = ('no', )


admin.site.register(Subject, SubjectModelAdmin)
admin.site.register(Teacher, TeacherModelAdmin)

为了更好的查看模型,我们为Subject类添加__str__魔法方法,并在该方法中返回学科名字。这样在如上图所示的查看老师的页面上显示老师所属学科时,就不再是Subject object(1)这样晦涩的信息,而是学科的名称。

实现学科页和老师页效果

1.修改polls/views.py文件,编写视图函数实现对学科页和老师页的渲染。

from django.shortcuts import render, redirect

from polls.models import Subject, Teacher


def show_subjects(request):
    subjects = Subject.objects.all().order_by('no')
    return render(request, 'subjects.html', {'subjects': subjects})


def show_teachers(request):
    try:
        sno = int(request.GET.get('sno'))
        teachers = []
        if sno:
            subject = Subject.objects.only('name').get(no=sno)
            teachers = Teacher.objects.filter(subject=subject).order_by('no')
        return render(request, 'teachers.html', {
            'subject': subject,
            'teachers': teachers
        })
    except (ValueError, Subject.DoesNotExist):
        return redirect('/')

2.修改templates/subjects.htmltemplates/teachers.html模板页。

subjects.html

 

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>学科信息</title>
   <style>
       #container {
           width: 80%;
           margin: 10px auto;
       }
       .user {
           float: right;
           margin-right: 10px;
       }
       .user>a {
           margin-right: 10px;
       }
       #main>dl>dt {
           font-size: 1.5em;
           font-weight: bold;
       }
       #main>dl>dd {
           font-size: 1.2em;
       }
       a {
           text-decoration: none;
           color: darkcyan;
       }
   </style>
</head>
<body>
   <div id="container">
       <div class="user">
           <a href="login.html">用户登录</a>
           <a href="register.html">快速注册</a>
       </div>
       <h1>扣丁学堂所有学科</h1>
       <hr>
       <div id="main">
           {% for subject in subjects %}
           <dl>
               <dt>
                   <a href="/teachers/?sno={{ subject.no }}">{{ subject.name }}</a>
                   {% if subject.is_hot %}
                   <img src="/static/images/hot-icon-small.png">
                   {% endif %}
               </dt>
               <dd>{{ subject.intro }}</dd>
           </dl>
           {% endfor %}
       </div>
   </div>
</body>
</html>

teachers.html

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师信息</title>
    <style>
        #container {
            width: 80%;
            margin: 10px auto;
        }
        .teacher {
            width: 100%;
            margin: 0 auto;
            padding: 10px 0;
            border-bottom: 1px dashed gray;
            overflow: auto;
        }
        .teacher>div {
            float: left;
        }
        .photo {
            height: 140px;
            border-radius: 75px;
            overflow: hidden;
            margin-left: 20px;
        }
        .info {
            width: 75%;
            margin-left: 30px;
        }
        .info div {
            clear: both;
            margin: 5px 10px;
        }
        .info span {
            margin-right: 25px;
        }
        .info a {
            text-decoration: none;
            color: darkcyan;
        }
    </style>
</head>
<body>
    <div id="container">
        <h1>{{ subject.name }}学科的老师信息</h1>
        <hr>
        {% if not teachers %}
            <h2>暂无该学科老师信息</h2>
        {% endif %}
        {% for teacher in teachers %}
        <div class="teacher">
            <div class="photo">
                <img src="/static/images/{{ teacher.photo }}" height="140" alt="">
            </div>
            <div class="info">
                <div>
                    <span><strong>姓名:{{ teacher.name }}</strong></span>
                    <span>性别:{{ teacher.sex | yesno:'男,女' }}</span>
                    <span>出生日期:{{ teacher.birth | date:'Y年n月j日'}}</span>
                </div>
                <div class="intro">{{ teacher.intro }}</div>
                <div class="comment">
                    <a href="">好评</a>&nbsp;(<strong>{{ teacher.good_count }}</strong>)
                    &nbsp;&nbsp;&nbsp;&nbsp;
                    <a href="">差评</a>&nbsp;<strong>{{ teacher.bad_count }}</strong>)
                </div>
            </div>
        </div>
        {% endfor %}
        <a href="/">返回首页</a>
    </div>
</body>
</html>

3.修改vote/urls.py文件,实现映射URL。

from django.contrib import admin
from django.urls import path

from polls.views import show_subjects, show_teachers

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', show_subjects),
    path('teachers/', show_teachers),
]

到此为止,页面上需要的图片(静态资源)还没有能够正常展示。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值