Skip to main content
 首页 » 编程设计

python之在 Django GeoJSON 序列化器字段中指定相关模型的字段

2024年11月24日29fff_TT

我正在尝试使用 geojson 序列化器 在 map 中绘制经纬度点。对于此功能,我有两个名为 Activity 和 ClusterA 的模型。

Activity 是一种模型,用于存储项目中定义的某些事件的数据。此事件包含一个名为 location 的 PointField 字段。 这是我的 Activity 模型:

class Activity(models.Model): 
    name = models.CharField(max_length=200) 
    description = models.CharField(max_length=500) 
    target_number = models.IntegerField(null=True, blank=True) 
    target_unit = models.CharField(max_length=200, null=True, blank=True) 
    beneficiary_level = models.BooleanField(default=True) 
    weight = models.FloatField(default=0) 
    location = PointField(geography=True, srid=4326, blank=True, null=True) 
 
    def __str__(self): 
        return self.name 
 
    @property 
    def latitude(self): 
        if self.location: 
            return self.location.y 
 
    @property 
    def longitude(self): 
        if self.location: 
            return self.location.x 

同样,一个事件可以属于一个集群。此数据存储在模型 ClusterA(Cluster Activity)中。 ClusterA 是指特定于集群的事件。

集群模型

class Cluster(models.Model): 
    name = models.CharField(max_length=200) 
    ward = models.CharField(max_length=200) 
 
    def __str__(self): 
        return self.name 

ClusterA 模型

class ClusterA(models.Model): 
    activity = models.ForeignKey('Activity', related_name='clustera') 
    target_number = models.IntegerField(null=True, blank=True, default=0) 
    target_unit = models.CharField(max_length=200, null=True, blank=True, default='') 
    time_interval = models.ForeignKey(ProjectTimeInterval, related_name='cainterval', null=True, blank=True) 
    target_completed = models.IntegerField(null=True, blank=True, default=0) 
    interval_updated = models.BooleanField(default=False) 
    target_updated = models.BooleanField(default=False) 
    location = PointField(geography=True, srid=4326, blank=True, null=True) 
 
    def __str__(self): 
        return self.name 
 
    @property 
    def latitude(self): 
        if self.location: 
            return self.location.y 
 
    @property 
    def longitude(self): 
        if self.location: 
            return self.location.x 
 
    def save(self, force_insert=False, force_update=False, using=None, update_fields=None): 
        if not self.id: 
            if not self.activity.beneficiary_level: 
                self.target_unit = self.activity.target_unit 
            self.time_interval = self.activity.time_interval 
        return super(ClusterA, self).save() 

现在我使用一个函数返回集群事件的 geojson 数据:

def get_map_data(request): 
    ca = ClusterA.objects.all() 
    data = serialize( 
        'geojson', 
        ca, 
        geometry_field='location', 
        fields = ('activity', 'location', ) 
    ) 
    print(data) 
    return HttpResponse(data) 

我得到的输出是:

{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "EPSG:4326"}}, "features": [{"geometry": {"type": "Point", "coordinates": [85.336775, 27.542718]}, "type": "Feature", "properties": {"activity": 27}}, {"geometry": null, "type": "Feature", "properties": {"activity": 19}}, {"geometry": {"type": "Point", "coordinates": [85.336776, 27.735227]}, "type": "Feature", "properties": {"activity": 26}}]} 

activity 字段给出了activity 的id。但我需要事件名称,以便我可以在 map 中绘制的标记的弹出窗口中显示事件名称。

这就是我尝试在标记的弹出窗口中显示事件名称的方式:

onEachFeature: function (feature, layer) { 
   layer.bindPopup(feature.properties.name); 
}  

如果我将本地模型的字段传递给它,弹出窗口会显示其他数据。

我试过使用:

fields = ('activity__name', 'location', ) 

在get_map_data函数中,打印输出中不显示字段为:

{ 
  "type": "FeatureCollection", 
  "crs": { 
    "type": "name", 
    "properties": { 
      "name": "EPSG:4326" 
    } 
  }, 
  "features": [ 
    { 
      "geometry": { 
        "type": "Point", 
        "coordinates": [ 
          85.336775, 
          27.542718 
        ] 
      }, 
      "type": "Feature", 
      "properties": { 
 
      } 
    }, 
    { 
      "geometry": null, 
      "type": "Feature", 
      "properties": { 
 
      } 
    }, 
    { 
      "geometry": { 
        "type": "Point", 
        "coordinates": [ 
          85.336776, 
          27.735227 
        ] 
      }, 
      "type": "Feature", 
      "properties": { 
 
      } 
    } 
  ] 
} 

如您所见,在上述输出的 properties 中没有指定字段。

我需要的帮助是能够获取事件模型的名称字段而不是 id。

我正在使用 Django 1.8。

编辑: 添加select_related

后print(ca. dict)的输出
{'activity_id': 44,  
'target_unit': u'Check',  
'_state': <django.db.models.base.ModelState object at 0x7f57e19c8150>,  
'target_completed': 0,  
'cag_id': 35,  
'target_updated': False,  
'_activity_cache': <Activity: Test>,  
'location': <Point object at 0x7f57e19391c0>,  
'time_interval_id': 84,  
'target_number': 12,  
'interval_updated': False,  
'id': 72} 

自定义序列化器的错误回溯

ERROR 2019-06-12 14:40:15,638 base 27641 140154705491712 Internal Server Error: /core/get-map-data/ 
Traceback (most recent call last): 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/handlers/base.py", line 132, in get_response 
    response = wrapped_callback(request, *callback_args, **callback_kwargs) 
  File "/home/sanip/naxa/mes-core/onadata/apps/core/views.py", line 366, in get_map_data 
    data = serializers.serialize(ca, geometry_field='location', fields=('activity__name', 'location',)) 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/serializers/base.py", line 69, in serialize 
    self.end_object(obj) 
  File "/home/sanip/naxa/mes-core/onadata/apps/core/serializers.py", line 170, in end_object 
    super(CustomSerializer, self).end_object(obj) 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/serializers/json.py", line 61, in end_object 
    cls=DjangoJSONEncoder, **self.json_kwargs) 
  File "/usr/lib/python2.7/json/__init__.py", line 189, in dump 
    for chunk in iterable: 
  File "/usr/lib/python2.7/json/encoder.py", line 434, in _iterencode 
    for chunk in _iterencode_dict(o, _current_indent_level): 
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict 
    for chunk in chunks: 
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict 
    for chunk in chunks: 
  File "/usr/lib/python2.7/json/encoder.py", line 442, in _iterencode 
    o = _default(o) 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/serializers/json.py", line 115, in default 
    return super(DjangoJSONEncoder, self).default(o) 
  File "/usr/lib/python2.7/json/encoder.py", line 184, in default 
    raise TypeError(repr(o) + " is not JSON serializable") 
TypeError: <Point object at 0x7f783f0c92e0> is not JSON serializable 
Internal Server Error: /core/get-map-data/ 
Traceback (most recent call last): 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/handlers/base.py", line 132, in get_response 
    response = wrapped_callback(request, *callback_args, **callback_kwargs) 
  File "/home/sanip/naxa/mes-core/onadata/apps/core/views.py", line 366, in get_map_data 
    data = serializers.serialize(ca, geometry_field='location', fields=('activity__name', 'location',)) 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/serializers/base.py", line 69, in serialize 
    self.end_object(obj) 
  File "/home/sanip/naxa/mes-core/onadata/apps/core/serializers.py", line 170, in end_object 
    super(CustomSerializer, self).end_object(obj) 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/serializers/json.py", line 61, in end_object 
    cls=DjangoJSONEncoder, **self.json_kwargs) 
  File "/usr/lib/python2.7/json/__init__.py", line 189, in dump 
    for chunk in iterable: 
  File "/usr/lib/python2.7/json/encoder.py", line 434, in _iterencode 
    for chunk in _iterencode_dict(o, _current_indent_level): 
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict 
    for chunk in chunks: 
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict 
    for chunk in chunks: 
  File "/usr/lib/python2.7/json/encoder.py", line 442, in _iterencode 
    o = _default(o) 
  File "/home/sanip/.virtualenvs/mes/lib/python2.7/site-packages/django/core/serializers/json.py", line 115, in default 
    return super(DjangoJSONEncoder, self).default(o) 
  File "/usr/lib/python2.7/json/encoder.py", line 184, in default 
    raise TypeError(repr(o) + " is not JSON serializable") 
TypeError: <Point object at 0x7f783f0c92e0> is not JSON serializable 

请您参考如下方法:

默认情况下,django 序列化程序不提供 ForeignKey 值。所以你可以覆盖 geojson序列化器字段。例如:

from django.contrib.gis.serializers.geojson import Serializer  
 
class CustomSerializer(Serializer): 
 
    def end_object(self, obj): 
        for field in self.selected_fields: 
            if field == 'pk': 
                continue 
            elif field in self._current.keys(): 
                continue 
            else: 
                try: 
                    if '__' in field: 
                        fields = field.split('__') 
                        value = obj 
                        for f in fields: 
                            value = getattr(value, f) 
                        if value != obj: 
                            self._current[field] = value 
 
                except AttributeError: 
                    pass 
        super(CustomSerializer, self).end_object(obj) 

用法:

serializers = CustomSerializer() 
ca = ClusterA.objects.all() 
data = serializers.serialize(ca, geometry_field='location', fields=('activity__name', 'location', ))