Django + Factory Boy: Use Trait to create other factory objects - django

Is it possible to use Traits (or anything else in Factory Boy) to trigger the creation of other factory objects? For example: In a User-Purchase-Product situation, I want to create a user and inform that this user has a product purchased with something simple like that:
UserFactory.create(with_purchased_product=True)
Because it feels like too much trouble to call UserFactory, ProductFactory and PurchaseFactory, then crate the relationship between them. There has to be a simpler way to do that.
Any help would be appreciated.

First, I'll be honest with you: I do not know if this is the best answer or if it follows the good practices of python.
Anyway, the solution that I found for this kind of scenario was to use post_generation.
import factory
class UserFactory(factory.DjangoModelFactory):
class Meta:
model = User
name = factory.Faker('name'))
#factory.post_generation
def with_purchased_products(self, create, extracted, **kwargs):
if extracted is not None:
PurchaseFactory.create(user=self, with_products=extracted)
class PurchaseFactory(factory.DjangoModelFactory):
class Meta:
model = Purchase
user = factory.SubFactory(UserFactory)
#factory.post_generation
def with_products(self, create, extracted, **kwargs):
if extracted is not None:
ProductFactory.create_batch(extracted, purchase=self)
class ProductFactory(factory.DjangoModelFactory):
class Meta:
model = Product
purchase = factory.SubFactory(PurchaseFactory)
To make this work you just need to:
UserFactory.create(with_purchased_products=10)
And this is just a article that is helping learn more about Django tests with fakes & factories. Maybe can help you too.

Related

What is the simplest way to fake a Django model field?

Currently my Django models looks like this:
class Car(Model):
horse_power = IntegerField()
I have lots of models similar to this, they are used all over the application, there are serializers of DRF endpoints using these model fields as well. Of course the field names are different in other models.
However, the horse_power value as well as other fields are no longer IntegerField, but a foreign key to another table containing more complex information like below:
class Car(Model):
horse_power_complex = OneToOneField(ComplexDataModel, on_delete=CASCADE)
To avoid lots of code change in every place the Car model is manipulated, I would like to keep the same getter and setter interfaces as before, in other words, I would like car.horse_power to return a simple integer calculated from the ComplexDataModel, and I also would like car.horse_power = 23 to access the ComplexDataModel and save the processed data so that I dont have to go over the entire application to update all references.
It is important to keep in mind that the model constructor should still work, for example, car = Car(horse_power=50).
I have tried python descriptors like below:
class HorsePower:
def __get__(self, obj, objtype=None):
return obj.horse_power_complex.get_value()
def __set__(self, obj, value):
obj.horse_power_complex.set_value(value)
class Car(Model):
horse_power = HorsePower()
horse_power_complex = OneToOneField(ComplexDataModel, on_delete=CASCADE)
This approach works well in most cases, but it fails to instantiate a new Car because the constructor does not accept non-django-field arguments.
Please do not question the reason it has to be made complex, because it really is complex, the car and horse power was just a way to make the example look clean and not confuse you with weird scientific terms
Use a property
class Car( Model)
horse_power_complex = ...
#property
def horse_power(self): # getter
return self.horse_power_complex.get_value()
#horse_power.setter
def horse_power( self, value):
self.horse_power_complex.set_value(value)

Is there a way to return the opposite of a custom Django QuerySet

I am looking for a way to return the exact opposite of a custom Django Manager queryset. I am working on a project that has some querysets already defined and I want to get the opposite of these. For simplicity I'll use the example from the docs. Suppose I had a Book model and a DahlManager that filtered the books by author='Roald Dahl'. Inside the DahlManager, let's suppose I have another method get_childrens_books that only returns his childrens books, something like is_for_children=True. Is there a way to get the opposite of this new method without writing a fully separate filter or method? I want something like Books.dahl_objects.get_childrens_books().exclude() or Books.dahl_objects.exclude(get_childrens_books), rather than adding another method to DahlManager. The motivation here is that I can see the potential for adding a lot of code that is repetative. I don't want to be writing a not_filter for every filter that I write.
You can do what you are asking with a custom queryset function:
class DahlBookQueryset(models.Queryset):
def oposite(self):
current_ids = self.values_list('id', flat=True)
return Book.dahl_objects.all().exclude(id__in=current_ids)
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return DahlBookQueryset(self.model, using=self._db).filter(author='Roald Dahl')
def get_children_books(self):
return self.get_queryset().filter(children=True)
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
And the query will be Books.dahl_objects.get_childrens_books().oposite().
If you don't understand something feel free to ask.
django has exclude() method for their object managers, but you need to use that during the filtering process. In other words, if you create a queryset before like your get_childrens_books() method, you cannot take it's opposite. Instead, you can useexclude() on the fly.
Let's say you want to filter Books that are not children books. This will work:
not_children_books = Books.objects.exclude(children=True)

Parametrize filtering manager queryset in Django

I want to implement kind of row level security for my model in Django. I want to filter out data as low as it's possible based on some requirement.
Now, I know I could create specified managers for this model as docs says but it seems for me that it needs to be static. I also know that I can create just method that will return queryset as I want but I'll be not sufficient, I mean the possibility to just get all data is still there and the simplest mistake can lead to leak of them.
So I found this post but as author said - it's not safe nor pretty to mess around with global states. This post is nearly 10 years old so I hope that maybe someone has come up with a better generic solution.
Here is piece of example to visualise what I need:
models.py:
class A(models.Model):
...
class B(models.Model):
user = models.ForeignKey(User)
a = models.ForeignKey(A)
And I want to create global functionality for getting objects of A only if instance of B with user as logged in user exists.
I came up with solution to just override get_queryset() of A manager like so:
managers.py
class AManager(models.Manager):
def get_queryset(self):
return super().get_queryset(b__user=**and_here_i_need_a_user**)
but I can't find hot to parametrize it.
==== EDIT ====
Another idea is to simply not allow to get querysets of A explicitly but only via related field from B but I can't find any reference how to accomplish that. Has anyone done something like that?
So you're sort of on the right track. How about something like this...
class AQuerySet(models.QuerySet):
def filter_by_user(self, user, *args, **kwargs):
user_filter = Q(b__user=user)
return self.filter(user_filter, *args, **kwargs)
class AManager(models.Manager):
queryset_class = AQuerySet
def filter_by_user(self, user, *args, **kwargs):
return self.get_queryset().filter_by_user(user, *args, **kwargs)
class A(models.Model):
objects = AManager()
# ...
then you can use it like this:
A.objects.filter_by_user(get_current_user(), some_filter='some_value')

Using Django filters inside model function

The main purpose of a model is to contain business logic, so I want most of my code inside Django model in the form of methods. For example I want to write a method named get_tasks_by_user() inside task model. So that I can access it as
Tasks.get_tasks_by_user(user_id)
Following is my model code:
class Tasks(models.Model):
slug=models.URLField()
user=models.ForeignKey(User)
title=models.CharField(max_length=100)
objects=SearchManager()
def __unicode__(self):
return self.title
days_passed = property(getDaysPassed)
def get_tasks_by_user(self,userid):
return self.filters(user_id=userid)
But this doesn't seems to work, I have used it in view as:
tasks = Tasks.objects.get_tasks_by_user(user_id)
But it gives following error:
'SearchManager' object has no attribute 'get_tasks_by_user'
If I remove objects=SearchManager, then just name of manager in error will change so I think that is not issue. Seems like I am doing some very basic level mistake, how can I do what I am trying to do? I know I can do same thing via :Tasks.objects.filters(user_id=userid) but I want to keep all such logic in model. What is the correct way to do so?
An easy way to do this is by using classmethod decorator to make it a class method. Inside class Tasks:
#classmethod
def get_tasks_by_user(cls, userid):
return cls.objects.filters(user_id=userid)
This way you can simply call:
tasks = Tasks.get_tasks_by_user(user_id)
Alternatively, you can use managers per Tom's answer.
To decided on which one to choose in your specific case, you can refer James Bennett's (the release manager of Django) blog post on when to use managers/classmethod.
Any methods on a model class will only be available to instances of that model, i.e. individual objects.
For your get_tasks_by_user function to be available as you want it (on the collection), it needs to be implemented on the model manager.
class TaskManager(models.Manager):
def get_tasks_by_user(self, user_id):
return super(TaskManager, self).get_query_set().filter(user=user_id)
class Task(models.Model):
# ...
objects = TaskManager()

Loose coupling of apps & model inheritance

I have a design question concerning Django. I am not quite sure how to apply the principle of loose coupling of apps to this specific problem:
I have an order-app that manages orders (in an online shop). Within this order-app I have two classes:
class Order(models.Model):
# some fields
def order_payment_complete(self):
# do something when payment complete, ie. ship products
pass
class Payment(models.Model):
order = models.ForeignKey(Order)
# some more fields
def save(self):
# determine if payment has been updated to status 'PAID'
if is_paid:
self.order.order_payment_complete()
super(Payment, self).save()
Now the actual problem: I have a more specialized app that kind of extends this order. So it adds some more fields to it, etc. Example:
class SpecializedOrder(Order):
# some more fields
def order_payment_complete(self):
# here we do some specific stuff
pass
Now of course the intended behaviour would be as follows: I create a SpecializedOrder, the payment for this order is placed and the order_payment_complete() method of the SpecializedOrder is called. However, since Payment is linked to Order, not SpecializedOrder, the order_payment_complete() method of the base Order is called.
I don't really know the best way to implement such a design. Maybe I am completely off - but I wanted to build this order-app so that I can use it for multiple purposes and wanted to keep it as generic as possible.
It would be great if someone could help me out here!
Thanks,
Nino
I think what you're looking for is the GenericForeignKey from the ContentTypes framework, which is shipped with Django in the contrib package. It handles recording the type and id of the subclass instance, and provides a seamless way to access the subclasses as a foreign key property on the model.
In your case, it would look something like this:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Payment(models.Model):
order_content_type = models.ForeignKey(ContentType)
order_object_id = models.PositiveIntegerField()
order = generic.GenericForeignKey('order_content_type', 'order_object_id')
You don't need to do anything special in order to use this foreign key... the generics handle setting and saving the order_content_type and order_object_id fields transparently:
s = SpecializedOrder()
p = Payment()
p.order = s
p.save()
Now, when your Payment save method runs:
if is_paid:
self.order.order_payment_complete() # self.order will be SpecializedOrder
The thing you want is called dynamic polymorphism and Django is really bad at it. (I can feel your pain)
The simplest solution I've seen so far is something like this:
1) Create a base class for all your models that need this kind of feature. Something like this: (code blatantly stolen from here)
class RelatedBase(models.Model):
childclassname = models.CharField(max_length=20, editable=False)
def save(self, *args, **kwargs):
if not self.childclassname:
self.childclassname = self.__class__.__name__.lower()
super(RelatedBase, self).save(*args, **kwargs)
#property
def rel_obj(self):
return getattr(self, self.childclassname)
class Meta:
abstract = True
2) Inherit your order from this class.
3) Whenever you need an Order object, use its rel_obj attribute, which will return you the underlying object.
This solution is far from being elegant, but I've yet to find a better one...