Testing python methods that call class methods - python

I have a very simple method:
Class Team(models.Model):
def sides(self):
return SideNames.objects.filter(team=self)
SideNames is another model defined in the same file as Team,
Which when I try and test:
self.assertEquals(len(t.sides()), 2)
I get the following error:
return SideNames.objects.filter(team=self)
AttributeError: 'NoneType' object has no attribute 'objects'
but if I change the test to be
self.assertEquals(len(SideNames.objects.filter(team=t)), 2)
Then I don't get the error. What's the difference between calling SideNames.objects.filter from the test itself and calling the actual method?
For reference, here are the 2 classes in their entirety.
class Team(models.Model):
"""The model for a football team."""
class Admin:
pass
def __unicode__(self):
return u'%s' % self.name
def is_player(self, player):
"""Checks to see if 'player' is a member if this team. Returns True if they are, or False otherwise."""
try:
teamPlayer = TeamPlayers.objects.get(player=player, team=self)
return True
except ObjectDoesNotExist:
return False
def sides(self):
"""Return the side names for this team"""
return SideNames.objects.filter(team=self)
def updateSides(self, side_a, side_b):
"""Update the side names"""
names = SideNames.objects.filter(team=self);
a = SideNames.objects.get(name = names[0].name)
a.name = side_a
a.save()
b = SideNames.objects.get(name = names[1].name)
b.name = side_b
b.save()
name = models.CharField("Team Name", max_length=255)
organiser = models.ForeignKey(User)
class SideNames(models.Model):
"""Holds the names of the sides for each team"""
class Admin:
pass
def __unicode__(self):
"""Pretty print the SideNames object"""
return self.name
team = models.ForeignKey(Team)
name = models.CharField(max_length=128)

By any chance, does your test do something like this:
from myapp import models
...
models.SideNames = None
since that's the only explanation I can think of for why SideNames should be None in the context of that method.
As an aside, the method itself is pointless, as backwards relations are automatically provided by Django, so you could just call t.sidenames_set.all().

In the module that defines the test, you're importing the name SideNames from some other module. In the module where that sides method is defined, the name SideNames is not defined or imported.

Related

Change representation of Superclass without field depending of Subclass object (__str__)

I am running a django app and have a setup like this:
ModelSuper(models.Model):
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name
So ModelForeign has a FK to ModelSuper. What happens now is that when I create an instance of ModelForeign I can choose if it belongs either to ModelSub1 or to ModelSub2. But the string representation is ModelSuper Onject (3) where (3) is the id.
Normally I can change this representation by overwriting the __str__ method on the model, but since I do not have any fields on the Supermodel I can't return anything.
What I tried:
I have already implemented the __str__ method in the Submodels but that does not help.
I wanted to make the Super model abstract. But this does not let me point FKs to the Supermodel, so I can't do this. My setup requires this FK
I used a generic FK with django's ContentType framework. This is also not an option because it messes completely with my app and is also not recommended from an SQL perspective.
Also when I do API-calls I get ModelSuper Onject (3) back instead of a human-readable name.
Is there a way to do what I intend to do? Thanks in advance for help and hints. Very much appreciated!
EDIT1: What I tried thanks to Abdul's help:
class ModelA(models.Model):
class Meta:
abstract = False
TYPE_CHOICES = [('sub1', 'sub1'), ('sub2', 'sub2')]
type_model = models.CharField(max_length=50, choices=TYPE_CHOICES, null=True, blank=True)
def __str__(self):
if self.type_model == "sub1":
return "sub1"
elif self.type_model == "sub2":
return "sub2"
else:
return "unkown"
I am not understanding how your foreign key works as model inheritance means the tables are separate. How about trying something like this:-
ModelA(models.Model):
TYPE_CHOICES = [('Sub1', 'ModelSub1'), ('Sub2', 'ModelSub2')]
model_type = models.CharField(max_length=4, choices=TYPE_CHOICES)
def __str__:
# Return string representation using if-else
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name

Object has no attribute _set', Django

I have these models:
class BlogCategory(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
def get_number_of_categorys_items(self):
return self.post_set.count()
class Post(models.Model):
title = models.CharField(max_length=130)
content = models.TextField()
category = models.ForeignKey(BlogCategory, related_name='blog_category')
def __str__(self):
return self.title
And when I try to call method get_number_of_categorys_items it cause error:
AttributeError: 'BlogCategory' object has no attribute 'post_set'
This method should return number of posts with this category.
What can I do?
Since you've specified the related_name, Django would not automatically create the related name as post_set for you. Either use:
def get_number_of_categorys_items(self):
return self.blog_category.count()
Or, don't specify the related_name:
category = models.ForeignKey(BlogCategory)

Django tests getting error

I'm trying to prepare some simple tests for my app. I have a model as below:
class Kategoria(models.Model):
nazwa = models.CharField('Nazwa Kategorii', max_length=30)
class Meta:
verbose_name = "Kategoria"
verbose_name_plural = "Kategorie"
def __str__(self):
return self.nazwa
In the tests.py:
import unittest
from blog.models import Kategoria
class KategoriaTestCase(unittest.TestCase):
def setUp(self):
self.nazwa = 'Nowa_kategoria'
def test_tworzenie_obiektu(self):
tworzenie_nowej_kategoria=Kategoria.objects.create(self.nazwa)
self.assertTrue(tworzenie_nowej_kategoria)
self.assertEqual(nowa_kategoria.nazwa,'Nowa_kategoria')
On the end test fails because:
TypeError: create() takes 1 positional argument but 2 were given
What am I doing wrong?
create takes keywords arguments.
In your test_tworzenie_obiektu method change
tworzenie_nowej_kategoria = Kategoria.objects.create(self.nazwa)
^^^^
to
tworzenie_nowej_kategoria = Kategoria.objects.create(nazwa=self.nazwa)
So you method should be
def test_tworzenie_obiektu(self):
tworzenie_nowej_kategoria = Kategoria.objects.create(nazwa=self.nazwa)
self.assertTrue(tworzenie_nowej_kategoria)
self.assertEqual(nowa_kategoria.nazwa,'Nowa_kategoria')
You need to supply the field name with create(), like this:
def test_tworzenie_obiektu(self):
tworzenie_nowej_kategoria = Kategoria.objects.create(nazwa=self.nazwa)
self.assertTrue(tworzenie_nowej_kategoria)
self.assertEqual(nowa_kategoria.nazwa,'Nowa_kategoria')
You should also confirm the last self.assertEqual, it will always fail since there is no nowa_kategoria variable in your class; you probably want
self.assertEqual(tworzenie_nowej_kategoria.nazwa, self.nazwa)
Note that I removed the hardcoded name, and changed the name of the variable to that of the object being returned.

Determining a suitable subclass to store specific type of data in Python

I have a generic set of attributes (e.g. of type string and integer) and I would like to use the following django models to store them:
class Attribute(models.Model):
item = models.ForeignKey('Item')
class Meta:
abstract = True
#staticmethod
def get_subclass_by_type(type):
TEXT_TYPES = ["text", "string"]
INTEGER_TYPES = ["integer", "number"]
if type in TEXT_TYPES:
return TextAttribute
if type in INTEGER_TYPES:
return IntegerAttribute
class TextAttribute(Attribute):
value = models.CharField(max_length=128)
class IntegerAttribute(Attribute):
value = models.IntegerField()
Is there any clean & simple way I've missed to define the types in the subclasses directly? Something like:
class Attribute(models.Model):
item = models.ForeignKey('Item')
class Meta:
abstract = True
#staticmethod
def get_subclass_by_type(type):
<do something>
class TextAttribute(Attribute):
TYPES = ["text", "string"]
value = models.CharField(max_length=128)
class IntegerAttribute(Attribute):
TYPES = ["integer", "number"]
value = models.IntegerField()
You can try to define your method get_subclass_by_type like the following
#classmethod
def get_subclass_by_type(cls, type):
for subcls in cls.__subclasses__():
if type in subcls.TYPE:
return subcls
Haven't tested this code, but i think it should work.

python unit test: assertEqual on same objects throwing AssertionError

I have a class as
class PlaylistManager(models.Manager):
def add_playlist(self, name):
playlist = Playlist(name=name)
playlist.save()
return playlist
def get_playlist_with_id(self, id):
return super(PlaylistManager, self).get_query_set().filter(pk=id)
class Playlist(models.Model):
name = models.CharField(max_length=30)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
deleted = models.BooleanField(default=False)
objects = PlaylistManager() # is a customer manager
def __repr__(self):
return '<Playlist name:%s, date_created:%s, date_modified:%s, deleted:%s>' % \
(self.name, self.date_created, self.date_modified, self.deleted)
class Meta:
db_table = 'playlists'
and i test as
def test_get_playlist(self):
playlist = Utility.add_playlist()
self.assertEqual(Playlist.objects.get_playlist_with_id(playlist.id), playlist)
class Utility():
#staticmethod
def add_playlist(playlist_name=PLAYLIST):
return Playlist.objects.add_playlist(playlist_name)
When I run the test, I see error as
AssertionError: [<Playlist name:playlist, date_created:2012-07-18 19:54:12.265909+00:00, date_modified:2012-07-18 19:54:12.265955+00:00, deleted:False>] != <Playlist name:playlist, date_created:2012-07-18 19:54:12.265909+00:00, date_modified:2012-07-18 19:54:12.265955+00:00, deleted:False>
even when the two objects are same.
Is there anything I am missing here?
assertEqual() uses the == operator to compare the classes. The default == operator of user-defined classes compares instances by object identity. This means two instances are only considered equal when they are the same instance.
Compare model id or pk and it will be ok.
The AssertionError is correct. Your get_playlist_with_id returns a Queryset not a Playlist instance. If you want get_playlist_with_id to return the single instance then you should use get not filter.

Categories