【问题标题】:django select records from multiple tables with same foreign keydjango 从具有相同外键的多个表中选择记录
【发布时间】:2021-06-03 14:15:44
【问题描述】:

我想在 Django 中执行一个查询,该查询通过外键在多个表中检索相关数据。目前我必须在每个表上运行一个查询,例如(房屋、家具、人)使用门牌号作为过滤器。

在 SQL 中,我可以像这样在一个查询中做到这一点:

SELECT house.number, house.number_of_rooms, furniture.type, people.name
FROM (house INNER JOIN furniture ON house.number = furniture.house_number) 
INNER JOIN people ON house.number = people.house_number
WHERE (((house.number)="21"));

这可以在 Django 中完成吗?

请参阅下面的示例模型:

class House(models.Model):
    number = models.CharField('House Number', max_length=10, blank=True, unique=True, primary_key=True)
    number_of_rooms = models.IntegerField(default=1, null=True)

class Furniture(models.Model):
    house_number = models.ForeignKey(House, on_delete=models.CASCADE, null=True)
    type = models.CharField('Furniture Type', max_length=50)

class People(models.Model):
    house_number = models.ForeignKey(House, on_delete=models.CASCADE, null=True)
    first_name = models.CharField('First Name', max_length=50)

【问题讨论】:

    标签: django inner-join django-queryset


    【解决方案1】:

    在您的模型中为外键添加 related_name 参数,以便您可以检索与 House() 实例相关的对象。

    class Furniture(models.Model):
        house_number = models.ForeignKey(House, related_name='house_furniture', on_delete=models.CASCADE, null=True)
        type = models.CharField('Furniture Type', max_length=50)
    
    class People(models.Model):
        house_number = models.ForeignKey(House, related_name='house_people', on_delete=models.CASCADE, null=True)
        first_name = models.CharField('First Name', max_length=50)
    

    然后使用以下命令运行迁移。

    python manage.py makemigrations
    python manage.py migrate
    

    然后在同一个应用中创建一个新的 serializers.py 模块。

    #import models Furniture, People, house
    
    from rest_framework import serializers
    
    class FurnitureSerializer(serializer.ModelSerializer):
    
        class Meta:
            model = Furniture
            fields = ['type'] # if you want all the fields of model than user '__all__'.
    
    class PeopleSerializer(serializer.ModelSerializer):
        
        class Meta:
            model = People
            fields = ['first_name'] # if you want all the fields of model than user '__all__'.
    
    class HouseSerializer(serializer.ModelSerializer):
    
        house_furniture = FurnitureSerializer(many=True)
        house_people = PeopleSerializer(many=True)
    
        class Meta:
            model = Furniture
            fields = ['number', 'number_of_rooms', 'house_furniture', 'house_people'] 
    

    现在,在您的 views.py 中,您可以简单地查询模型 House 并使用 HouseSerializer() 序列化结果。

    #从models.py导入模型 #从 serializers.py 导入序列化程序

    从 rest_framework.views 导入 APIView 从 rest_framework.response 导入响应 从 rest_framework 导入状态 从 rest_framework.generics 导入 ListAPIView

    类 ListHouseView(ListAPIView):

    serializer_class = HouseSerializer
    queryset = House.objects.filter() #here you can apply filters on the fields of house model and user using related_name you can filter on other related models as well.
    

    现在,只需在您应用的 urls.py

    中调用 ad this
    url_pattern = [
        path('list-house/', ListHouseView.as_view()),
    ]
    

    确保您的项目的 urls.py 中有一条路径可以访问此应用的 urls.py

    【讨论】:

      【解决方案2】:

      处理此问题的常用 Django 方法是 Queryset.prefetch_related() 并通过 Python 进行迭代(除非您使用 Postgres,它有自己的 ArrayAgg 解决方案)。给定您的模型,它将花费三个查询,但您不必处理非规范化的行结果。

      h = House.objects.prefetch_related('furniture_set', 'people_set').get(number='21')
      for furniture in house.furniture_set.all():
          print(furniture)
      for person in house.people_set.all():
          print(people)
      

      prefetch_related() 缓存结果并在评估查询集后在 Python 中“加入”,因此遍历反向关系不会产生额外的查询,并且您可以随意构建/序列化数据.来自这里的原始 SQL 类似于:

      SELECT house.number, house.number_of_rooms FROM house WHERE house.number = '1'
      SELECT furniture.id, furniture.house_number_id, furniture.type FROM furniture WHERE furniture.house_number_id IN ('1')
      SELECT people.id, people.house_number_id, people.first_name FROM people WHERE people.house_number_id IN ('1')
      

      但 Django 会在幕后完成这项工作,这样您就可以在 Python 中处理模型实例。

      【讨论】:

        猜你喜欢
        • 2019-10-27
        • 2017-06-15
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        • 2020-12-23
        • 2013-05-29
        • 2017-07-31
        • 1970-01-01
        相关资源
        最近更新 更多