How to mock a call to a method of a related model? - django

I have the following django models
class Charge(models.Model):
total = models.PositiveIntegerField()
def execute():
# make some external calls
return invoice_url
class Transaction(models.Model):
product = models.ForeignKey(Product)
charge = models.ForeignKey(Charge)
def do_charge():
self.charge = Charge.objects.create(total=self.product.price)
url = self.charge.execute()
return url
I'm trying to test do_charge by mocking the call to execute.
The problem is that the object is being created within do_charge.
Something like that (this is obviously not working just to illustrate)
#mock.patch('Charge.execute')
def test_should_return_url(self, mock):
mock.side_effect = 'www.foo.testing/invoice'
t = Transaction.objects.create(product=p1)
invoice_url = t.do_charge()
self.assertIsEqual(invoice_url, 'www.foo.testing/invoice')
Is it possible to mock Charge.execute?
python 3.4, django 1.8.

I've tried next code to test mock behavior and it seems that mocking Charge.execute should work.
>>> class A(object):
... def foo(self):
... return 'a'
>>> #mock.patch('__main__.A.foo', return_value='12')
... def bar(mockfoo):
... a = A()
... return a.foo()
...
>>> bar()
'12'
Here I've mocked a method of the class, make a class instance and called object's method and it used mocked version. So I think in your test mocked Charge.execute will be used. I think the problem you are facing can be related with correct path to the class you want to mock.

Related

Correct way to replace Django Model class with an equivalent+decorated subclass

I'm writing a decorator intended to add functionality to decorated Django Model classes. Something like this:
class NewFunctionality:
#classmethod
def fun1(cls):
...
#property
def prop1(self):
...
def add_functionality(Decorated):
class NewClass(Decorated, NewFunctionality):
class Meta(getattr(Decorated, 'Meta', object)):
app_label = Decorated._meta.app_label
NewClass.__name__ = Decorated.__name__
NewClass.__doc__ = Decorated.__doc__
return NewClass
#add_functionality
class MyModel(models.Model):
...
This seems to work until there are two decorated model classes, when I get an error Conflicting 'modelclass' models in application 'my_app'.
This is apparently due to the registry of models that Django keeps, which clearly has some automagic that doesn't appreciate new model classes being made, even if they are direct replacements of the existing one.
Is there anything I can do to accomplish this, other than by monkeypatching the decorated class, adding each needed method?
Edit
I've avoided the error by making the wrapper class into a proxy class of the decorated class:
def add_functionality(Decorated):
class NewClass(Decorated, NewFunctionality):
class Meta(getattr(Decorated, 'Meta', object)):
app_label = Decorated._meta.app_label
proxy = True
NewClass.__name__ = Decorated.__name__
NewClass.__doc__ = Decorated.__doc__
return NewClass
I also replace the use of the class keyword, using builtins.type, as in:
def add_functionality(Decorated):
return type(
Decorated.__name__,
(NewFunctionality, Decorated,),
{
'Meta': type('Meta', (object,), {
'app_label': Decorated._meta.app_label,
'proxy': True,
'__module__': Decorated.__module__,
}
)
I really like this as it seemingly completely replaces the decorated class. Now, however, I'm getting the following warning from Django:
.venv/lib/python3.9/site-packages/django/db/models/base.py:321
/home/tyler/orm-cache/django-ormsgpack/.venv/lib/python3.9/site-packages/django/db/models/base.py:321: RuntimeWarning: Model 'my_app.atestmodel' was already registered. Reloading models is not advised as it can lead to inconsistencies, most notably with related models.
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
If anyone can shed light onto how I might utilize the Django api to properly overwrite the registered model, I'd love to hear it!
thanks

writing unit test for many to many model in django program

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')

Django model - interface design to avoid need of passing object in CHILD class calling method defined in PARENT class

I am using django 2.0.8 and Python 3.5. I have written a base class which encapsulates behavior in a base class.
When using the interface in the child class, I find that I have to pass the object of the child class to the parent - which is not only ugly, is error prone.
I do not want to use composition (instead of an interface), because AFAIK fields in django models are saved to the DB - that aside, I prefer the sub classing approach, since all the functionality can remain in the base class.
Is there any way I can (in the parent class), find/obtain the instance (or at least the name of the class and it's id) that invoked the method call?
Here is my code:
class Likeable(models.Model):
likes = GenericRelation(Like)
def action_is_permissible(self, actionable_object, actor):
ct = ContentType.objects.get_for_model(actionable_object)
object_id = actionable_object.id
found_objects = Like.objects.filter(content_type=ct, object_id=object_id, liker=actor)
return ((len(found_objects) == 0), ct, object_id, found_objects)
def add_like(self, actionable_object, actor):
can_add, ct, object_id, found_objects = self.action_is_permissible(actionable_object, actor)
if can_add:
like = self.likes.create(content_type=ct, object_id=object_id, liker=actor)
else:
# do nothing
return
class Meta:
abstract = True
class Foo(Likeable):
name = models.CharField(max_length=255,default='')
objects = models.Manager()
Example use (imports omitted)
foo = Foo.objects.get(id=1)
p = User.objects.get(id=1)
foo.add_like(foo, p) # <- nasty API calling convention
You can access it using self.
self refers to the object which is calling.
Ref: What is the purpose of self?
EDIT (code changes):
class Likeable(models.Model):
def add_like(self, actor):
# update `actionable_object` to `self`
foo.add_like(p)

How to make a str var callable

What i want is to make a string var callable. I just have a list with different models_names and i want to call their create methods like this way.
class Object_model_a:
#def ...
class Object_model_b:
#def ...
class Object_model_c:
#def ...
list = ('Object_model_a', 'Object_model_b', 'Object_model_c')
x = list[0]() # this must create a new instance of Object_model_a
This is possible to develop using php like this way:
$hi = 'Hello'
$Hello = 'Hi!!'
echo $$hi
>> Hi!!
Anyone knows if this is possible using django?? This will simplify a lot my code.
Thanks a lot!
You could use Django's get_model helper function, which takes the app name and model name and returns the model class.
from django.db.models import get_model
list = ('Object_model_a', 'Object_model_b', 'Object_model_c')
model = get_model('my_app_name', list[0]) # returns Object_model_a class
instance = model() # creates a model instance
If for whatever reason you have a string that represents a particular class, the correct way to use that to instantiate an object of that class is to place your classes in a dict, keyed by the strings, then just invoke the class.
classes = dict((c.__name__, c) for c in (Object_model_a, Object_model_b, Object_model_c))
instance = classes['Object_model_a']()
Obviously, you don't need to use the classname as the key.
Thanks for all your comments, this is the way i've solved this requirement.
def create_object_instance(model_name, id=None):
for model in get_models():
if str(model.__name__).lower() == str(model_name).lower():
if id:
try:
return model.objects.get(id=id)
except ObjectsDoesNotExist:
return None
else:
return model.objects.all()
return None

How to use custom managers in chain queries?

I made a custom manager that has to randomize my query:
class RandomManager(models.Manager):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
When I use the method defined in my manager in the first place, it's works ok:
>>> PostPages.random_objects.randomize()
>>> <PostPages: post 3>
I need to randomize the already filtered query. When I tried to use the manager and the method in chain I got an error:
PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/home/i159/workspace/shivaroot/shivablog/<ipython-input-9-98f654c77896> in <module>()
----> 1 PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
AttributeError: 'QuerySet' object has no attribute 'randomize'
Result of filtering is not an instance of model class, but it's django.db.models.query.QuerySet, so that it does not have my manager and method, respectively.
Is there a way to use custom manager in chain query?
This is how you chain custom methods on custom manager ie: Post.objects.by_author(user=request.user).published()
from django.db.models.query import QuerySet
class PostMixin(object):
def by_author(self, user):
return self.filter(user=user)
def published(self):
return self.filter(published__lte=datetime.now())
class PostQuerySet(QuerySet, PostMixin):
pass
class PostManager(models.Manager, PostMixin):
def get_query_set(self):
return PostQuerySet(self.model, using=self._db)
Just a code example using the new as_manager() method (see update information from #zzart.
class MyQuerySet(models.query.QuerySet):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
class MyModel(models.Model):
.....
.....
objects = MyQuerySet.as_manager()
.....
.....
And then you will be able to use something like this in your code:
MyModel.objects.filter(age__gt=16).randomize()
As you can see, the new as_manager() is really neat:)
Looks like this snippet provides a solution to your situation: Custom managers with chainable filters.
Given that you have an existing models.Manager and you don't want to expose some of the manager method to a chainable queryset, you can use Manager.from_queryset(QuerySet)().
So, you could still place all your chainable queryset method inside the QuerySet and your manager method independently.
Example given in the official site.
Snippet from Django Docs
class BaseManager(models.Manager):
# Available only on Manager.
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def manager_and_queryset_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
CustomManager = BaseManager.from_queryset(CustomQuerySet)
class MyModel(models.Model):
objects = CustomManager()
How about something like below which creates the custom QuerySet dynamically and allows us to 'transplant' our custom queries onto the returned QuerySet instance:
class OfferManager(models.Manager):
"""
Additional methods / constants to Offer's objects manager
"""
### Model (db table) wide constants - we put these and
### not in model definition to avoid circular imports.
### One can access these constants through like
<foo>.objects.STATUS_DISABLED or ImageManager.STATUS_DISABLED
STATUS_DISABLED = 0
...
STATUS_CHOICES = (
(STATUS_DISABLED, "Disabled"),
(STATUS_ENABLED, "Enabled"),
(STATUS_NEGOTIATED, "Negotiated"),
(STATUS_ARCHIVED, "Archived"),
)
...
# we keep status and filters naming a little different as
# it is not one-to-one mapping in all situations
QUERYSET_PUBLIC_KWARGS = {'status__gte': STATUS_ENABLED}
QUERYSET_ACTIVE_KWARGS = {'status': STATUS_ENABLED}
def get_query_set(self):
""" our customized method which transpalats manager methods
as per get_query_set.<method_name> = <method> definitions """
CustomizedQuerySet = QuerySet
for name, function in self.get_query_set.__dict__.items():
setattr(CustomizedQuerySet, name, function)
return CustomizedQuerySet(self.model, using=self._db)
def public(self):
""" Returns all entries accessible through front end site"""
return self.all().filter(**OfferManager.QUERYSET_PUBLIC_KWARGS)
get_query_set.public = public # will tranplat the function onto the
# returned QuerySet instance which
# means 'self' changes depending on context.
def active(self):
""" returns offers that are open to negotiation """
return self.public().filter(**OfferManager.QUERYSET_ACTIVE_KWARGS)
get_query_set.active = active
...
More polished version of this method and django ticket here: https://code.djangoproject.com/ticket/20625.