将 Factory Boy 与 GeoDjango PointFields 一起使用

Using Factory Boy with GeoDjango PointFields

我正在为我开始的新 GeoDjango 项目编写测试。通常我使用 Factory Boy and Faker to create model instances for testing. However it's not clear to me how you can mock GeoDjango PointField fields. When looking at the record in 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?

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

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

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

挑剔即将deprecated as Factory Boy documentation说。

Now that FactoryBoy includes the factory.Faker class, most of these built-in fuzzers are deprecated in favor of their Faker equivalents.

正如@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() 已弃用,参见

最后,就像使用 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 文档中的国家/地区代码。

也可以直接设置值

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