How to pass in a starting sequence number to a Django factory_boy factory?
Asked Answered
T

4

16

factory_boy defaults to 1 for sequences. How can I pass in a number to use as a different starting number instead? I can subclass the _setup_next_sequence() method, but how can I give it a variable to use?

# File: models.py
from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)


# File: factories.py
from .models import Book
import factory

class BookFactory(factory.Factory):
  FACTORY_FOR = BookModel  
  title = factory.Sequence(lambda n: u'Title #{}'.format(n))

  @classmethod
  def _setup_next_sequence(cls):      
      # Instead of defaulting to starting with number 1, start with starting_seq_num.
      # But how do I set starting_seq_num?
      return starting_seq_num


# File: make_data.py
from factories import BookFactory

# somehow set starting sequence number here?

BookFactory().create()

I'm using factory_boy 1.2.0 (via pip install factory_boy)
factory_boy code: https://github.com/dnerdy/factory_boy

Thill answered 14/3, 2013 at 5:59 Comment(0)
T
6

I found two ways of solving this:

  1. Use a module variable
  2. Use a class attribute set outside of the class definition

Use a module variable:

# File: factories.py
from .models import Book
import factory

starting_seq_num = 0

class BookFactory(factory.Factory):
  FACTORY_FOR = BookModel  
  title = factory.Sequence(lambda n: u'Title #{}'.format(n))

  @classmethod
  def _setup_next_sequence(cls):      
      # Instead of defaulting to starting with 0, start with starting_seq_num.
      return starting_seq_num

# File: make_data.py
import factories

factories.starting_seq_num = 100    
factories.BookFactory().create()

Use a class attribute set outside of the class definition:

# File: factories.py
from .models import Book
import factory

class BookFactory(factory.Factory):
  # Note that starting_seq_num cannot be set here in the class definition,
  # because Factory will then pass it as a kwarg to the model's create() method
  # and cause an exception.  It must be set outside the class definition.
  FACTORY_FOR = BookModel  
  title = factory.Sequence(lambda n: u'Title #{}'.format(n))

  @classmethod
  def _setup_next_sequence(cls):      
      return getattr(cls, 'starting_seq_num', 0)

# File: make_data.py
from factories import BookFactory

BookFactory.starting_seq_num = 100
BookFactory().create()
Thill answered 14/3, 2013 at 19:21 Comment(0)
B
10

In addition to the answer of Rob Bednark

We can use reset_sequence() function, which will reset the counter to a specific value.

# File: make_data.py
import factories

factories.BookFactory.reset_sequence(100)
my_book = factories.BookFactory().create()
print(my_book.title) # Title #100
Bubonocele answered 7/4, 2016 at 19:3 Comment(0)
T
6

I found two ways of solving this:

  1. Use a module variable
  2. Use a class attribute set outside of the class definition

Use a module variable:

# File: factories.py
from .models import Book
import factory

starting_seq_num = 0

class BookFactory(factory.Factory):
  FACTORY_FOR = BookModel  
  title = factory.Sequence(lambda n: u'Title #{}'.format(n))

  @classmethod
  def _setup_next_sequence(cls):      
      # Instead of defaulting to starting with 0, start with starting_seq_num.
      return starting_seq_num

# File: make_data.py
import factories

factories.starting_seq_num = 100    
factories.BookFactory().create()

Use a class attribute set outside of the class definition:

# File: factories.py
from .models import Book
import factory

class BookFactory(factory.Factory):
  # Note that starting_seq_num cannot be set here in the class definition,
  # because Factory will then pass it as a kwarg to the model's create() method
  # and cause an exception.  It must be set outside the class definition.
  FACTORY_FOR = BookModel  
  title = factory.Sequence(lambda n: u'Title #{}'.format(n))

  @classmethod
  def _setup_next_sequence(cls):      
      return getattr(cls, 'starting_seq_num', 0)

# File: make_data.py
from factories import BookFactory

BookFactory.starting_seq_num = 100
BookFactory().create()
Thill answered 14/3, 2013 at 19:21 Comment(0)
H
6

Update: factory_boy now handles it!

In the latest version of factory_boy (2.8.1 to this day) it is now possible to force the sequence counter into a define value:

Forcing the value on a per-call basis

In order to force the counter for a specific Factory instantiation, just pass the value in the __sequence=42 parameter:

class AccountFactory(factory.Factory):
        class Meta:
            model = Account
        uid = factory.Sequence(lambda n: n)
        name = "Test"

Then in the console:

>>> obj1 = AccountFactory(name="John Doe", __sequence=10)
>>> obj1.uid  # Taken from the __sequence counter
10
>>> obj2 = AccountFactory(name="Jane Doe")
>>> obj2.uid  # The base sequence counter hasn't changed
1

And it is also possible to reset the counter to a specific value:

>>> AccountFactory.reset_sequence(42)
>>> AccountFactory().uid
42
>>> AccountFactory().uid
43
Haugh answered 21/2, 2017 at 13:11 Comment(0)
D
0

The third, and simplest way:

# File: factories.py
from .models import BookModel
import factory

class BookFactory(factory.Factory, starting_seq_num):
  FACTORY_FOR = BookModel  
  title = factory.Sequence(lambda n: u'Title #{}'.format(n + starting_seq_num))

# File: make_data.py
import factories

book = factories.BookFactory(512).create()  #Start with 512

I'm only starting with Factory Boy myself, and not too experienced in Python either, so I may be missing something, but you see where I'm going here. To make it clearer, I think I'd actually prefer it to be keyworded:

class BookFactory(factory.Factory, title_seq_start=-1):
...
book = factories.BookFactory(title_seq_start=512).create()
Dietz answered 24/10, 2014 at 1:32 Comment(1)
this throws error. How are you extending from undefined variable?Whoso

© 2022 - 2024 — McMap. All rights reserved.