Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve code comments #31

Merged
merged 5 commits into from
Nov 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ repos:
rev: 3.7.6
hooks:
- id: flake8
- repo: https://gitlab.com/pycqa/pydocstyle
rev: 4.0.1
hooks:
- id: pydocstyle
1 change: 1 addition & 0 deletions model_bakery/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Model Bakery module configuration."""
__version__ = '1.0.2'
__title__ = 'model_bakery'
__author__ = 'Vanderson Mota'
Expand Down
80 changes: 38 additions & 42 deletions model_bakery/baker.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
ModelNotFound, AmbiguousModelName, InvalidQuantityException, RecipeIteratorEmpty,
CustomBakerNotFound, InvalidCustomBaker
)
from .utils import import_from_str, import_if_str
from .utils import import_from_str

recipes = None

Expand All @@ -35,10 +35,10 @@ def _valid_quantity(quantity):

def make(_model, _quantity=None, make_m2m=False, _save_kwargs=None, _refresh_after_create=False,
_create_files=False, **attrs):
"""
Creates a persisted instance from a given model its associated models.
It fill the fields with random values or you can specify
which fields you want to define its values by yourself.
"""Create a persisted instance from a given model its associated models.

It fill the fields with random values or you can specify which
fields you want to define its values by yourself.
"""
_save_kwargs = _save_kwargs or {}
baker = Baker.create(_model, make_m2m=make_m2m, create_files=_create_files)
Expand All @@ -62,11 +62,10 @@ def make(_model, _quantity=None, make_m2m=False, _save_kwargs=None, _refresh_aft


def prepare(_model, _quantity=None, _save_related=False, **attrs):
"""
Creates BUT DOESN'T persist an instance from a given model its
associated models.
It fill the fields with random values or you can specify
which fields you want to define its values by yourself.
"""Create but do not persist an instance from a given model.

It fill the fields with random values or you can specify which
fields you want to define its values by yourself.
"""
baker = Baker.create(_model)
if _valid_quantity(_quantity):
Expand Down Expand Up @@ -96,18 +95,20 @@ def prepare_recipe(baker_recipe_name, _quantity=None, _save_related=False, **new


class ModelFinder(object):
"""
Encapsulates all the logic for finding a model to Baker.
"""
"""Encapsulates all the logic for finding a model to Baker."""

_unique_models = None
_ambiguous_models = None

def get_model(self, name):
"""
Get a model.
"""Get a model.

Args:
name (str): A name on the form 'applabel.modelname' or 'modelname'

Returns:
object: a model class

:param name String on the form 'applabel.modelname' or 'modelname'.
:return a model class.
"""
try:
if '.' in name:
Expand All @@ -124,11 +125,10 @@ def get_model(self, name):
return model

def get_model_by_name(self, name):
"""
Get a model by name.
"""Get a model by name.

If a model with that name exists in more than one app,
raises AmbiguousModelName.
If a model with that name exists in more than one app, raises
AmbiguousModelName.
"""
name = name.lower()

Expand All @@ -142,9 +142,7 @@ def get_model_by_name(self, name):
return self._unique_models.get(name)

def _populate(self):
"""
Cache models for faster self._get_model.
"""
"""Cache models for faster self._get_model."""
unique_models = {}
ambiguous_models = []

Expand Down Expand Up @@ -172,9 +170,12 @@ def is_iterator(value):


def _custom_baker_class():
"""
Returns custom baker class specified by BAKER_CUSTOM_CLASS in the django
settings, or None if no custom class is defined
"""Return the specified custom baker class.

Returns:
object: The custom class is specified by BAKER_CUSTOM_CLASS in Django's
settings, or None if no custom class is defined.

"""
custom_class_string = getattr(settings, 'BAKER_CUSTOM_CLASS', None)
if custom_class_string is None:
Expand Down Expand Up @@ -204,9 +205,7 @@ class Baker(object):

@classmethod
def create(cls, _model, make_m2m=False, create_files=False):
"""
Factory which creates the baker class defined by the BAKER_CUSTOM_CLASS setting
"""
"""Create the baker class defined by the `BAKER_CUSTOM_CLASS` setting."""
baker_class = _custom_baker_class() or cls
return baker_class(_model, make_m2m, create_files)

Expand All @@ -230,8 +229,8 @@ def init_type_mapping(self):
self.type_mapping = generators.get_type_mapping()
generators_from_settings = getattr(settings, 'BAKER_CUSTOM_FIELDS_GEN', {})
for k, v in generators_from_settings.items():
field_class = import_if_str(k)
generator = import_if_str(v)
field_class = import_from_str(k)
generator = import_from_str(v)
self.type_mapping[field_class] = generator

def make(
Expand All @@ -241,8 +240,7 @@ def make(
_from_manager=None,
**attrs
):
"""Creates and persists an instance of the model associated
with Baker instance."""
"""Create and persist an instance of the model associated with Baker instance."""
params = {
'commit': True,
'commit_related': True,
Expand All @@ -254,8 +252,7 @@ def make(
return self._make(**params)

def prepare(self, _save_related=False, **attrs):
"""Creates, but does not persist, an instance of the model
associated with Baker instance."""
"""Create, but do not persist, an instance of the associated model."""
return self._make(commit=False, commit_related=_save_related, **attrs)

def get_fields(self):
Expand Down Expand Up @@ -444,8 +441,7 @@ def _remote_field(self, field):
return field.remote_field

def generate_value(self, field, commit=True):
"""
Calls the generator associated with a field passing all required args.
"""Call the associated generator with a field passing all required args.

Generator Resolution Precedence Order:
-- attr_mapping - mapping per attribute name
Expand Down Expand Up @@ -486,10 +482,10 @@ def generate_value(self, field, commit=True):


def get_required_values(generator, field):
"""
Gets required values for a generator from the field.
If required value is a function, calls it with field as argument.
If required value is a string, simply fetch the value from the field
"""Get required values for a generator from the field.

If required value is a function, calls it with field as argument. If
required value is a string, simply fetch the value from the field
and return.
"""
# FIXME: avoid abbreviations
Expand Down
4 changes: 2 additions & 2 deletions model_bakery/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from . import random_gen
from .gis import default_gis_mapping
from .utils import import_if_str
from .utils import import_from_str

try:
from django.contrib.postgres.fields import ArrayField
Expand Down Expand Up @@ -98,7 +98,7 @@ def get_type_mapping():


def add(field, func):
user_mapping[import_if_str(field)] = import_if_str(func)
user_mapping[import_from_str(field)] = import_from_str(func)


def get(field):
Expand Down
38 changes: 21 additions & 17 deletions model_bakery/random_gen.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
Generators are callables that return a value used to populate a field.

If this callable has a `required` attribute (a list, mostly), for each item in
the list, if the item is a string, the field attribute with the same name will
be fetched from the field and used as argument for the generator. If it is a
callable (which will receive `field` as first argument), it should return a
list in the format (key, value) where key is the argument name for generator
and value is the value for that argument.
"""Generators are callables that return a value used to populate a field.

If this callable has a `required` attribute (a list, mostly), for each
item in the list, if the item is a string, the field attribute with the
same name will be fetched from the field and used as argument for the
generator. If it is a callable (which will receive `field` as first
argument), it should return a list in the format (key, value) where key
is the argument name for generator and value is the value for that
argument.
"""

import string
Expand Down Expand Up @@ -43,14 +43,18 @@ def gen_image_field():
return get_content_file(f.read(), name=name)


def gen_from_list(L):
'''Makes sure all values of the field are generated from the list L
Usage:
from baker import Baker
class ExperientBaker(Baker):
attr_mapping = {'some_field':gen_from_list([A, B, C])}
'''
return lambda: choice(list(L))
def gen_from_list(a_list):
"""Make sure all values of the field are generated from a list.

Examples:
Here how to use it.

>>> from baker import Baker
>>> class ExperienceBaker(Baker):
>>> attr_mapping = {'some_field': gen_from_list(['A', 'B', 'C'])}

"""
return lambda: choice(list(a_list))


# -- DEFAULT GENERATORS --
Expand Down
13 changes: 6 additions & 7 deletions model_bakery/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,15 @@ def __init__(self, recipe):


def foreign_key(recipe):
"""
Returns the callable, so that the associated _model
will not be created during the recipe definition.
"""Return a `RecipeForeignKey`.

Return the callable, so that the associated `_model` will not be created
during the recipe definition.
"""
return RecipeForeignKey(recipe)


class related(object):
class related(object): # FIXME
def __init__(self, *args):
self.related = []
for recipe in args:
Expand All @@ -109,7 +110,5 @@ def __init__(self, *args):
raise TypeError('Not a recipe')

def make(self):
"""
Persists objects to m2m relation
"""
"""Persist objects to m2m relation."""
return [m.make() for m in self.related]
4 changes: 2 additions & 2 deletions model_bakery/timezone.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Add support for Django 1.4+ safe datetimes.
"""Add support for Django 1.4+ safe datetimes.

https://docs.djangoproject.com/en/1.4/topics/i18n/timezones/
"""
# TODO: the whole file seems to be not needed anymore, since Django has this tooling built-in
Expand Down
28 changes: 9 additions & 19 deletions model_bakery/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,18 @@
from .timezone import tz_aware


def import_if_str(import_string_or_obj):
"""
Import and return an object defined as import string in the form of

path.to.module.object_name

or just return the object if it isn't a string.
"""
if isinstance(import_string_or_obj, str):
return import_from_str(import_string_or_obj)
return import_string_or_obj


def import_from_str(import_string):
"""
Import and return an object defined as import string in the form of
"""Import an object defined as import if it is an string.

path.to.module.object_name
If `import_string` follows the format `path.to.module.object_name`,
this method imports it; else it just return the object.
"""
path, field_name = import_string.rsplit('.', 1)
module = importlib.import_module(path)
return getattr(module, field_name)
if isinstance(import_string, str):
path, field_name = import_string.rsplit('.', 1)
module = importlib.import_module(path)
return getattr(module, field_name)
else:
return import_string


def seq(value, increment_by=1):
Expand Down
5 changes: 2 additions & 3 deletions tests/generic/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,11 @@ class Movie(models.Model):

class MovieManager(models.Manager):
def get_queryset(self):
'''Annotate queryset with an alias field 'name'.
"""Annotate queryset with an alias field 'name'.

We want to test whether this annotation has been run after
calling baker.make().

'''
"""
return (
super(MovieManager, self).get_queryset()
.annotate(name=models.F('title'))
Expand Down
Loading