【问题标题】:How to serialize a Join query set from several tables in Django Rest Framework如何从 Django Rest Framework 中的多个表中序列化 Join 查询集
【发布时间】:2021-09-25 16:08:30
【问题描述】:

有时,框架使事情变得更复杂,而不是简化它们。我想序列化一个像这样的连接

queryset = Cities.objects.raw("SELECT 1 as id, cities.name as ci, states.name as s, countries.name as co FROM cities JOIN states ON states.id = cities.state_id LEFT OUTER JOIN countries ON countries.id = states.country_id WHERE cities.name = %s", [city])

或者喜欢这个,如果不推荐原始查询

city = self.request.query_params.get("cityname")

如您所见,这是一个反向连接。这个想法是序列化这样的结果集

0:  
name:   "Guadalajara"
state:  "Castilla La Mancha"
country:    "Spain"

1:
name: "Guadalajara"
state: "Jalisco"
coutry: "Mexico"

两个城市名称相同,但属于不同的州和国家。我需要这个来实现一种自动完成功能。这实际上是伪代码,但它提供了我想要获得的 JSON 结果类型的想法。

我阅读了文档并搜索了互联网,但我没有找到关于如何执行此操作的任何内容。

我是 Django 新手,我完全迷路了,这是一个很容易手动完成的简单任务,但我不知道如何使用 Django Rest Framework(或 Django 的任何其他工具)来实现这一点)。

任何帮助将不胜感激。

【问题讨论】:

  • 该结果集是什么格式的? 01 如果那是 json,是某种键吗?你能分享你的模型吗?

标签: python django serialization django-rest-framework django-queryset


【解决方案1】:

我找到了一个解决方案,我不知道它是否是最好的解决方案,但它对我有用,我想分享它。如果有人找到更好的方法来做到这一点,欢迎您提出更改建议。

我的模特:

""" The following 3 models deal with tables that already exist in the database """
class Countries(models.Model):
    name = models.CharField(max_length=100)
    iso3 = models.CharField(max_length=3, blank=True, null=True)
    iso2 = models.CharField(max_length=2, blank=True, null=True)
    phonecode = models.CharField(max_length=255, blank=True, null=True)
    capital = models.CharField(max_length=255, blank=True, null=True)
    currency = models.CharField(max_length=255, blank=True, null=True)
    currency_symbol = models.CharField(max_length=255, blank=True, null=True)
    tld = models.CharField(max_length=255, blank=True, null=True)
    native = models.CharField(max_length=255, blank=True, null=True)
    region = models.CharField(max_length=255, blank=True, null=True)
    subregion = models.CharField(max_length=255, blank=True, null=True)
    timezones = models.TextField(blank=True, null=True)
    translations = models.TextField(blank=True, null=True)
    latitude = models.DecimalField(max_digits=10, decimal_places=8, blank=True, null=True)
    longitude = models.DecimalField(max_digits=11, decimal_places=8, blank=True, null=True)
    emoji = models.CharField(max_length=191, blank=True, null=True)
    emojiu = models.CharField(db_column='emojiU', max_length=191, blank=True, null=True)  # Field name made lowercase.
    created_at = models.DateTimeField(blank=True, null=True)
    updated_at = models.DateTimeField()
    flag = models.IntegerField()
    wikidataid = models.CharField(db_column='wikiDataId', max_length=255, blank=True, null=True)  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'countries'


class States(models.Model):
    name = models.CharField(max_length=255)
    country = models.ForeignKey(Countries, models.DO_NOTHING)
    country_code = models.CharField(max_length=2)
    fips_code = models.CharField(max_length=255, blank=True, null=True)
    iso2 = models.CharField(max_length=255, blank=True, null=True)
    latitude = models.DecimalField(max_digits=10, decimal_places=8, blank=True, null=True)
    longitude = models.DecimalField(max_digits=11, decimal_places=8, blank=True, null=True)
    created_at = models.DateTimeField(blank=True, null=True)
    updated_at = models.DateTimeField()
    flag = models.IntegerField()
    wikidataid = models.CharField(db_column='wikiDataId', max_length=255, blank=True, null=True)  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'states'


class Cities(models.Model):
    name = models.CharField(max_length=255)
    state = models.ForeignKey('States', models.DO_NOTHING)
    state_code = models.CharField(max_length=255)
    country = models.ForeignKey('Countries', models.DO_NOTHING)
    country_code = models.CharField(max_length=2)
    latitude = models.DecimalField(max_digits=10, decimal_places=8)
    longitude = models.DecimalField(max_digits=11, decimal_places=8)
    created_at = models.DateTimeField()
    updated_at = models.DateTimeField()
    flag = models.IntegerField()
    wikidataid = models.CharField(db_column='wikiDataId', max_length=255, blank=True, null=True)  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'cities'

当然,数据库来自这个项目,如果有人需要的话 https://dr5hn.github.io/countries-states-cities-database/

这是我的观点

class CityViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = CitiesSerializer
    def get_queryset(self):
        city = self.request.query_params.get("cityname")
        queryset = Cities.objects.filter(name__startswith=city)
        return queryset

最后是我的 serializers.py

""" Fields:
'name','country','country-code','fips_code','iso2','latitude','longitude','created_at','updated_at','flag','wikidataid'
"""
class StatesSerializer(serializers.ModelSerializer):    
    class Meta:
        model = States
        fields = ['name', 'iso2']

""" Fields 
'name','iso3','iso2','phonecode','capital','currency_symbol','tld','native','region','subregion','timezones','translations','latitude','longitude','emoji','emojiu','created_at','updated_at','flag','wikidataid'
"""
class CountriesSerializer(serializers.ModelSerializer):
    class Meta:
        model = Countries
        fields = ['name', 'iso2', ]#'translations']


"""Fields:
'name','state','state_code','country','country_code','latitude','longitude','created_at','updated_at','flag','wikidataid'
"""
class CitiesSerializer(serializers.ModelSerializer):
    state = StatesSerializer(many=False)
    country = CountriesSerializer(many=False)

    class Meta:
        model = Cities
        fields = ['name', 'state', 'country', 'latitude', 'longitude']

使用此代码,我将获得正确的预期 JSon 输出。当然,它必须由适当的 forntend 来阅读,例如 JQuery、Vue 甚至 Vanilla JavaScript,但问题的对象是完整的。

如果你知道更好的方法,请不要犹豫。

【讨论】:

    【解决方案2】:

    DRF 提供通用视图以使用特定模型序列化对象集合。假设您有一个名为 City 的模型,如下所示:

    from django.db import models
    
    class City(models.Model):
        name = models.CharField()
        state = models.CharField()
        country = models.CharField()
    

    还有一个像这样的序列化器:

    from rest_framework import serializers
    
    class CitySerializer(serializers.ModelSerializer):
        class Meta:
             model = City
             fields = ['name', 'state', 'country']
    

    在您看来,您需要像这样从generics.ListAPIView 继承:

    from rest_framwork.generics import ListAPIView
    
    class CityList(ListAPIView):
        serializer_class = CitySerializer
    
        def get_queryset(self):
            return City.objects.filter(
                name=self.request.query_params.get('cityname')
            )
    

    ListAPIView 类很容易为您序列化 City 实例列表。你可以在这里找到进一步的解释:https://www.django-rest-framework.org/api-guide/generic-views/#listapiview

    【讨论】:

    • 这不能回答我的问题。 name、state 和 country 不是来自同一张表。它们来自具有一对多关系的 3 个不同的表。我已经有了模型、数据库甚至数据。我需要做的是执行与我在示例中提供的查询完全相同的查询。我需要序列化由此产生的结果集并将其作为 JSON 提供以用于自动完成目的。我不需要或不想修改模型。这不是交易。
    猜你喜欢
    • 2020-11-27
    • 1970-01-01
    • 2014-04-16
    • 2017-07-09
    • 2017-11-02
    • 1970-01-01
    • 2019-02-17
    • 2014-04-13
    • 2023-03-30
    相关资源
    最近更新 更多