11 ORM关联表、事务

    • ORM对关联表的操作
      • 在models.py中定义两个Model,对应两张表
      •  1 # common.models.py
         2 # 国家表
         3 class Country(models.Model):
         4     name = models.CharField(max_length=100)
         5 
         6 # 学生表, country 字段是国家表的外键,形成一对多的关系
         7 class Student(models.Model):
         8     name    = models.CharField(max_length=100)
         9     grade   = models.PositiveSmallIntegerField()
        10     country = models.ForeignKey(Country,
        11                                 on_delete=models.PROTECT)
      • 执行python manage.py makemigrations common
      • 执行python manage.py migrate
      • 执行python manage.py shell
      • 输入代码
        from common.models import *
        c1 = Country.objects.create(name='中国')
        c2 = Country.objects.create(name='美国')
        c3 = Country.objects.create(name='法国')
        Student.objects.create(name='白月', grade=1, country=c1)
        Student.objects.create(name='黑羽', grade=2, country=c1)
        Student.objects.create(name='大罗', grade=1, country=c1)
        Student.objects.create(name='真佛', grade=2, country=c1)
        Student.objects.create(name='Mike', grade=1, country=c2)
        Student.objects.create(name='Gus',  grade=1, country=c2)
        Student.objects.create(name='White', grade=2, country=c2)
        Student.objects.create(name='Napolen', grade=2, country=c3)
    • 外键表字段访问(Student->Country)  
      • s1 = Student.objects.get(name = '白月'):获取到一个学生对象,怎么得到他的国家名称?
      • s1.country.name:获取国家的名称
    • 外键表字段过滤  
      • 获取Student表中所有一年级的学生:Student.objects.filter(grade=1).values()
      • 查找Student表中所有一年级中国学生:
      • cn = Country.objects.get(name='中国')   Student.objects.filter(grade=1,country_id=cn.id).values()   
      • cn = Country.objects.get(name='中国')   Student.objects.filter(grade=1,country=cn).values()   
      • 上面的方法,写起来麻烦,有两步操作,需要发送两次数据请求给数据库服务器,性能不高
      • 中间方案:Student.objects.filter(grade=1,country__name='中国').values('name','country__name')
      • 选择出来的记录中,国家名是country__name,两个下划线比较怪
      • 有时候前后端接口的设计者,定义好了接口格式,如果要求一定是countryname这个怎么办呢?
      • 最终方案:
        from django.db.models import F
        
        # annotate 可以将表字段进行别名处理
        Student.objects.annotate(
            countryname=F('country__name'),
            studentname=F('name')
            ).filter(grade=1,countryname='中国').values('studentname','countryname')
  • 外键表反向访问
    • 外键表字段访问时通过表外键字段表示,比如Student表的country字段
    • 反向关系时通过表Model名转化成小写表示的
    • 如果已经获取了一个Country对象,如何获取到所有属于这个国家的学生呢?:通过表Model名转化为小写,后面加上_set来获取所有的反向外键关联对象
    • cn = Country.objects.get(name='中国')cn.student_set.all()
    • django给出了一个方法,可以更直观的反应关联关系,在定义Model的时候,外键字段使用related_name参数

 

# 国家表
class Country(models.Model):
    name = models.CharField(max_length=100)

# country 字段是国家表的外键,形成一对多的关系
class Student(models.Model):
    name    = models.CharField(max_length=100)
    grade   = models.PositiveSmallIntegerField()
    country = models.ForeignKey(Country,
                on_delete = models.PROTECT,
                # 指定反向访问的名字
                related_name='students')

 

    • 可能会遇到AttributeError: 'Country' object has no attribute 'students':关闭django shell,重启启动即可。
  • 反向过滤
    • 如果我们要获取所有具有一年级学生的国家名?
    • country_ids = Student.objects.filter(grade=1).values_list('country',flat=True)  Country.objects.filter(id__in=country_ids).values()
    • 使用distinct()消除重复:
    • # 先获取去重后的国家ID
      country_ids = Student.objects.filter(grade=1).values_list('country', flat=True).distinct()
      # 再查询
      Country.objects.filter(id__in=country_ids).values()
    •   Country.objects.filter(students__grade=1).values() 
    •   Country.objects.filter(students__grade=1).values().distinct() 
    • 据说.distinct()对MySQL数据库无效
posted @ 2025-12-07 17:52  理想赵雷  阅读(3)  评论(0)    收藏  举报