I'm seeing duplicate values being generated when using randint and factory_boy. My test fails when checking for the asset for each object. The barcode is also the same between the two when calling create() on the factory.
Do I need to provide a different seed before each object creation?
Packages
factory-boy 2.11.1
Faker 1.0.4
python 2.7
django 1.11
Files
seed/factories.py
import factory
from A1.models import *
from random import randint, random
faker = Factory.create()
class AssetFactory(factory.DjangoModelFactory):
class Meta:
model = Asset
asset = "ABC" + str(randint(0000, 9999))
barcode = "AA" + randint(111111, 999999)
user = factory.lazy_attribute(lambda x: faker.name())
tests.py
def test_randomCreation(self):
new_asset = AssetFactory.create()
new_asset2 = AssetFactory.create()
self.assertNotEqual(new_asset.asset, new_asset2.asset) #FAILS
A1/models.py
class Asset(models.Model):
asset = models.CharField(max_length=255, verbose_name="Asset")
barcode = models.CharField(max_length=255)
user = models.CharField(max_length=255)
May someone point me in the right direction?
Thanks in advance!!
The way the Python language is designed, your code cannot work.
It is strictly equivalent to the following:
_RANDOM_ASSET = "ABC" + str(randint(0000, 9999))
_RANDOM_BARCODE = "AA" + str(randint(111111, 999999))
class AssetFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Asset
asset = _RANDOM_ASSET
barcode = _RANDOM_BARCODE
user = factory.LazyAttribute(lambda x: faker.name())
You've fixed a random value for asset and barcode, so every object generated through that factory will have the exact same value.
The proper way would be to use the various built-in declarations:
class AssetFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Asset
asset = factory.fuzzy.FuzzyText(length=4, prefix='ABC', chars=string.digits)
barcode = factory.fuzzy.FuzzyText(length=6, prefix='AA', chars=string.digits)
user = factory.Faker('name')
Related
in my django projects, I have a two class like following:
class DataTag(models.Model):
title = models.CharField(max_length=120, unique=True)
relations = models.ManyToManyField('DataTag',
related_name='related_data_tags', blank=True)
and the another class is:
class Data(models.Model):
tags = models.ManyToManyField('DataTag',related_name = 'data')
def tag_name(self):
if self.tags.all():
return self.tags.all()[0].title
return ''
both my models work, but now I want to write a test for main_tag_name, and checking if this function returns a true value or not.until now I write the following:
from unittest import TestCase
class DataTest(TestCase):
def test_tag_name(self):
self.data = Data.objects.create()
self.tag1 = DataTag.objects.create()
I am new on writing test. please help me for writing this test.
Thanks in advance
What you can do:
Create a Data object:
data_object = Data.objects.create()
Now, create a tag for this:
data_object.tags.create(name='Foo')
You can assert that the relationship exists: self.assertTrue(data_objects.tags.exists())
And now you can assert that tag_name returns what you expect: self.assertEqual(data_object.tag_name, 'Foo')
In my project, i have 2 models:
class Product(models.Model):
name = models.CharField(max_length=200)
class Material(models.Model):
name = models.CharField(max_length=200)
product = models.ForeignKey(Product)
Now, I want to make a copy of Product and keep all of the assigned materials.
This is what I tried:
new_product = Product.object.get(pk='someexistingpk')
new_product.pk = None
new_product.name += ' (Copy)'
new_product.save()
Another variant I tried:
new_product = deepcopy(Product.object.get(pk='someexistingpk'))
new_product.pk = None
new_product.name += ' (Copy)'
new_product.save()
But in both cases, the resulting model_set is empty. None of the attached items are kept.
new_product.material_set.all()
<QuerySet []>
How can I resolve this? Ideally without iterating over every item in the original material_set.
Given that ForeignKeys, can not have multiple assignments, it only makes sense that they are not copied over, as that would break the original object.
So, when copying over the Product, one should also do the same for the related objects. Like this:
new_prod = deepcopy(prod)
new_prod.pk = None
new_prod.save()
for mat in prod.material_set.all():
new_mat = deepcopy(mat)
new_mat.pk = None
new_mat.product = new_prod
new_mat.save()
Now, it results in a nice Queryset with all of the material objects attached, as expected.
new_prod.material_set.all()
<QuerySet [<Material: 1.01 Katoen cats and dogs>, <Material: 1.0 Hour-cost>, <Material: 2.0 lint>, <Material: 1.0 Katoen cats and dogs>]>
From my understanding you are trying to duplicate a model. The way I approached this problem in my project was using dynamic models and inheritance. If you use inheritance, all the fields are going to be automatically copied to the new model.
from MyApp.models import Product
class Meta:
pass
setattr(Meta, 'app_label', 'MyApp')
attrs = {'__module__': module, 'Meta': Meta}
model = type('ProductCopy', (Product,), attrs)
So here the new model that is created is called ProductCopy and will have a table on your Django Admin page.
To learn more about dynamic models you can take a look at this documentation.
I am using django 1.6 and factory-boy.
class UserFactory(factory.Factory):
class Meta:
model = models.User
username = factory.Sequence(lambda n: 'user%d' % n)
Here username is a simple CharField in model. So that each time I am calling UserFactory() I am saving and getting unique user named object.
In factory-boy I can use factory.SubFactory(SomeFactory).
How I can generate list of SomeFactory in ParentOfSomeFactory ?
So that, if I call ParentOfSomeFactory() I will create list of SomeFactory as well as ParentOfSomeFactory database
Use factory.List:
class ParentOfUsers(factory.Factory):
users = factory.List([
factory.SubFactory(UserFactory) for _ in range(5)
])
You could provide a list with factory.Iterator
import itertools
import factory
# cycle through the same 5 users
users = itertools.cycle(
(UserFactory() for _ in range(5))
)
class ParentFactory(factory.Factory):
user = factory.Iterator(users)
This is my model:
class Post(models.Model):
owner = models.ForeignKey(User, related_name="%(app_label)s%(class)s_set")
post = models.CharField(max_length=400)
class Meta:
abstract = True
class DS(Post):
location = models.ForeignKey(Location, blank=True, null=True, related_name="%(app_label)s%(class)s_set")
class Meta(Post.Meta):
abstract = True
class S(DS):
# same as DS
pass
Now, when I open up Python shell and do this:
a = User.objects.get(username='a')
dir(a)
Then these two appear:
['myapps_set', 's_set']
and when I do:
a.s_set.all()
it returns one S object, but when when I do:
a.myapps_set.all()
it returns three S objects (the first S object it returns is the same one which was returned when I do a.s_set.all(). My two questions are,
1) how come even when I specifically did owner = models.ForeignKey(User, related_name="%(app_label)s%(class)s_set"), a s_set is able to be accessed with a user object?
2) how come they return two different sets of objects (i.e. how come myapps_set.all() returns 3 (the correct answer) while s_set.all() only returns one?
I've just tested you code with django==1.8 on clean virtual environment and got only one reverse relationship.
$ pip freeze
decorator==4.0.6
Django==1.8
ipython==4.0.1
ipython-genutils==0.1.0
path.py==8.1.2
pexpect==4.0.1
pickleshare==0.5
ptyprocess==0.5
simplegeneric==0.8.1
traitlets==4.0.0
wheel==0.24.0
$./manage.py shell
In [1]: from django.contrib.auth.models import User
In [2]: a = User.objects.all()[0]
In [3]: [item for item in sorted(dir(a)) if 'tutu' in item or item.startswith('s') and not item.startswith('_')]
Out[3]:
['save',
'save_base',
'serializable_value',
'set_password',
'set_unusable_password',
'tutus_set']
Here is the code: https://www.dropbox.com/s/rsej26d70swyllr/stack34406825.tar.gz?dl=0
It looks like you've done something with your local version of django or you've shown not all code here.
I currently have two different models.
class Journal(models.Model):
date = models.DateField()
from_account = models.ForeignKey(Account,related_name='transferred_from')
to_account = models.ForeignKey(Account,related_name='transferred_to')
amount = models.DecimalField(max_digits=8, decimal_places=2)
memo = models.CharField(max_length=100,null=True,blank=True)
class Ledger(models.Model):
date = models.DateField()
bank_account = models.ForeignKey(EquityAccount,related_name='paid_from')
account = models.ForeignKey(Account)
amount = models.DecimalField(max_digits=8, decimal_places=2)
name = models.ForeignKey(Party)
memo = models.CharField(max_length=100,null=True,blank=True)
I am creating a report in a view and get the following error:
Merging 'ValuesQuerySet' classes must involve the same values in each case.
What I'm trying to do is only pull out the fields that are common so I can concatenate both of them e.g.
def report(request):
ledger = GeneralLedger.objects.values('account').annotate(total=Sum('amount'))
journal = Journal.objects.values('from_account').annotate(total=Sum('amount'))
report = ledger & journal
...
If I try to make them exactly the same to test e.g.
def report(request):
ledger = GeneralLedger.objects.values('memo').annotate(total=Sum('amount'))
journal = Journal.objects.values('memo').annotate(total=Sum('amount'))
report = ledger & journal
...
I get this error:
Cannot combine queries on two different base models.
Anyone know how this can be accomplished?
from itertools import chain
report = chain(ledger, journal)
Itertools for the win!
If you want to do an Union, you should convert these querysets into python set objects.
If it is possible to filter the queryset itself rightly, you should really do that!
Use itertools.chain:
from itertools import chain
report = list(chain(ledger, journal))
Note: you need to turn the resulting object into a list for Django to be able to process it.
I had the same issue. I solved it using the union method combined_queryset = qs1.union(qs2)
Using your example: report = ledger.union(journal)