# 模型层

通过讲mysql的系统,翻译一遍,在django中通过创建model去和数据库中的表进行一一映射,并且通过ORM封装 的处理方式去练习这一到习题,并写出如下的解题答案

# 1.数据库准备

在model中定义数据库,其中的性别,男的存1,女的存0。

class Student(models.Model):
    stuname = models.CharField(max_length=20)
    stusex = models.BooleanField()
    stubirth = models.DateField()
    stutel = models.CharField(max_length=255)

    class Meta:
        db_table = 'student'

# 2.数据库迁移

python manage.py makemigrations
python manage.py migrate

# 3. 数据插入

# 3.1 使用表单form提交post请求数据
<form action="/app/addStu/" method="post">
    stuname: <input type="text" name="name">
    stusex: <input type="text" name="sex">
    stubirth: <input type="date" name="birth">
    stutel: <input type="text" name="tel">
    <input type="submit" value="提交">
</form>
# 3.2 获取post请求,获取请求数据,并且创建数据
# 方法1:获取类对象进行save()保存
stu = Student()  #得到一个对象,即记录
stu.stuname = stuname
stu.stusex = sex
stu.stubirth = birth
stu.stutel = tel
stu.save()
# 方法2:使用create()方法
Student.objects.create(stuname=stuname, 
                        stusex=sex,
                        stubirth=birth, 
                        stutel=tel)
# 方法3:使用初始化
在Student模型中重构__init__()方法,添加如下代码

def __init__(self, name, birth=None, sex=None,tel=None):
    super().__init__()
    self.stuname = name
    self.stubirth = birth 
    self.stusex = sex
    self.stutel = tel

# 视图函数中定义创建学习信息的方法为:
stu = Student('小草', 18, 1, 12331244323)
stu.save()

----------------------------- 复习 -----------------------------------

class A:
    def __init__(self):
        self.name = '儿子'  # 当有了init函数时,相当于有了实例,所以此时的name时'儿子'
    name = '父级'  # 当没有init时,打印'父级',此时A()回去执行父级object的init返回一个空间
        
obj_1 = A()  # 区分在这个括号,判断到底能不能实例化,但始终儿子会继承父级的东西
print(obj_1.name)
print(A.name)  # 父级

-----------------------------------------------------------------------
#详细讲解
#models.py 文件中
from django.db import models

class Class(models.Model):
    def __init__(self,name):  #添加了init函数
        super().__init__()
        self.name = name   
        
    name = models.CharField(max_length=12)
    
#此时对应的views.py文件
def add_class(request):
    
    # 方法一:在class中没有__init__时,此时他将会回到Model的init中执行
    class_obj = models.Class()
    class_obj.name = 'Pyhton20期'
    class_obj.save()
    
    # 方法二: 在class中设置了__init__ 初始化函数时,我们可以通过
    class_obj = models.Class('linux 20期') #使用了
    class_obj.save()
    
    return HttpResponse('添加成功')

#可能这里会有点模糊:name和self.name 怎么就联系在一起了? 其实在我们通过Class()方式来创建一条记录的时候,就是对类进行了一次实例化,也就是说,其本身就是一个实例化过程,最开始程序进入Class类,加载了name字段(在父类Model中初始化字段,对字段进行约束),也就是说在这时候就已经有了self.name,那么我就就可以在init后对self.name进行覆盖:

class Class(models.Model):
    def __init__(self,name):
        self.name = name    # 放在init之前无效
        super().__init__()  # 这里会重置、分配name字段,之后实例化的self.name 就可以用于赋值
        self.name = '123'   # 也就是说使用这种方法赋值,必须放在init后面,且,所有赋值的原理都是如此
            
    name = models.CharField(max_length=12)

注意:重构init方法的时候,一定要使用super().init(),否则会报studen对象没有_state的属性。

# 4. 查询所有的学生信息

使用all()方法获取所有的数据

Student.objects.all()

# 4. 查询所有女学生的姓名和出生日期

Student.objects.filter(stusex=0)
或者
Student.objects.exclude(stusex=1)

其中:

filter():返回符合条件的数据

exclude():过滤掉符合条件的数据

# 5.查询所有的学生,按照id从大到小排序

Student.objects.all().order_by('-id')

其中:

order_by('id'):表示按照id升序的排列

order_by('-id'):表示按照id降序的排列

# 6. 查询所有的学生信息,并序列化

Student.objects.all().values()
Student.objects.all().values('s_name', 's_age')

# 7.查询所有80后学生的姓名、性别和出生日期(筛选)

Student.objects.filter(stubirth__gte='1980-01-01', 
                        stubirth__lte='1990-01-01')

# 8.查询名字中有王字的学生的姓名(模糊),like '%小%', like '小%',like '%小'

Student.objects.filter(s_name__contains='小')
Student.objects.filter(s_name__startswith='小')
Student.objects.filter(s_name__endswith='小')

# 9.查询id等于1,2的学生信息

# select * from student where id in (1,2)
stus = Student.objects.filter(id__in=[1,2])

# 10. 获取id为1的信息,get()和filter()的使用

Student.objects.filter(id=1)
Student.objects.get(id=1)
Student.objects.get(pk=1)

# get获取不到数据会直接报错, filter获取不到数据是返回空
stus = Student.objects.get(pk=5)
Student.objects.filter(id=5)

# get只能返回一个数据,返回多个会报错
Student.objects.get(s_age=15) # 前提条件:数据库中s_age为15的数据有多条

# 11.获取所有学生(按照id降序)中第一个/最后一个学生信息

# 获取按照id降序的第一个学生信息
Student.objects.all().order_by('-id')[0]
Student.objects.all().order_by('-id').first()
# 获取所有学生(按照id降序)中最后一个学生信息
Student.objects.all().order_by('-id').last()

# 模型加参

1.建表

class Grade(models.Model):
    #班级名称
    g_name = models.CharField(max_length=10, unique=True, null=False)

    class Meta:
        db_table = 'grade'


class StudentInfo(models.Model):
    #学生信息:地址和电话
    address = models.CharField(max_length=50, null=True)
    phone = models.CharField(max_length=11, null=True)

    class Meta:
        db_table = 'stu_info'


class Student(models.Model):
    # 长度为10,且唯一不能为空的姓名s_name字段  CharField - 字符串类型
    s_name = models.CharField(max_length=10, unique=True, null=False)
    # IntegerField - 整型字段
    age = models.IntegerField(default=18)
    # auto_now_add:表示第一次创建数据时,自动默认为创建的时间
    create_time = models.DateTimeField(auto_now_add=True)
    # auto_now: 表示修改时,自动更新为修改时间
    operate_time = models.DateTimeField(auto_now=True)
    # 是否删除
    is_delete = models.BooleanField(default=0)
    # 定义浮点数总长度3位,小数点后1位(decimal_places=1)
    yuwen = models.DecimalField(max_digits=3, decimal_places=1, null=True)
    math = models.DecimalField(max_digits=3, decimal_places=1, null=True)
    # 一对一:定义在关联的两个模型中的任何一方都可以;地址和学生是一一对应的,排除兄弟情况
    stuinfo = models.OneToOneField(StudentInfo, related_name='stu', on_delete=models.CASCADE, null=True)
    # 一对多:只能定义在‘多’的一方,一个班级对应多个学生
    grade = models.ForeignKey(Grade, related_name='stu', on_delete=models.CASCADE, null=True)

    class Meta:
        db_table = 'student'

2.数据库迁移

python manage.py makemigrations
python manage.py migrate
# 模型对应关系描述如下:

1:1 一对一 OneToOneField :两方都可以 1:N 一对多 ForeignKey :只能写在多的一方 M:N 多对多 ManyToManyField 会自动建一个中间表 :两方都可以

# 在views.py文件中
def add_stu_info(request):
    if request.method == 'GET':
        # 向拓展表中添加信息
        stu_info = StudentInfo()  #这里是实例直接执行了Model中的__init__,我们也可以自己定义__init__
        stu_info.phone = '13545455433'
        stu_info.save()
        s_id = stu_info.id
        stu = Student.objects.filter(s_name='小明').first()
        # 第一种写法: 学生对象.OneToOneField管理对象 = 关联对象,这就相当于stuinfo是一个连接外表的外键,指向与外表的一条记录,这里的stu_info,相当于一条记录,下面的方法相当于讲小明这条记录,与刚创建的stu_info记录的主键进行了绑定
        stu.stuinfo = stu_info
        # 第二种写法: 学生对象.OneToOneField字段 = 关联对象.id,这个就相当于对每个字段进行赋值了:student_id = '2',记主键之间的绑定
        stu.stuinfo_id = stu_info.id
        stu.save()

        return HttpResponse('添加拓展表信息')


def sel_stuinfo_by_stu(request):
    if request.method == 'GET':
        # 通过学生信息找拓展表信息
        stu = Student.objects.get(s_name='小明')
        # StudentInfo.objects.get(pk=stu.stuinfo_id)  # 等价于stu.stuinfo,都是找主键
        # 学生对象(stu).OneToOneField字段(stuinfo)
        stu.stuinfo
        return HttpResponse('获取拓展表信息成功')


def sel_stu_by_info(request):
    if request.method == 'GET':
        # 通过拓展表中的手机号码找学生信息
        stuinfo = StudentInfo.objects.filter(phone='13545455433').first()
        # stu = Student.objects.filter(stuinfo=stuinfo)  #通过学生表的关联表找
        # stu = Student.objects.filter(stuinfo_id=stuinfo.id)  # 通过一个student_id修改
        # print(stu)
        
        # 拓展表对象.关联的模型名称小写
        # stu = stuinfo.student  # 和上面两步一样,这步很简单,已经知道stuinfo了,让其.student
        # 定义related_name参数, 拓展表对象.related_name值
        stu = stuinfo.stu  # 这的stu是onetonoe字段的related_name参数,它和上面那步是互斥的
        print(stu)
        return HttpResponse('通过拓展表信息查询学生表信息')


def add_grade(request):
    if request.method == 'GET':
        # 添加班级信息, 并给学生分配班级
        names = ['Python班级', 'Java班级', 'Php班级', 'C++班级']
        for name in names:
            if Grade.objects.filter(g_name=name).exists():
                Grade.objects.creat(g_name=name)
        # 分配班级
        stus = Student.objects.filter(pk_in=[5, 6, 7, 10, 11, 12]).all()
        g = Grade.objects.filter(g_name='Python班级').first()
        for stu in stus:
            stu.grade = g
            # stu.grade_id = g.id
            stu.save()
        return HttpResponse('添加班级和学生信息')


def sel_grade_by_stu(request):
    if request.method == 'GET':
        # 通过学生查询班级信息
        stu = Student.objects.filter(s_name='小明').first()
        g = stu.grade
        print(g)
        # 班级查询学生
        stus = g.student_set.all()  # 和下面一样
        stus = g.stu.all()  # 定义了related_name='stu',但是用了related_name之后,就不能用_set了

        return HttpResponse('通过学生查询班级成功')

# 多对多

先声明两个类Course, Student

class Course(models.Model):
    c_name = models.CharField(max_length=10, unique=True, null=False)

    class Meta:
        # 指定表名
        db_table = 'course'

class Student(models.Model):
    # 长度为10,且唯一不能为空的姓名s_name字段  CharField - 字符串类型
    s_name = models.CharField(max_length=10, unique=True, null=False)
    # IntegerField - 整型字段
    age = models.IntegerField(default=18)
    # auto_now_add:表示第一次创建数据时,自动默认为创建的时间
    create_time = models.DateTimeField(auto_now_add=True)
    # auto_now: 表示修改时,自动更新为修改时间
    operate_time = models.DateTimeField(auto_now=True)
    # 是否删除
    is_delete = models.BooleanField(default=0)
    # 定义浮点数总长度3位,小数点后1位(decimal_places=1)
    yuwen = models.DecimalField(max_digits=3, decimal_places=1, null=True)
    math = models.DecimalField(max_digits=3, decimal_places=1, null=True)

    # 多对多
    course = models.ManyToManyField(Course, related_name='stu')

    class Meta:
        db_table = 'student'

views.py文件中

def add_course(request):
    if request.method == 'GET':
        # 添加课程信息
        names = ['大学语文', '日语', '数电', '模电']
        for name in names:
            cou = Course()
            cou.c_name = name
            cou.save()

        return HttpResponse('添加课程成功')


def sel_cou_by_stu(request):
    if request.method == 'GET':
        # 查询id为1的学生所选择的课程信息
        stu = Student.objects.get(pk=1)
        # 学生查找课程
        stu.course

        # 课程查询学生
        cou = Course.objexts.get(pk=1)
        cou.student_set.all()  # student是小写的类名, 没有related_name之前可以用
        cou.stu.all()
        return HttpResponse('多对多的查询成功')


def add_del_stu_cou(request):
    if request.method == 'GET':
        # 增删中间表的信息
        # 给小王分配'日语', '数电', '模电'
        stu = Student.objects.filter(s_name='小明').first()
        cou1 = Course.objects.get(c_name='日语')
        cou2 = Course.objects.get(c_name='数电')
        cou3 = Course.objects.get(c_name='模电')
        # 新增中间表数据
        stu.course.add(cou1)
        stu.course.add(cou2)
        stu.course.add(cou3)  # add方法加入课程(flask中用的append), 此时中间表就有信息了
        # cou1.stu.add(学生对象)  # 和上面一样的

        # 删除中间表数据  stu.course此时有3门课程
        stu.course.remove(cou2)
        stu.course.remove(cou3)  # 此时删掉了数电和模电课程
        return HttpResponse('操作中间表信息成功')


def on_delete_stu(request):
    if request.method == 'GET':
        # 演示删除, on_delete参数的效果
        stuinfo = StudentInfo.objects.get(id=6)
        stuinfo.delete()
        # models.CASCADE 表示:主键所在行的数据被删, 外键所在行的数据也会被删
        # models.PROTECT 表示:主键有对应的外键数据时, 不让删除主键的数据
        # models.SET_NULL 表示:主键删除, 外键置空
        # 常用的就上面三个
        return HttpResponse('on_delete演示成功')

# 模型关联关系

# 一对一

class A():
    id = modules.IntegerFiled()

class B():
    aa = mldels.OneToOneField(A, on_delete=models.CASCADE, null=True,related_name='cc')
  • OneToOneField(关联模型)
  • 模型定义
    • 关联名 = models.OneToOneField(关联的表名, related_name = '关系名', on_delete=models.CASCADE, null=True)
  • 已知 A 对象 a 查询 B 对象
    • 当 related_name 没定义时: a.b
    • 当 related_name = 'cc'时: a.cc
  • 已知 B 对象 b 查询 A 对象 b.aa
  • 一对一:定义在关联的两个模型中的任意一方都可以

# 一对多

class A():
    id

class B():
    aa = models.ForeignKey(A, on_delete=models.CASCADE, null=True,related_name='cc')
  • ForeignKey(关联模型)
  • 模型定义
    • aa = ForeignKey(A)
  • 已知 A 对象 a,查询 B 对象
    • 当 related_name 没定义时: a.b_set
    • 当 related_name = 'cc'时: a.cc
  • 已知 B 对象 b 查询 A 对象 b.aa
  • 一对多:定义在'多'的一方

# 多对多

  • course = models.ManyToManyField(Course 要进行关联的表的名字,related_name='stu') 会自动生成中间文件,中间文件的表名为 course
  • 查询 id 为 1 的学生课程信息
  • stu = Student.objects.get(pk=1)
  • 学生查询课程
    • stu.course
  • 课程查询学生
    • cou = Course.objects(pk=1)
    • 当 related_name 没定义时: cou.student_set.all()
    • 当 related_name 定义时: cou.stu.all()
  • 增加中间表信息
    • stu = Student.objects.filter(s_name = '小明').first()
    • cou1 = Course.objects.get(c_name='日语')
    • stu.course.add(cou1)
  • 删除中间表信息
    • stu.course.remove(cou1)
  • on_delete 参数
    • models.CASCADE 表示: 主键所在行的数据被删,外键所在行的数据也会被删
    • models.PROTECT 表示: 主键有对应的外键数据时,不让删除主键的数据
    • models.SET_NULL 表示: 主键删除,外键置空
  • 注意: ManyToManyFiled 定义的字段定义在任何一个关联模型中都可以