Skip to content
This repository has been archived by the owner on Oct 22, 2019. It is now read-only.

Commit

Permalink
Merge branch 'release/1.5.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
berinhard committed Jan 16, 2018
2 parents 2a81da5 + c346f46 commit 6b37be7
Show file tree
Hide file tree
Showing 11 changed files with 261 additions and 36 deletions.
5 changes: 5 additions & 0 deletions Changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ TODO
Development
-----------

1.5.1
-----
- Support for GIS fields when `django.contrib.gis` is installed
- Do not create refrence if FK is specified with id (<name>_id)

1.5.0
-----
- Allow usage with Models which include a model attribute
Expand Down
2 changes: 1 addition & 1 deletion model_mommy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#coding:utf-8
__version__ = '1.5.0'
__version__ = '1.5.1'
__title__ = 'model_mommy'
__author__ = 'Vanderson Mota'
__license__ = 'Apache 2.0'
7 changes: 6 additions & 1 deletion model_mommy/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
FloatField, FileField, ImageField, IPAddressField,
ForeignKey, ManyToManyField, OneToOneField, NullBooleanField)

from model_mommy.utils import import_if_str
from .gis import default_gis_mapping
from .utils import import_if_str

try:
from django.db.models import BigIntegerField
Expand Down Expand Up @@ -50,6 +51,7 @@
HStoreField = None

from . import random_gen

default_mapping = {
ForeignKey: random_gen.gen_related,
OneToOneField: random_gen.gen_related,
Expand Down Expand Up @@ -98,6 +100,9 @@
if HStoreField:
default_mapping[HStoreField] = random_gen.gen_hstore

# Add GIS fields
default_mapping.update(default_gis_mapping)


def get_type_mapping():
mapping = default_mapping.copy()
Expand Down
30 changes: 30 additions & 0 deletions model_mommy/gis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from __future__ import absolute_import
from django.apps import apps

MOMMY_GIS = apps.is_installed("django.contrib.gis")

default_gis_mapping = {}

__all__ = ['MOMMY_GIS', 'default_gis_mapping']

if MOMMY_GIS:
from . import random_gen
from django.contrib.gis.db.models import (
GeometryField,
PointField,
LineStringField,
PolygonField,
MultiPointField,
MultiLineStringField,
MultiPolygonField,
GeometryCollectionField,
)

default_gis_mapping[GeometryField] = random_gen.gen_geometry
default_gis_mapping[PointField] = random_gen.gen_point
default_gis_mapping[LineStringField] = random_gen.gen_line_string
default_gis_mapping[PolygonField] = random_gen.gen_polygon
default_gis_mapping[MultiPointField] = random_gen.gen_multi_point
default_gis_mapping[MultiLineStringField] = random_gen.gen_multi_line_string
default_gis_mapping[MultiPolygonField] = random_gen.gen_multi_polygon
default_gis_mapping[GeometryCollectionField] = random_gen.gen_geometry_collection
3 changes: 2 additions & 1 deletion model_mommy/mommy.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ def _make(self, commit=True, commit_related=True, _save_kwargs=None, **attrs):
else:
self.m2m_dict[field.name] = self.model_attrs.pop(field.name)
elif field.name not in self.model_attrs:
self.model_attrs[field.name] = self.generate_value(field, commit_related)
if not isinstance(field, ForeignKey) or '{0}_id'.format(field.name) not in self.model_attrs:
self.model_attrs[field.name] = self.generate_value(field, commit_related)
elif callable(self.model_attrs[field.name]):
self.model_attrs[field.name] = self.model_attrs[field.name]()
elif field.name in self.iterator_attrs:
Expand Down
104 changes: 99 additions & 5 deletions model_mommy/random_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
import warnings
from decimal import Decimal
from os.path import abspath, join, dirname
from random import randint, choice, random
from django.core.files.base import ContentFile
from django.core.exceptions import ValidationError
from random import randint, choice, random, uniform

import six
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile

from model_mommy.timezone import now

Expand All @@ -27,21 +28,23 @@
except NameError:
pass


MAX_LENGTH = 300
# Using sys.maxint here breaks a bunch of tests when running against a
# Postgres database.
MAX_INT = 10000


def get_content_file(content, name):
return ContentFile(content, name=name)


def gen_file_field():
name = 'mock_file.txt'
file_path = abspath(join(dirname(__file__), name))
with open(file_path, 'rb') as f:
return get_content_file(f.read(), name=name)


def gen_image_field():
name = 'mock-img.jpeg'
file_path = abspath(join(dirname(__file__), name))
Expand All @@ -58,6 +61,7 @@ class KidMommy(Mommy):
'''
return lambda: choice(list(L))


# -- DEFAULT GENERATORS --


Expand All @@ -84,9 +88,10 @@ def gen_decimal(max_digits, decimal_places):
num_as_str = lambda x: ''.join([str(randint(0, 9)) for i in range(x)])
if decimal_places:
return Decimal("%s.%s" % (num_as_str(max_digits - decimal_places - 1),
num_as_str(decimal_places)))
num_as_str(decimal_places)))
return Decimal(num_as_str(max_digits))


gen_decimal.required = ['max_digits', 'decimal_places']


Expand All @@ -104,12 +109,16 @@ def gen_time():

def gen_string(max_length):
return str(''.join(choice(string.ascii_letters) for i in range(max_length)))


gen_string.required = ['max_length']


def gen_slug(max_length):
valid_chars = string.ascii_letters + string.digits + '_-'
return str(''.join(choice(valid_chars) for i in range(max_length)))


gen_slug.required = ['max_length']


Expand Down Expand Up @@ -145,6 +154,7 @@ def gen_ipv46():
ip_gen = choice([gen_ipv4, gen_ipv6])
return ip_gen()


def gen_ip(protocol, default_validators):
protocol = (protocol or '').lower()

Expand All @@ -170,21 +180,26 @@ def gen_ip(protocol, default_validators):
generator = gen_ipv46

return generator()


gen_ip.required = ['protocol', 'default_validators']


def gen_byte_string(max_length=16):
generator = (randint(0, 255) for x in range(max_length))
if six.PY2:
return "".join(map(chr, generator))
elif six.PY3:
return bytes(generator)


def gen_interval(interval_key='milliseconds'):
from datetime import timedelta
interval = gen_integer()
kwargs = {interval_key: interval}
return timedelta(**kwargs)


def gen_content_type():
from django.contrib.contenttypes.models import ContentType
try:
Expand All @@ -200,6 +215,7 @@ def gen_content_type():
warnings.warn('Database access disabled, returning ContentType raw instance')
return ContentType()


def gen_uuid():
import uuid
return uuid.uuid4()
Expand All @@ -216,6 +232,7 @@ def gen_json():
def gen_hstore():
return {}


def _fk_model(field):
try:
return ('model', field.related_model)
Expand All @@ -231,11 +248,88 @@ def _prepare_related(model, **attrs):
def gen_related(model, **attrs):
from .mommy import make
return make(model, **attrs)


gen_related.required = [_fk_model]
gen_related.prepare = _prepare_related


def gen_m2m(model, **attrs):
from .mommy import make, MAX_MANY_QUANTITY
return make(model, _quantity=MAX_MANY_QUANTITY, **attrs)


gen_m2m.required = [_fk_model]


# GIS generators

def gen_coord():
return uniform(0, 1)


def gen_coords():
return '{x} {y}'.format(x=gen_coord(), y=gen_coord())


def gen_point():
return 'POINT ({})'.format(
gen_coords(),
)


def _gen_line_string_without_prefix():
return '({}, {})'.format(
gen_coords(),
gen_coords(),
)


def gen_line_string():
return 'LINESTRING {}'.format(
_gen_line_string_without_prefix()
)


def _gen_polygon_without_prefix():
start = gen_coords()
return '(({}, {}, {}, {}))'.format(
start,
gen_coords(),
gen_coords(),
start
)


def gen_polygon():
return 'POLYGON {}'.format(
_gen_polygon_without_prefix(),
)


def gen_multi_point():
return 'MULTIPOINT (({}))'.format(
gen_coords(),
)


def gen_multi_line_string():
return 'MULTILINESTRING ({})'.format(
_gen_line_string_without_prefix(),
)


def gen_multi_polygon():
return 'MULTIPOLYGON ({})'.format(
_gen_polygon_without_prefix(),
)


def gen_geometry():
return gen_point()


def gen_geometry_collection():
return 'GEOMETRYCOLLECTION ({})'.format(
gen_point(),
)
31 changes: 21 additions & 10 deletions runtests.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
#!/usr/bin/env python

from os.path import dirname, join
import sys
from optparse import OptionParser
import warnings
from optparse import OptionParser
from os.path import dirname, join

import django


def parse_args():
parser = OptionParser()
parser.add_option('--use-tz', dest='USE_TZ', action='store_true')
parser.add_option('--postgresql', dest='USE_POSTGRESQL', action='store_true')
parser.add_option('--postgis', dest='USE_POSTGIS', action='store_true')
return parser.parse_args()


def configure_settings(options):
from django.conf import settings

Expand All @@ -25,7 +29,7 @@ def configure_settings(options):
'NAME': ':memory:',
}
},
INSTALLED_APPS = (
INSTALLED_APPS=(
'test.generic',
'django.contrib.contenttypes',
'test_without_migrations',
Expand All @@ -38,9 +42,13 @@ def configure_settings(options):
MIDDLEWARE_CLASSES=(),
)
if getattr(options, 'USE_POSTGRESQL', False):
if getattr(options, 'USE_POSTGIS', False):
engine = 'django.contrib.gis.db.backends.postgis'
else:
engine = 'django.db.backends.postgresql_psycopg2'
params['DATABASES'] = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'ENGINE': engine,
'NAME': 'model_mommy',
'TEST_NAME': 'test_model_mommy',
'USER': 'postgres',
Expand All @@ -59,6 +67,7 @@ def configure_settings(options):

return settings


# We only need this if HstoreFields are a possibility
from django.db.backends.signals import connection_created
from django.test.runner import DiscoverRunner
Expand Down Expand Up @@ -86,13 +95,13 @@ def get_runner(settings):
Asks Django for the TestRunner defined in settings or the default one.
'''
from django.test.utils import get_runner
extra_apps = []
if getattr(options, 'USE_POSTGRESQL', False):
setattr(settings, 'INSTALLED_APPS',
['django.contrib.postgres']
+ list(getattr(settings, 'INSTALLED_APPS')))
setattr(settings, 'INSTALLED_APPS',
['django.contrib.auth']
+ list(getattr(settings, 'INSTALLED_APPS')))
extra_apps.append('django.contrib.postgres')
if getattr(options, 'USE_POSTGIS', False):
extra_apps.append('django.contrib.gis')
extra_apps.append('django.contrib.auth')
setattr(settings, 'INSTALLED_APPS', extra_apps + list(getattr(settings, 'INSTALLED_APPS')))
from test_without_migrations.management.commands._base import DisableMigrations
setattr(settings, 'MIGRATION_MODULES', DisableMigrations())
TestRunner = get_runner(settings)
Expand All @@ -114,4 +123,6 @@ def runtests(options=None, labels=None):

if __name__ == '__main__':
options, labels = parse_args()
if getattr(options, 'USE_POSTGIS', False):
setattr(options, 'USE_POSTGRESQL', True)
runtests(options, labels)
Loading

0 comments on commit 6b37be7

Please sign in to comment.