[实验] Django 自定义逻辑添加 (通过自定义 manager 管理器实现) (一次性完成多对多表数据的插入)

注意:

本实验是接着 《Django 数据展示(多对多版)》而继续的

正文:

步骤一:在没有自定义 manager 管理器的情况下尝试一次性完成多对多表数据的插入

1.1 进入相应的 Django 环境

>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from user.models import *

1.2 在没有自定义 manager 管理器的情况下尝试一次性完成多对多表数据的插入

>>> Student.objects.create(sname='lili',clazz='Class1',course=('Python','Shell'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/zhumingyu/Code/Django/mtwo/django_env/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/zhumingyu/Code/Django/mtwo/django_env/lib/python3.8/site-packages/django/db/models/query.py", line 451, in create
    obj = self.model(**kwargs)
  File "/Users/zhumingyu/Code/Django/mtwo/django_env/lib/python3.8/site-packages/django/db/models/base.py", line 503, in __init__
    raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg))
TypeError: Student() got an unexpected keyword argument 'clazz'


补充:
(1)这里数据插入失败
(2)按正常顺序应该是先插入班级 clazz,之后再插入学生 student,之后再插入课程 course,之后再去插入中间表

1.3 退出相应的 Django 环境

>>> quit();

步骤二:确认要插入的数据和预期的一致(可选)

2.1 创建 1 个自定义 manager 管理器的类

将 mysite/test/models.py 中的以下内容:

......
class Clazz(models.Model):
    cno = models.AutoField(primary_key=True)
    cname = models.CharField(max_length=30)

    def __str__(self):
        return u'Clazz:%s'%self.cname

class Course(models.Model):
    course_no = models.AutoField(primary_key=True)
    course_name = models.CharField(max_length=30)

    def __str__(self):
        return u'Course:%s'%self.course_name

class Student(models.Model):
    sno = models.AutoField(primary_key=True)
    sname = models.CharField(max_length=30)
    cls = models.ForeignKey(Clazz,on_delete=models.CASCADE)
    cour = models.ManyToManyField(Course)

    def __str__(self):
        return u'Student:%s'%self.sname

# Get class object according to class name
def getCls(cname):
    try:
        cls = Clazz.objects.get(cname=cname)
    except Clazz.DoesNotExist:
        cls = Clazz.objects.create(cname=cname)
    return cls

def getCourseList(*coursenames):
    courseList = []

    for cn in coursenames:
        try:
            c = Course.objects.get(course_name=cn)
        except Course.DoesNotExist:
            c = Course.objects.create(course_name=cn)
        courseList.append(c)

    return courseList

def registerStu(sname,cname,*coursenames):
    #1. Get class objects
    cls = getCls(cname)
    #2. Gets the list of course objects
    courseList = getCourseList(*coursenames)
    #3. Insert student table data
    try:
        stu = Student.objects.get(sname=sname)
    except Student.DoesNotExist:
        stu = Student.objects.create(sname=sname,cls=cls)
    #4. Insert intermediate table data
    stu.cour.add(*courseList)

    return True

修改为:

......
from django.db import models
from django.db.models.manager import Manager

# Create your models here.
class Clazz(models.Model):
    cno = models.AutoField(primary_key=True)
    cname = models.CharField(max_length=30)

    def __str__(self):
        return u'Clazz:%s'%self.cname

class Course(models.Model):
    course_no = models.AutoField(primary_key=True)
    course_name = models.CharField(max_length=30)

    def __str__(self):
        return u'Course:%s'%self.course_name

class CustomManager(Manager):
    def create(self, **kwargs):
        print(kwargs)

class Student(models.Model):
    sno = models.AutoField(primary_key=True)
    sname = models.CharField(max_length=30)
    cls = models.ForeignKey(Clazz,on_delete=models.CASCADE)
    cour = models.ManyToManyField(Course)

    objects = CustomManager()

    def __str__(self):
        return u'Student:%s'%self.sname

# Get class object according to class name
def getCls(cname):
    try:
        cls = Clazz.objects.get(cname=cname)
    except Clazz.DoesNotExist:
        cls = Clazz.objects.create(cname=cname)
    return cls

def getCourseList(*coursenames):
    courseList = []

    for cn in coursenames:
        try:
            c = Course.objects.get(course_name=cn)
        except Course.DoesNotExist:
            c = Course.objects.create(course_name=cn)
        courseList.append(c)

    return courseList

def registerStu(sname,cname,*coursenames):
    #1. Get class objects
    cls = getCls(cname)
    #2. Gets the list of course objects
    courseList = getCourseList(*coursenames)
    #3. Insert student table data
    try:
        stu = Student.objects.get(sname=sname)
    except Student.DoesNotExist:
        stu = Student.objects.create(sname=sname,cls=cls)
    #4. Insert intermediate table data
    stu.cour.add(*courseList)

    return True

(补充:这里以添加一个显示自己所有键值对的 CustomManager(Manager) 对象并在 Student 类中使用 objects 调用为例)

2.2 进入相应的 Django 环境

>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from user.models import *

2.3 确认要插入的数据和预期的一致

>>> Student.objects.create(sname='lili',clazz='Class1',course=('Python','Shell'))
{'sname': 'lili', 'clazz': 'Class1', 'course': ('Python', 'Shell')}

2.4 退出相应的 Django 环境

>>> quit();

步骤三:一次性完成多对多表数据的插入

3.1 创建 1 个自定义 manager 管理器的类

将 mysite/test/models.py 中的以下内容:

......
class Clazz(models.Model):
    cno = models.AutoField(primary_key=True)
    cname = models.CharField(max_length=30)

    def __str__(self):
        return u'Clazz:%s'%self.cname

class Course(models.Model):
    course_no = models.AutoField(primary_key=True)
    course_name = models.CharField(max_length=30)

    def __str__(self):
        return u'Course:%s'%self.course_name

class Student(models.Model):
    sno = models.AutoField(primary_key=True)
    sname = models.CharField(max_length=30)
    cls = models.ForeignKey(Clazz,on_delete=models.CASCADE)
    cour = models.ManyToManyField(Course)

    def __str__(self):
        return u'Student:%s'%self.sname

# Get class object according to class name
def getCls(cname):
    try:
        cls = Clazz.objects.get(cname=cname)
    except Clazz.DoesNotExist:
        cls = Clazz.objects.create(cname=cname)
    return cls

def getCourseList(*coursenames):
    courseList = []

    for cn in coursenames:
        try:
            c = Course.objects.get(course_name=cn)
        except Course.DoesNotExist:
            c = Course.objects.create(course_name=cn)
        courseList.append(c)

    return courseList

def registerStu(sname,cname,*coursenames):
    #1. Get class objects
    cls = getCls(cname)
    #2. Gets the list of course objects
    courseList = getCourseList(*coursenames)
    #3. Insert student table data
    try:
        stu = Student.objects.get(sname=sname)
    except Student.DoesNotExist:
        stu = Student.objects.create(sname=sname,cls=cls)
    #4. Insert intermediate table data
    stu.cour.add(*courseList)

    return True

修改为:

......
class Clazz(models.Model):
    cno = models.AutoField(primary_key=True)
    cname = models.CharField(max_length=30)

    def __str__(self):
        return u'Clazz:%s'%self.cname

class Course(models.Model):
    course_no = models.AutoField(primary_key=True)
    course_name = models.CharField(max_length=30)

    def __str__(self):
        return u'Course:%s'%self.course_name

class CustomManager(Manager):
    #Returns a class object
    def getClsObj(self,cname):
        try:
            cls = Clazz.objects.get(cname=cname)
        except Clazz.DoesNotExist:
            cls = Clazz.objects.create(cname=cname)
        return cls

    def getCourseList(self,*coursenames):
        cList = []
        for cn in coursenames:
            try:
                cour = Course.objects.get(course_name=cn)
            except Course.DoesNotExist:
                cour = Course.objects.create(course_name=cn)
            cList.append(cour)
        return cList

    def  create(self, **kwargs):
         clazz = kwargs.get('cls','')

         #Class information multi storage operation
         clas = self.getClsObj(clazz) 

         #Replace string with object
         #Student.objects.create(sname='',cls=clas)
         kwargs['cls'] = clas

         #Assign a separate value pair of course keys to course
         course = kwargs.pop('cour')

         #Storage operation of student information
         stu = Manager.create(self,**kwargs)

         #Total of the course information in storage
         courseList = self.getCourseList(*course)

         #The middle table of student course is stored in the database. An object or tuple can be unpacked automatically here
         stu.cour.add(*courseList)

class Student(models.Model):
    sno = models.AutoField(primary_key=True)
    sname = models.CharField(max_length=30)
    cls = models.ForeignKey(Clazz,on_delete=models.CASCADE)
    cour = models.ManyToManyField(Course)

    objects = CustomManager()

    def __str__(self):
        return u'Student:%s'%self.sname

# Get class object according to class name
def getCls(cname):
    try:
        cls = Clazz.objects.get(cname=cname)
    except Clazz.DoesNotExist:
        cls = Clazz.objects.create(cname=cname)
    return cls

def getCourseList(*coursenames):
    courseList = []

    for cn in coursenames:
        try:
            c = Course.objects.get(course_name=cn)
        except Course.DoesNotExist:
            c = Course.objects.create(course_name=cn)
        courseList.append(c)

    return courseList

def registerStu(sname,cname,*coursenames):
    #1. Get class objects
    cls = getCls(cname)
    #2. Gets the list of course objects
    courseList = getCourseList(*coursenames)
    #3. Insert student table data
    try:
        stu = Student.objects.get(sname=sname)
    except Student.DoesNotExist:
        stu = Student.objects.create(sname=sname,cls=cls)
    #4. Insert intermediate table data
    stu.cour.add(*courseList)

    return True

(补充:这里以创建一次性完成多对多表数据的插入的 manage 管理器为例)

3.2 进入相应的 Django 环境

(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from user.models import *

3.3 一次性完成多对多表数据的插入

>>> Student.objects.create(sname='lili',cls='Class4',cour=('CSS','JS'))


补充:这里
1) 第 1 步执行:objects = CustomManager()
2) 第 2 步执行:def create(self, **kwargs)
3) 第 3 步执行:clazz = kwargs.get(‘cls’,”),这步的结果是 clazz = ‘Class4’
4) 第 4 步执行:clas = self.getClsObj(clazz),这步的结果是如果 Class4 在数据库中不存在则插入此条数据,并让 clas = cls
5) 第 5 步执行:cours = kwargs.pop(‘cour’),这步的结果是 cours = (‘CSS’,’JS’)
6) 第 6 步执行:stu = Manager.create(self, **kwargs),这步的结果是 stu = Student.objects.create(sname=”,cls=clas)
7) 第 7 步执行:courseList = self.getCourseList(*course),这步的结果是如果 course 元组里的元素不存在则在数据库里创建,并将所有 course 里的元素放在 courseList 空列表中
8) 第 8 步执行:stu.cour.add(*courseList),这步的结果是将 courseList 里的元素插入 Student 类的 cour 字段对应的中间表里

[内容] Python3 模块属性的查询

步骤一:进入 Python3 模式

# python3

步骤二:导入要显示所有属性的模块

>>> import types

(补充:这里以导入 types 模块为例)

步骤三:显示模块多所有属性

>>> dir(types)
['BooleanType',
 'BufferType',
 'BuiltinFunctionType',
 'BuiltinMethodType',
 'ClassType',
 'CodeType',
 'ComplexType',
 'DictProxyType',
 'DictType',
 'DictionaryType',
 'EllipsisType',
 'FileType',
 'FloatType',
 'FrameType',
 'FunctionType',
 'GeneratorType',
 'GetSetDescriptorType',
 'InstanceType',
 'IntType',
 'LambdaType',
 'ListType',
 'LongType',
 'MemberDescriptorType',
 'MethodType',
 'ModuleType',
 'NoneType',
 'NotImplementedType',
 'ObjectType',
 'SliceType',
 'StringType',
 'StringTypes',
 'TracebackType',
 'TupleType',
 'TypeType',
 'UnboundMethodType',
 'UnicodeType',
 'XRangeType',
 '__all__',
 '__builtins__',
 '__doc__',
 '__file__',
 '__name__',
 '__package__']

(补充:这里以显示 types 模块里的属性为例)

[实验] Django 自定义逻辑删除 (通过自定义 manager 管理器实现) (并非真的删除数据)

注意:

本实验是接着 《Django 通过自定义 manger 管理器进行自定义查询》而继续的

正文:

步骤一:准备测试数据

[root@mariadb ~]# mysql -uroot -p
MariaDB [(none)]> use test;
MariaDB [test]> update test_student SET isdelete=0 WHERE test_student.isdelete=1;
MariaDB [test]> quit;

步骤二:通过自定义 manager 管理器对某个数据进行单个的逻辑删除

2.1 创建 1 个自定义 manager 管理器的类

将 mysite/test/models.py 中的以下内容:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=True)

class NotDeletedManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=False)

class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    objects = CustomManager()

    show = NotDeletedManager()

    def __str__(self):
        return u'student:%s'%self.isdelete
......

修改为:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    #def all(self):
    #    return Manager.all(self).filter(isdelete=True)

    def get_queryset(self):
        return Manager.get_queryset(self).filter(isdelete=True)

class NotDeletedManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=False)

class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    #objects = CustomManager()

    #show = NotDeletedManager()

    def __str__(self):
        return u'student:%s'%self.isdelete

    def delete(self, using=None, keep_parents=False):
        self.isdelete = True
        self.save()

......

(补充:这里以将 delete 重写成将 isdelete 设置为 True 为例)

2.2 进入相应的 Django 环境

(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from test.models import *

2.3 通过自定义 manager 管理器查询数据

>>> Student.objects.all()
<QuerySet [<Student: student:False>, <Student: student:False>, <Student: student:False>]>

2.4 通过自定义 manager 管理器逻辑删除第 1 条数据(将第 1 条数据的 isdelete=False 修改成 isdelete=True)

>>> Student.objects.first().delete()

2.5 通过自定义 manager 管理器查询删除后的数据

>>> Student.objects.all()
<QuerySet [<Student: student:True>, <Student: student:False>, <Student: student:False>]>

(补充:这里第一条数据 isdelete=True)

步骤三:通过自定义 manager 管理器对一类数据进行批量的逻辑删除

3.1 创建 1 个自定义 manager 管理器的类

将 mysite/test/models.py 中的以下内容:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    #def all(self):
    #    return Manager.all(self).filter(isdelete=True)

    def get_queryset(self):
        return Manager.get_queryset(self).filter(isdelete=True)

class NotDeletedManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=False)
   
class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    #objects = CustomManager()

    #show = NotDeletedManager()

    def __str__(self):
        return u'student:%s'%self.isdelete

    def delete(self, using=None, keep_parents=False):
        self.isdelete = True
        self.save()

......

修改为:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    #def all(self):
    #    return Manager.all(self).filter(isdelete=True)

    def get_queryset(self):
        return Manager.get_queryset(self).filter(isdelete=True)

class DeletedManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=True)
   
class BatchDelManager(Manager):
    def get_queryset(self):
        return Manager.get_queryset(self).filter(isdelete=False)

    def filter(self, *args, **kwargs):
        #1. Get the record that needs to be deleted
        delList = Manager.get_queryset(self)

        #2. Define the closure method to modify isdelete = true
        def delete1(delqueryset):
            for dq in delqueryset:
                dq.isdelete = True
                dq.save()
        #3. Create an instance method dynamically, declare that delete has a delete method, call interface is delete (delist), call the delete1 object to execute
        import types
        delList.delete = types.MethodType(delete1,delList)

        return delList

class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    objects = BatchDelManager()

    show = DeletedManager()

    def __str__(self):
        return u'student:%s'%self.isdelete

    def delete(self, using=None, keep_parents=False):
        self.isdelete = True
        self.save()

......


补充:
1) 这里以添加 1 个将父类的 all 重写成将所有 isdelete=True 的数据修改成 isdelete=False 的 BatchDelManager(Manager) 对象并在 Student 类中使用 objects 调用为例
2) 这里的实现方法是:动态创建 1 个实例方法,对外声明 delete 有 1 个 delete 方法调用接口是 delete(deList) ,调用 delete1 对象进行执行
3) 这里的 DeletedManager(Manager) 会查看所有 isdelete=True 的数据,并通过 Student 类中的 show 调用
4) 只要子类重写了父类的方法,就不会掉用父类的方法了,而是调用子类的方法
5) 闭包的作用是内部执行 1 个结果,对外还是 1 个外部函数的 1 个调用
6) python2 使用是 new 模块的 instancemethod 属性实现此功能,而 python3 使用是 types 模块的 MethodType属性实现此功能

3.2 进入相应的 Django 环境

(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from test.models import *

3.3 查看所有 isdelete=False 的数据

>>> Student.objects.all()
<QuerySet [<Student: student:False>, <Student: student:False>]>

(补充:这里的 objects 已经经过了重写,所以只能显示 isdelete=False 的数据)

3.4 通过自定义 manager 管理器对一类数据进行批量的逻辑删除(将所有 isdelete=False 的数据修改成 isdelete=True)

>>> Student.objects.filter().delete()

3.5 查看所有 isdelete=False 的数据

>>> Student.objects.all()
<QuerySet []>

(补充:此时已经没有 isdelete=False 的数据)

3.6 查看所有 isdelete=True 的数据

>>> Student.show.all()
<QuerySet [<Student: student:True>, <Student: student:True>, <Student: student:True>]>

[实验] Django 自定义查询 (通过自定义 manager 管理器实现)

注意:

文中的 python 系统名、mysite 项目、user 应用和 Student 类只是站主在本次操作中随意取的名称,读者可以根据自己的喜好换成任意别的名称

正文:

步骤一:系统环境要求

(1)服务器的系统需要是 openSUSE 15.2 版本
(2)服务器要关闭防火墙
(3)服务器系统要配置好可用的软件源(最好是软件数量最多的官方版本)
(4)服务器要能够连接外网

步骤二:安装 Django

2.1 安装 Python3

[root@python ~]# zypper -n install python3

2.2 创建并进入 Django 项目的目录

[root@python ~]# mkdir project
[root@python ~]# cd project

2.3 将 Django 项目的目录指定为 Django 环境

[root@python project]# python3 -m venv django_env

2.4 进入 Django 环境

[root@python project]# source django_env/bin/activate
(django_env) [root@python project]# pip install django

(补充:在此次操作发生时,最新的 Django 版本是 3.2)

步骤三:创建 mysite 项目

3.1 创建 mysite 项目

(django_env) [root@python project]# django-admin startproject mysite

3.2 mysite 项目的目录

3.2.1 安装 tree 目录显示软件
(django_env) [root@python project]# zypper -n install tree
3.2.2 显示 mysite 项目的目录
(django_env) [root@python project]# cd mysite
(django_env) [root@python mysite]# tree
.
├── manage.py
└── mysite
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

1 directory, 5 files
3.2.3 Django 项目目录介绍

(1)mysite 此 Django 项目的容器
(2)manage.py 命令行工具,与 Django 项目进行交互
(3)mysite/__init__.py 空文件,通知 Python 此项目是 1 个 Python 包
(4)mysite/settings.py 此 Django 项目的配置文件
(5)mysite/urls.py 此 Django 项目的 URL 声明和 Django 的网站“目录”
(6)mysite/wsgi.py WSGI 兼容 Web 服务器的入口

步骤四:创建 test 应用

4.1 创建 test 应用

(django_env) [root@python mysite]# django-admin startapp test

4.2 test 应用的目录

4.2.1 显示 test 应用的目录
(django_env) [root@python mysite]# tree
.
├── manage.py
├── mysite
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── test
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

3 directories, 13 files
4.2.2 Django 应用目录介绍

(1)test/app.py 此 Django 应用的容器
(2)test/__init__.py 空文件,通知 Python 此项目是 1 个 Python 包
(3)test/admin.py 此 Django 应用自带的后台管理相关的类
(4)test/app.py 此 Django 应用指定应用名的文件
(5)test/migrations.py 此 Django 应用通过 python 代码生成数据库表时里面会产生一些迁移文件
(6)test/models.py 可以在里面创建一些 Python 对象并通过这些对象在数据库里创建对应的表
(7)test/test.py 此 Django 应用的测试文档
(8)test/views.py 此 Django 应用的视图,接收前端数据,把数据传递给后端,响应相关页面

4.3 在 mysite 应用中导入 test 应用

在 mysite/mysite/settings.py 中添加以下内容:

......
INSTALLED_APPS = [
......
    'test',
]
......

步骤五:实现连接 MariaDB 数据库

5.1 安装 MairaDB 数据库和客户端

(django_env) [root@python mysite]# zypper -n install mariadb mariadb-devel mariadb-server mariadb-client

5.2 创建用于 Django 的 MairaDB 的库和用户

5.2.1 进入 MairaDB 数据库
(django_env) [root@python mysite]# mysql -h 127.0.0.1 -p
5.2.2 创建用于 Django 的库
MariaDB [(none)]> create database user;

(补充:这里以创建 user 库为例)

5.2.3 创建用于 Django 的用户
MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION;

(补充:这里以创建 root 用户,密码是 password 为例)

5.2.4 刷新权限
MariaDB [(none)]> flush privileges;

5.3 退出 MariaDB 数据库

MariaDB [(none)]> exit

5.4 重启 MariaDB

(django_env) [root@python mysite]# systemctl restart mariadb

步骤六:搭建用于 Django 的 MariaDB 数据库环境

6.1 安装 Django 连接 MariaDB 的模块

(django_env) [root@python mysite]# pip3 install hexdump
(django_env) [root@python mysite]# pip3 install pymysql

6.2 在 mysite 应用中添加 Django 连接 MariaDB 的模块

在 mysite/mysite/__init__.py 中添加以下内容:

import pymysql
pymysql.install_as_MySQLdb()

6.3 在 test 应用数据库模板中添加 Student 类

在 mysite/test/models.py 中添加以下内容:

class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

(补充:这里以创建包含 sname 字段和 isdelete 字段的 Student 类为例)

6.4 在 mysite 应用中设置连接到 MariaDB 数据库

将 mysite/mysite/settings.py 中的以下内容:

......
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
......

修改为:

......
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': 'password',
    }
}
......


补充:这里以
(1)使用的库是 test
(2)使用的服务器是 127.0.0.1
(3)使用的端口是 3306
(4)使用的用户是 root
(5)使用的密码是 password
为例

6.5 生成牵引文件

(django_env) [root@python mysite]# python3 manage.py makemigrations
Migrations for 'user':
  user/migrations/0001_initial.py
    - Create model Student

6.6 将牵引文件导入 MariaDB 数据库

(django_env) [root@python mysite]# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, user
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
  Applying user.0001_initial... OK

步骤七:插入测试数据

7.1 进入相应的 Django 环境

(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from test.models import *

7.2 插入测试数据

>>> Student.objects.create(sname='zhangsan')
<Student: Student object (1)>

>>> Student.objects.create(sname='lisi',isdelete=True)
<Student: Student object (2)>

>>> Student.objects.create(sname='wangwu',isdelete=True)
<Student: Student object (3)>

(补充:这里以往 Student 类任意插入三条测试数据为例)

7.3 退出相应的 Django 环境

>>> quit();

步骤八:正常查询数据

8.1 进入相应的 Django 环境

(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from test.models import *

8.2 正常查询数据

>>> Student.objects.all()
<QuerySet [<Student: Student object (1)>, <Student: Student object (2)>, <Student: Student object (3)>]>

(补充:此时会一次性查询出 Student 类里所有的数据)

8.3 退出相应的 Django 环境

>>> quit();

步骤九:通过自定义 manager 管理器查询数据

9.1 自定义 manager 管理器(通过重写父类的 all 实现)

9.1.1 自定义 1 个查询 isdelete=True 数据的 manager 管理器
9.1.1.1 创建 1 个自定义 manager 管理器的类

将 mysite/test/models.py 中的以下内容:

......
class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)
......

修改为:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=True)

class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    objects = CustomManager()

    def __str__(self):
        return u'student:%s'%self.isdelete
......


补充:
(1)self 在 python 中代表当前对象,在其他语言中,会用 this 来代表当前对象
(2)这里以将父类的 all 重写成只选择 isdelete=True 数据的 CustomManager(Manager) 类并在 Student 类中使用 objects 调用为例

9.1.1.2 进入相应的 Django 环境
(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from test.models import *
9.1.1.3 通过自定义 manager 管理器查询数据
>>> Student.objects.all()
<QuerySet [<Student: student:True>, <Student: student:True>]>

(补充:这里只查出了 isdelete=True 的数据)

9.1.1.4 退出相应的 Django 环境
>>> quit();
9.1.2 自定义 1 个查询 isdelete=False 数据的 manager 管理器
9.1.2.1 创建 1 个自定义 manager 管理器的类

将 mysite/test/models.py 中的以下内容:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=True)

class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    objects = CustomManager()

    def __str__(self):
        return u'student:%s'%self.isdelete
......

修改为:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=True)

class NotDeletedManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=False)

class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    objects = CustomManager()

    show = NotDeletedManager()

    def __str__(self):
        return u'student:%s'%self.isdelete
......

(补充:这里以添加一个将父类的 all 重写成只选择 isdelete=False 数据的 NotDeletedManager(Manager) 对象并在 Student 类中使用 show 调用为例)

9.1.2.2 进入相应的 Django 环境
(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from test.models import *
9.1.2.3 通过自定义 manager 管理器查询数据
>>> Student.show.all()
<QuerySet [<Student: student:False>]>

(补充:这里只查出了 isdelete=False 的数据)

9.1.2.4 退出相应的 Django 环境
>>> quit();

9.2 自定义 manager 管理器(通过重写继承的子类 get_queryset 实现)

9.2.1 创建 1 个自定义 manager 管理器的类

将 mysite/test/models.py 中的以下内容:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=True)

class NotDeletedManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=False)
   
class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    objects = CustomManager()

    show = NotDeletedManager()

    def __str__(self):
        return u'student:%s'%self.isdelete
......

修改为:

......
from django.db.models.manager import Manager

class CustomManager(Manager):
    #def all(self):
    #    return Manager.all(self).filter(isdelete=True)

    def get_queryset(self):
        return Manager.get_queryset(self).filter(isdelete=True)

class NotDeletedManager(Manager):
    def all(self):
        return Manager.all(self).filter(isdelete=False)
   
class Student(models.Model):
    sname = models.CharField(max_length=30)
    isdelete = models.BooleanField(default=False)

    objects = CustomManager()

    show = NotDeletedManager()

    def __str__(self):
        return u'student:%s'%self.isdelete
......

(补充:这里以添加一个将子类的 get_queryset 重写成只选择 isdelete=True 数据的 CustomManager(Manager) 对象并在 Student 类中使用 objects 调用为例)

9.2.2 进入相应的 Django 环境
(django_env) [root@python mysite]# python3
>>> import os,django
>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
'mysite.settings'
>>> django.setup()
>>> from test.models import *
9.2.3 通过自定义 manager 管理器查询数据
>>> Student.objects.all()
<QuerySet [<Student: student:True>, <Student: student:True>]>

(补充:这里只查出了 isdelete=True 的数据)

9.2.4 退出相应的 Django 环境
>>> quit();

[实验] Django 数据展示 (多对多版)

注意:

文中的 python 系统名、mysite 项目、user 应用 Clazz 类、Course 类和 Student 类只是站主在本次操作中随意取的名称,读者可以根据自己的喜好换成任意别的名称

正文:

步骤一:系统环境要求

1) 服务器的系统需要是 openSUSE 15.2 版本
2) 服务器要关闭防火墙
3) 服务器系统要配置好可用的软件源(最好是软件数量最多的官方版本)
4) 服务器要能够连接外网

步骤二:安装 Django

2.1 安装 Python3

[root@python ~]# zypper -n install python3

2.2 创建并进入 Django 项目的目录

[root@python ~]# mkdir project
[root@python ~]# cd project

2.3 将 Django 项目的目录指定为 Django 环境

[root@python project]# python3 -m venv django_env

2.4 进入 Django 环境

[root@python project]# source django_env/bin/activate
(django_env) [root@python project]# pip install django

(补充:在此次操作发生时,最新的 Django 版本是 3.2)

步骤三:创建 mysite 项目

3.1 创建 mysite 项目

(django_env) [root@python project]# django-admin startproject mysite

3.2 mysite 项目的目录

3.2.1 安装 tree 目录显示软件
(django_env) [root@python project]# zypper -n install tree
3.2.2 显示 mysite 项目的目录
(django_env) [root@python project]# cd mysite
(django_env) [root@python mysite]# tree
.
├── manage.py
└── mysite
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

1 directory, 5 files
3.2.3 Django 项目目录介绍

1) mysite 此 Django 项目的容器
2) manage.py 命令行工具,与 Django 项目进行交互
3) mysite/__init__.py 空文件,通知 Python 此项目是 1 个 Python 包
4) mysite/settings.py 此 Django 项目的配置文件
5) mysite/urls.py 此 Django 项目的 URL 声明和 Django 的网站“目录”
6) mysite/wsgi.py WSGI 兼容 Web 服务器的入口

步骤四:创建 user 应用

4.1 创建 user 应用

(django_env) [root@python mysite]# django-admin startapp user

4.2 user 应用的目录

4.2.1 显示 user 应用的目录
(django_env) [root@python mysite]# tree
.
├── manage.py
├── mysite
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── user
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

3 directories, 13 files
4.2.2 Django 应用目录介绍

1) user/app.py 此 Django 应用的容器
2) user/__init__.py 空文件,通知 Python 此项目是 1 个 Python 包
3) user/admin.py 此 Django 应用自带的后台管理相关的类
4) user/app.py 此 Django 应用指定应用名的文件
5) user/migrations.py 此 Django 应用通过 python 代码生成数据库表时里面会产生一些迁移文件
6) user/models.py 可以在里面创建一些 Python 对象并通过这些对象在数据库里创建对应的表
7) user/test.py 此 Django 应用的测试文档
8) user/views.py 此 Django 应用的视图,接收前端数据,把数据传递给后端,响应相关页面

4.3 在 mysite 应用中导入 user 应用

在 mysite/mysite/settings.py 中添加以下内容:

......
INSTALLED_APPS = [
......
    'user',
]
......

步骤五:实现 user 应用的层级多链接

在 mysite/mysite/urls.py 中添加以下内容:

......
from django.conf.urls import url, include
......
urlpatterns = [
......
    url(r'^register/', include('user.urls')),
]

(补充:这里以设置 /register/ 链接对应 user 应用的链接为例)

步骤六:实现连接 MariaDB 数据库

6.1 安装 MairaDB 数据库和客户端

(django_env) [root@python mysite]# zypper -n install mariadb mariadb-devel mariadb-server mariadb-client

6.2 创建用于 Django 的 MairaDB 的库和用户

6.2.1 进入 MairaDB 数据库
(django_env) [root@python mysite]# mysql -h 127.0.0.1 -p
6.2.2 创建用于 Django 的库
MariaDB [(none)]> create database user;

(补充:这里以创建 user 库为例)

6.2.3 创建用于 Django 的用户
MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION;

(补充:这里以创建 root 用户,密码是 password 为例)

6.2.4 刷新权限
MariaDB [(none)]> flush privileges;

6.3 退出 MariaDB 数据库

MariaDB [(none)]> exit

6.4 重启 MariaDB

(django_env) [root@python mysite]# systemctl restart mariadb

步骤七:搭建用于 Django 的 MariaDB 数据库环境

7.1 安装 Django 连接 MariaDB 的模块

(django_env) [root@python mysite]# pip3 install hexdump
(django_env) [root@python mysite]# pip3 install pymysql

7.2 在 mysite 应用中添加 Django 连接 MariaDB 的模块

在 mysite/mysite/__init__.py 中添加以下内容:

import pymysql
pymysql.install_as_MySQLdb()

7.3 在 user 应用数据库模板中添加 Clazz 类、Course 类、Student 类和插入多对多数据的对象

在 mysite/user/models.py 中添加以下内容:

class Clazz(models.Model):
    cno = models.AutoField(primary_key=True)
    cname = models.CharField(max_length=30)

    def __str__(self):
        return u'Clazz:%s'%self.cname

class Course(models.Model):
    course_no = models.AutoField(primary_key=True)
    course_name = models.CharField(max_length=30)

    def __str__(self):
        return u'Course:%s'%self.course_name

class Student(models.Model):
    sno = models.AutoField(primary_key=True)
    sname = models.CharField(max_length=30)
    cls = models.ForeignKey(Clazz,on_delete=models.CASCADE)
    cour = models.ManyToManyField(Course)

    def __str__(self):
        return u'Student:%s'%self.sname

# Get class object according to class name
def getCls(cname):
    try:
        cls = Clazz.objects.get(cname=cname)
    except Clazz.DoesNotExist:
        cls = Clazz.objects.create(cname=cname)
        return cls

def getCourseList(*coursenames):
    courseList = []

    for cn in coursenames:
        try:
            c = Course.objects.get(course_name=cn)
        except Course.DoesNotExist:
            c = Course.objects.create(course_name=cn)
        courseList.append(c)

    return courseList

def registerStu(sname,cname,*coursenames):
    #1. Get class objects
    cls = getCls(cname)
    #2. Gets the list of course objects
    courseList = getCourseList(*coursenames)
    #3. Insert student table data
    try:
        stu = Student.objects.get(sname=sname)
    except Student.DoesNotExist:
        stu = Student.objects.create(sname=sname,cls=cls)
    #4. Insert intermediate table data
    stu.cour.add(*courseList)

    return True


补充:
1) 这里 getCls 对象的作用是获取 cname 变量,如果数据库中没有 cname 变量的值则直接插入这个值
2) 这里 getCourseList 对象的作用是获取 coursenames 变量,如果数据库中没有 coursenames 变量的值则直接插入这个值
3) 这里 registerStu 对象的作用是执行 getCls 对象和 getCourseList 对象,获取 sname 变量,如果数据库中没有 sname 变量的值则直接插入这个值

7.4 在 mysite 应用中设置连接到 MariaDB 数据库

将 mysite/mysite/settings.py 中的以下内容:

......
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
......

修改为:

......
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'user',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': 'password',
    }
}
......


补充:这里以
1) 使用的库是 user
2) 使用的服务器是 127.0.0.1
3) 使用的端口是 3306
4) 使用的用户是 root
5) 使用的密码是 password
为例

7.5 生成牵引文件

(django_env) [root@python mysite]# python3 manage.py makemigrations
Migrations for 'user':
  user/migrations/0001_initial.py
    - Create model Clazz
    - Create model Course
    - Create model Student

7.6 将牵引文件导入 MariaDB 数据库

(django_env) [root@python mysite]# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, user
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
  Applying user.0001_initial... OK

(补充:此时会在数据库里生成 3 张表 user_clazz 表、user_course 表、user_student 表和 user_student_cour 表,其中 user_student_cour 表是 user_course 表和 user_student 表的中间表)

步骤八:实现多对多注册功能

8.1 创建所需的 HTML 模版

8.1.1 在 user 应用中添加 1 个学生注册功能的 HTML 模板

创建 mysite/user/templates/register.html 并添加以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>register</title>
</head>
<body>
    <form action="/register/" method="post">
        {% csrf_token %}
        <p>
            <label for="sn">Name: </label><input name="sname" id="sn"/>
        </p>
        <p>
            <label for="sc">Classroom: </label>
            <select name="clsname" id="sc">
                <option value="Class 1">Class 1</option>
                <option value="Class 2">Class 2</option>
                <option value="Class 3">Class 3</option>
            </select>
        </p>
        <p>
            <label for="cour">Course Selection: </label>
            <input type="checkbox" name="coursename" value="Python"/>Python &emsp;
            <input type="checkbox" name="coursename" value="Shell"/>Shell &emsp;
            <input type="checkbox" name="coursename" value="HTML"/>HTML &emsp;
            <input type="checkbox" name="coursename" value="Django"/>Django &emsp;
        </p>
        <p>
            <input type="submit" value="Register"/>
        </p>
    </form>
</body>
</html>


补充:这里以
1) 生成 1 个用户名输入栏,赋值给 sname
2) 生成 1 个单选框,赋值给 clsname
3) 生成 1 个多选框,赋值给 cour
4) 生成 1 个注册按钮
为例

8.1.2 在 user 应用中添加 1 个显示所有数据的 HTML 模板

创建 mysite/user/templates/showall.html 并添加以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>register</title>
</head>
<body>

<table border="1" cellspacing="0" width="500px">
    <tr>
        <th>Code</th>
        <th>ClassRoom</th>
        <th>Operation</th>
    </tr>
    {% for c in cls %}
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>{{ c.cname }}</td>
            <td>
                <a href="/register/getstu/?cno={{ c.cno }}">details</a>
            </td>
        <tr>
    {% endfor %}

</table>

</body>
</html>

(补充:这里以生成显示全部数据的列表为例)

8.1.3 在 user 应用中添加 1 个显示某 1 条 course 数据详细内容的 HTML 模板

创建 mysite/user/templates/stulist.html 并添加以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>register</title>
</head>
<body>

<table border="1" cellspacing="0" width="500px">
    <tr>
        <th>Code</th>
        <th>Student Name</th>
        <th>Classroom Name</th>
        <th>Course Name</th>
    </tr>
    {% for stu in stus %}
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>{{ stu.sname }}</td>
            <td>{{ stu.cls.cname }}</td>
            <td>
                {% for st in stu.cour.all %}
                    {{ st.course_name }}
                {% endfor %}
            </td>
        <tr>
    {% endfor %}

</table>

</body>
</html>


补充:
1) 这里以生成显示某 1 条 Classroom Name 为特定值的所有相关数据为例
2) 这里模版语法 all 后面不用加小括号“()”:

{% for st in stu.cour.all %}

8.2 在 user 应用中添加 1 个网页返回值的模块并实现所需功能

在 mysite/user/views.py 中添加以下内容:

......
from django.shortcuts import render
from .models import *
from django.http import HttpResponse, request
......

def index_view(request):
    if request.method == 'GET':
        return render(request,'register.html')
    else:
        # Receive request parameters
        sname = request.POST.get('sname','')
        cname = request.POST.get('clsname','')
        coursenames = request.POST.getlist('coursename',[])

        # Register data to database
        flag = registerStu(sname,cname,*coursenames)
        if flag:
            return HttpResponse('register success!')
        return HttpResponse('register fale!')

def showall_view(request):
    #Display all classroom informations
    cls = Clazz.objects.all()
    return render(request,'showall.html',{'cls':cls})

def getstu_view(request):
    #Get classroom code
    cno = request.GET.get('cno','')
    mno = int(cno)

    #Get student information from class code
    stus = Clazz.objects.get(cno=mno).student_set.all()

    return render(request,'stulist.html',{'stus':stus})


补充:这里以
1) 获取来自 HTML 的 sanme、clsname 和 coursename 变量
2) 使用 registerStu 对象处理所有变量,处理成功则返回 success! 否则返回 fale!
3) 使用 showall_view 对象将所有数据传递给 showall.html
4) 使用 getstu_view 对象将某 1 条 Classroom Name 为特定值的所有相关数据传递给 stulist.html
为例

8.3 在 movies 应用中添加 1 个链接并设置对应的模块

创建 mysite/user/urls.py 并添加以下内容:

from django.conf.urls import url
from user.views import *

urlpatterns=[
    url(r'^$',index_view),
    url(r'^showall/$',showall_view),
    url(r'^getstu/$',getstu_view),
]

(补充:这里以设置空链接链接对应 index_view 模块为例)

步骤九:启动 Django 服务

(django_env) [root@python mysite]# python3 manage.py runserver

步骤十:测试页码功能

1) 打开浏览器输入以下网址:

http://127.0.0.1:8000/register/

2) 可以看到 Name 输入栏、Classroom 单选框、Course Selection 多选框和 Register 注册按钮,并可以注册信息

3) 打开浏览器输入以下网址:

http://127.0.0.1:8000/showall/

4) 可以看到刚才注册的信息并且还有链接看到详细内容