反向查询详解以Django为例

发布于:2025-04-06 ⋅ 阅读:(19) ⋅ 点赞:(0)

以下给出两张表格

class User(AbstractUser):
    mobile=models.CharField(max_length=11,default=0,unique=True,verbose_name='手机号')
    email_active=models.BooleanField(default=False,verbose_name='邮箱验证状态')
    default_address = models.ForeignKey('Address', related_name='users', null=True, blank=True,
                                        on_delete=models.SET_NULL, verbose_name='默认地址')
    class Meta:
        db_table = 'tb_users'
        verbose_name="用户"
        verbose_name_plural=verbose_name
    def __str__(self):
        return self.username



from utils.models import BaseModel
class Address(BaseModel):
"""用户地址"""
   user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='addresses', verbose_name='用户')
   title = models.CharField(max_length=20, verbose_name='地址名称')
   receiver = models.CharField(max_length=20, verbose_name='收货人')
   province=models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='province_addresses', verbose_name='省')
   city = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='city_addresses', verbose_name='市')
   district = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='district_addresses', verbose_name='区')
   place = models.CharField(max_length=50, verbose_name='地址')
   mobile = models.CharField(max_length=11, verbose_name='手机')
   tel = models.CharField(max_length=20, null=True, blank=True, default='', verbose_name='固定电话')
   email = models.CharField(max_length=30, null=True, blank=True, default='', verbose_name='电子邮箱')
   is_deleted = models.BooleanField(default=False, verbose_name='逻辑删除')

class Meta:
   db_table = 'tb_address'
   verbose_name = '用户地址'
   verbose_name_plural = verbose_name
   ordering = ['-update_time']

问题集:

主键和外键名字为什么不是一模一样

主键是用于唯一标识表中一条记录的字段,而外键是用来建立表与表之间关联的字段。外键的名称通常不会和主键完全相同,主要是为了避免混淆,并且在一个表中可能会有多个外键指向同一张表。例如,在 Address 表中:

province、city、district 都是外键,指向 areas.Area 表。

它们分别表示 省、市、区,虽然都指向同一张表 areas.Area,但代表不同的地址层级,所以它们的名字必须不同,否则会引起歧义。

为什么province,city,district 是外键?

在 Address 表中,province、city 和 district 这三个字段都是指向 areas.Area 表的外键,它们的作用是让每个 Address 记录的省、市、区字段都与 Area 表中的相应区域关联起来。

假设 areas.Area 表的结构如下:

class Area(BaseModel): name = models.CharField(max_length=20, verbose_name="名称") parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name="父级地区")

这样:

province 关联的是 Area 表中的 省级 数据;

city 关联的是 Area 表中的 市级 数据;

district 关联的是 Area 表中的 区县级 数据。


related_name 的作用

related_name 主要用于 Django 进行 反向查询,即:通过 外键关系 反向访问关联的表。默认情况下,Django 通过 外键名_set 进行反向查询,例如 area.address_set.all(),但 related_name 可以自定义这个反向查询的名称,提高代码的可读性和直观性。

在 Address 表中:

province = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='province_addresses', verbose_name='省') city = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='city_addresses', verbose_name='市') district = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='district_addresses', verbose_name='区')

表示:

province_addresses:查询所有 该省 下的地址

city_addresses:查询所有 该市 下的地址

district_addresses:查询所有 该区 下的地址

举例

假设 areas.Area 表中有以下数据:

id name parent_id
1 江苏省 NULL
2 南京市 1
3 玄武区 2

在 Address 表中,有如下数据:

id user_id province city district place
1 10 1 2 3 XX路100号
如何使用 related_name 进行查询?

# 获取江苏省(id=1)下的所有地址 province = Area.objects.get(id=1) print(province.province_addresses.all()) # 输出:<QuerySet [<Address: XX路100号>]> ​ # 获取南京市(id=2)下的所有地址 city = Area.objects.get(id=2) print(city.city_addresses.all()) # 输出:<QuerySet [<Address: XX路100号>]> ​ # 获取玄武区(id=3)下的所有地址 district = Area.objects.get(id=3) print(district.district_addresses.all()) # 输出:<QuerySet [<Address: XX路100号>]>

如果不使用 related_name,查询方法会变成:

print(province.address_set.all()) # 默认反向查询,名字不直观

所以 related_name 的作用是让查询逻辑更加清晰和直观。

2.什么是正向查询和反向查询

1. 正向查询

正向查询是指通过 外键字段从子表(即引用外键的表)查询父表(即外键所指向的表)中的数据。

在 Django 中,正向查询是从 子模型 到 父模型 的查询。例如,从 Address 表查询某个地址的 province(省),即通过 Address 中的 province 外键字段,查找 Area 表中的 name 字段。

正向查询的例子

假设我们有以下模型:

class Area(models.Model): name = models.CharField(max_length=100) class Address(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) province = models.ForeignKey(Area, on_delete=models.PROTECT) city = models.ForeignKey(Area, on_delete=models.PROTECT)

如果你想从 Address 查询 province 对应的 Area,你可以执行正向查询:

address = Address.objects.get(id=1) print(address.province.name) # 获取地址的省名

结果:

print(address.province.name) # 输出: 河北省

*2. 反向查询*

反向查询是指从 父表 查询到 子表 关联的数据。在 Django 中,当你在 父模型(即引用外键的表)上使用反向查询时,它会自动创建一个从父模型到子模型的查询方法。

例如,你可以通过 Area 查询所有与某个 province(省)相关的 Address 记录。这里,Area 是父表,而 Address 是子表。

在 Address 模型中,你定义了:

province = models.ForeignKey('Area', on_delete=models.PROTECT, related_name='province_addresses')

related_name='province_addresses' 是反向查询的名字,它允许你通过 Area 查询到所有与该 Area 关联的 Address 记录。

反向查询的例子

从 Area 查询与之关联的所有 Address,例如查找所有与某个省(province_id)相关的地址:

province = Area.objects.get(id=130000) addresses_in_province = province.province_addresses.all()

结果:

for address in addresses_in_province: print(address.title, address.receiver)