django数据表不设置外键进行关联查询

django orm数据表不设置外键进行关联查询

最近在django项目中有遇到关联查询但是数据表中字段未设置外键关联的情况,记录一下解决办法。

数据表
from django.db import models


class Book(models.Model):
    """书籍表"""
    name = models.CharField('名称', max_length=50)
    price = models.IntegerField('价格', default=50)
    publisher_id = models.IntegerField('出版社ID')

    class Meta:
        db_table = 'books_book'


class Publisher(models.Model):
    """出版社表"""
    name = models.CharField('出版社名称', max_length=50)
    addr = models.CharField('出版社地址', max_length=50)

    class Meta:
        db_table = 'books_publisher'
        
问题:查询book所有字段和publisher.name? (等同如下sql效果)
select books_book.*, books_publisher.name from books_book
left join books_publisher
on books_book.publisher_id = books_publisher.id;
方法一: 修改books_book表 models字段定义,改为逻辑关联

Tips: django中的ForeignKey与数据库中FOREIGN KEY约束并不一样,ForeignKey是一种逻辑上的关联关系,是否使用数据库中的外键约束通过db_contraint参数设置。

class Book(models.Model):
    """书籍表"""
    name = models.CharField('名称', max_length=50)
    price = models.IntegerField('价格', default=50)
    publisher = models.ForeignKey('Publisher', on_delete=models.CASCADE, db_contraint=False)

	class Meta:
	        db_table = 'books_book'


class Publisher(models.Model):
    """出版社表"""
    name = models.CharField('出版社名称', max_length=50)
    addr = models.CharField('出版社地址', max_length=50)

	class Meta:
	        db_table = 'books_publisher'

查询语句:

queryset = Book.objects.values(‘id’, ‘name’, ‘price’, ‘publisher__name’)

方法二:通过extra api函数实现

queryset = Book.objects.extra(select={‘publisher_name’: ‘SELECT books_publisher.name FROM books_publisher WHERE books_publisher.id = books_book.publisher_id’})

测试结果:
可以发现查询出的queryset中多了一个publisher_name属性,然后在序列化阶段将这个字段加上即可。

>>> from books.models import Book, Publisher
>>> queryset = Book.objects.extra(select={'publisher_name': 'SELECT books_publisher.name FROM books_publisher WHERE books_publisher.id = books_book.publisher_id'})
>>> for item in queryset:
...     print(item.__dict__)
...
{'_state': <django.db.models.base.ModelState object at 0x000002993198F208>, 'id': 1, 'name': '书本1', 'price': 20, 'publisher_id': 1, 'publisher_name': '工业出版社'}
方法三:执行原生sql实现

方法三与方法二比较类似

queryset = Book.objects.raw( ‘select books_book.*, books_publisher.name as publisher_name from books_book left join books_publisher on books_book.publisher_id = books_publisher.id;’)

测试结果

>>> queryset = Book.objects.raw( 'select books_book.*, books_publisher.name as publisher_name from books_book left join books_publisher on books_book.publisher_id = books_publisher.id;')
>>> for item in queryset:
...     print(item.__dict__)
...
{'_state': <django.db.models.base.ModelState object at 0x0000029931119748>, 'id': 1, 'name': '书本1', 'price': 20, 'publisher_id': 1, 'publisher_name': '工业出版社'}
方法四:在序列化阶段根据相关联的id字段再执行一次查询
# ProjectVersion model 序列化器

class BookSerializer(serializers.ModelSerializer):
    publisher_name = serializers.SerializerMethodField()

    def get_publisher_name(self, obj):
        """
        :param obj: Book实例
        """
        publisher = Publisher.objects.filter(id=obj.publisher_id).first()
        if publisher:
            return publisher.name
        else:
            return ''

通过查看SerializerMethodField源码可以发现绑定的序列化方法为:get_{field_name}

class SerializerMethodField(Field):
    """
    A read-only field that get its representation from calling a method on the
    parent serializer class. The method called will be of the form
    "get_{field_name}", and should take a single argument, which is the
    object being serialized.

    For example:

    class ExampleSerializer(self):
        extra_info = SerializerMethodField()

        def get_extra_info(self, obj):
            return ...  # Calculate some data to return.
    """
    def __init__(self, method_name=None, **kwargs):
        self.method_name = method_name
        kwargs['source'] = '*'
        kwargs['read_only'] = True
        super().__init__(**kwargs)

    def bind(self, field_name, parent):
        # In order to enforce a consistent style, we error if a redundant
        # 'method_name' argument has been used. For example:
        # my_field = serializer.SerializerMethodField(method_name='get_my_field')
        default_method_name = 'get_{field_name}'.format(field_name=field_name)

        # The method name should default to `get_{field_name}`.
        if self.method_name is None:
            self.method_name = default_method_name

        super().bind(field_name, parent)

    def to_representation(self, value):
        method = getattr(self.parent, self.method_name)
        return method(value)

参考链接:
https://www.h5w3.com/10765.html
https://www.cnblogs.com/shouke/p/13423151.html

  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值