【问题标题】:Using Factory Boy with GeoDjango PointFields使用带有 GeoDjango PointFields 的 Factory Boy
【发布时间】:2015-12-26 01:02:27
【问题描述】:

我正在为我开始的一个新 GeoDjango 项目编写测试。通常我使用Factory BoyFaker 来创建模型实例进行测试。但是,我不清楚如何模拟 GeoDjango PointField 字段。查看Spacialite 中的记录时,它显示为二进制blob。

我对 GIS 完全陌生,对如何在 Django 中为 PointFields 创建工厂有点困惑。

# models.py

from django.contrib.gis.db import models

class Place(models.Model):
    name = models.CharField(max_length=255)
    location = models.PointField(blank=True, null=True)

    objects = models.GeoManager()

    def __str__(self):
        return "%s" % self.name
# factories.py

import factory
from faker import Factory as FakerFactory
from . import models

faker = FakerFactory.create()

class PlaceFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Place

    name = factory.LazyAttribute(lambda x: faker.name())
    #location = What do I do?

【问题讨论】:

标签: python django gis geodjango factory-boy


【解决方案1】:

我相信您需要为点实例创建自定义模糊属性。你能试试这个吗?现在我没有设置来运行它。

import random
from django.contrib.gis.geos import Point
from factory.fuzzy import BaseFuzzyAttribute

class FuzzyPoint(BaseFuzzyAttribute):
    def fuzz(self):
        return Point(random.uniform(-180.0, 180.0),
                     random.uniform(-90.0, 90.0))


class PlaceFactory(FakerFactory):
    name = factory.LazyAttribute(lambda x: faker.name())
    location = FuzzyPoint()
    class Meta:
        model = models.Place

【讨论】:

    【解决方案2】:

    Faker 已经有了一些不错的地理特征。您可以创建自己的提供程序以使其适用于 Django,例如:

    import factory
    from faker.providers import BaseProvider
    from django.contrib.gis.geos import Point
    
    class DjangoGeoLocationProvider(BaseProvider):
    
        countries = ['NL', 'DE', 'FR', 'BE']
    
        # uses faker.providers.geo
        def geolocation(self, country=None):
            country_code = country or factory.Faker('random_element', elements=self.countries).generate()
            faker = factory.Faker('local_latlng', country_code=country_code, coords_only=True)
            coords = faker.generate()
            return Point(x=float(coords[1]), y=float(coords[0]), srid=4326)
    

    这将为给定的国家/地区生成与 Django 兼容的积分。

    注册后:

    factory.Faker.add_provider(DjangoGeoLocationProvider)
    

    您可以像任何内置提供程序一样在 DjangoModelFactory 中使用它:

    from factory.django import DjangoModelFactory as Factory
    
    class PlaceFactory(Factory):
    
        class Meta:
            model = Place
    
        location = factory.Faker('geolocation')
        # or
        another_location = factory.Faker('geolocation', country='NL')
    

    【讨论】:

      【解决方案3】:

      正如Factory Boy documentation 所说,Fussy 即将成为deprecated

      现在 FactoryBoy 包含 factory.Faker 类,这些内置 fuzzer 中的大多数已被弃用,取而代之的是 Faker 等效项。

      正如@Steven B 所说,您必须创建自己的提供程序。我对他的代码做了一些更改,以使提供程序尽可能通用。

      class DjangoGeoPointProvider(BaseProvider):
      
          def geo_point(self, **kwargs):
              kwargs['coords_only'] = True
              # # generate() is not working in later Faker versions
              # faker = factory.Faker('local_latlng', **kwargs)
              # coords = faker.generate()  
              faker = factory.faker.faker.Faker()
              coords = faker.local_latlng(**kwargs)
              return Point(x=float(coords[1]), y=float(coords[0]), srid=4326)
      

      注意coords_only 必须始终为真,因为我们只需要 latlong 值,不需要任何额外的元数据。

      注意 2generate() 已弃用,请参阅 related answer

      最后,就像使用local_latlng provider 或任何内置提供程序一样。这是一个完整的例子:

      class TargetFactory(factory.django.DjangoModelFactory):
          factory.Faker.add_provider(DjangoGeoPointProvider)
      
          class Meta:
              model = Target
      
          radius = factory.Faker('random_int', min=4500, max=90000)
          location = factory.Faker('geo_point', country_code='US')
      

      注意:“US”是默认国家代码,在本例中可以省略,但您可以使用 Faker 文档中指定的任何其他国家代码。

      【讨论】:

        【解决方案4】:

        也可以直接设置值。

        import json
        
        from factory import LazyAttribute
        from factory.django import DjangoModelFactory
        
        
        class PlaceFactory(DjangoModelFactory):
            static_location = "{'type': 'Point', 'coordinates': [1,1]}"
            random_location = LazyAttribute(lambda x: json.dumps({
                'type': 'Point',
                'coordinates': [random.uniform(-180.0, 180.0), random.uniform(-180.0, 180.0)]}
            ))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-03
          • 2018-02-06
          • 1970-01-01
          • 2020-04-09
          • 1970-01-01
          • 2019-11-05
          • 2021-03-01
          • 1970-01-01
          相关资源
          最近更新 更多