使用Factory Boy和GeoDjango PointFields

7
我正在为一个新的GeoDjango项目编写测试。通常我使用Factory BoyFaker来创建测试模型实例。然而,我不清楚如何模拟GeoDjango PointField字段。在查看Spatialite中的记录时,它似乎是一个二进制块。
我对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?

1
如果您不固定于Factory Boy,可以查看这个扩展程序,它是用于创建测试记录的首选库:https://github.com/sigma-geosistemas/mommy_spatial_generators - djangonaut
4个回答

11

我认为你需要为点实例创建一个自定义的模糊属性。你可以试试这个方法吗?目前我没有设置来运行它。

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

11

Factory Boy文档所述,Fussy即将被弃用

现在,由于FactoryBoy包含factory.Faker类,这些内置的模糊器中的大部分已被弃用,而推荐使用它们的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)

注意:因为我们只需要latlong值,不需要任何额外的元数据,所以coords_only必须始终为真。
注意2: generate()已经被弃用,请参见相关答案
最后,就像使用local_latlng提供程序或任何内置提供程序一样。以下是完整示例:
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文档中使用任何其他指定的国家代码。


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兼容的Points。

注册后:

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')

0

你也可以直接设置值。

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)]}
    ))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接