Tutorial¶
Welcome to PyFactory!
This is a whirlwind tour of the features of PyFactory and a quick guide on how to use each of them.
What is PyFactory?¶
PyFactory is a library for writing and using model factories. Model factories allow you to replace test fixtures or manual model creation in tests with succint, easy to use factories.
PyFactory is also model-agnostic, meaning it can generate any sort of models for any ORM or backing store.
The specific feature list of PyFactory:
Model-agnostic. Generate models for any ORM or backing store.
Generate attributes. Instead of getting a full-blown model, you can request just the attributes of the model as a dictionary.
Build model objects (and optionally ask for them saved to the backing store as well).
Model complex associations in your schemas to generate all dependencies along with the model.
Your First Factory¶
Let’s create your first factory. Before doing that, however, we need to define what our models are. In your case, this may be a model for Django, SQLAlchemy, or something custom. For the purposes of our examples here, we’re going to simply set attributes on an object as our model.
The Model Builder¶
First, we need to create a model builder. This is the class which knows how to build our models given a dictionary of attributse. This is the core piece of PyFactory which enables model-agnosticism. Model builders are extremely easy to write, especially in our case:
class MyModelBuilder(object):
@classmethod
def build(cls, model_cls, attrs):
result = model_cls()
for key,val in attrs.iteritems():
setattr(result, key, val)
return result
@classmethod
def create(cls, model_cls, attrs):
result = self.build(model_cls, attrs)
result.saved = True
return result
As you can see, given a model class and attributes, the model builder knows
how to build them. In our case, this is simply using setattr
on an object.
In-depth documentation is available on model builders.
The Factory¶
Now that we have a model builder, let’s go forward and build a simple factory.
Let’s pretend we have a User
model we want to create a factory for:
from pyfactory import Factory, schema
class UserFactory(Factory):
_model = object
_model_builder = MyModelBuilder
@schema()
def basic(self):
return {
"first_name": "John",
"last_name": "Doe"
}
Given the above factory, we can now generate users!
user = UserFactory().create("basic")
print user.first_name, user.last_name # "John Doe"
Using Your Factory¶
In the example above we used the create
method to use our factory.
There are other methods available as well:
attributes
- This method will return the attributes of the model it would create as adict
.build
- This method will build the model but will not save it to the backing store. This is useful if you want a model to manipulate more before saving it.create
- This method will build the model and save it to the backing store.
Using these methods is straightforward:
factory = UserFactory()
factory.attributes("basic")
factory.build("basic")
factory.create("basic")
Associations¶
Most models, especially those in relational databases, have associations with other models. In the past, if you had a model which had many associations, you would have to manually create all these associations and maintain the brittle relationships. With PyFactory, these associations are managed for you.
Let’s create another factory, for a hypothetical Post
which has an author,
which is a User
that we created a factory for moments before.
from pyfactory import Factory, association, schema
class PostFactory(Factory):
_model = object
_model_builder = MyModelBuilder
@schema()
def basic(self):
return {
"title": "My Post",
"author": association(UserFactory(), "basic")
}
The new piece of the above factory, of course, is the association
function call. This means that the author
field is an association
to a UserFactory()
factory and the basic
schema of that factory,
which is the schema we created earlier.
Given the above example, we can now create a Post
which already has
a User
dependency created for us!
post = PostFactory().create("basic")
print post.title # => "My Post"
print post.author.first_name, post.author.last_name # => "John Doe"
Sequences¶
Sometimes, in order to help generate information that looks different from other information, it is useful to incorporate sequences of data. For example, instead of having ever user’s first name to “User” it would be nice if it could be “User #1,” “User #2,” etc. Sequences make this easy.
from pyfactory import Factory, schema, sequence
class UserFactory(Factory):
_model = object
_model_builder = MyModelBuilder
@schema()
def basic(self):
return {
"first_name": sequence("User %(n)d")
}
Given the above example, we can now create User
objects which
have sequential first names:
user1 = UserFactory().create("basic")
user2 = UserFactory().create("basic")
print user1.first_name # => "User 1"
print user2.first_name # => "User 2"