python unit test: assertEqual on same objects throwing AssertionError - python

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.

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

How to check in django model ManyToManyField is symmetrical,if symmetrical=False?

I'm trying to create a followers system in Django.
follows = models.ManyToManyField('self', related_name='follower', symmetrical=False, null=True, blank=True)
Since the system should not be symmetrical, how can I check that two users follow each other?
I want to have a function within the model, if possible. I might use this function within limit_choices_to of another field and some other places.
My initial thought was to add a "friends" field and make it symmetrical, but this can bring other issues later.
I didn't find an easy solution, that won't require additional models.
Here what I've done:
Creating Person(models.Model) is not required, if you have something like CustomUser(AbstractUser) implemented. Otherwise Person class can be useful in many situations.
count_ functions are pretty useful, but not required for most of the use cases.
Inside accounts/models.py I added:
RELATIONSHIP_FOLLOWING = 1
RELATIONSHIP_BLOCKED = 2
RELATIONSHIP_STATUSES = (
(RELATIONSHIP_FOLLOWING, 'Following'),
(RELATIONSHIP_BLOCKED, 'Blocked'),
)
class Person(models.Model):
relationships = models.ManyToManyField('self', through='Relationship',
symmetrical=False,
related_name='related_to')
def get_relationships(self, status):
return self.relationships.filter(
to_people__status=status,
to_people__from_person=self)
def get_related_to(self, status):
return self.related_to.filter(
from_people__status=status,
from_people__to_person=self)
def get_following(self):
return self.get_relationships(RELATIONSHIP_FOLLOWING)
def get_followers(self):
return self.get_related_to(RELATIONSHIP_FOLLOWING)
def count_following(self):
return len(self.get_relationships(RELATIONSHIP_FOLLOWING))
def count_followers(self):
return len(self.get_related_to(RELATIONSHIP_FOLLOWING))
def get_friends(self):
return self.relationships.filter(
to_people__status=RELATIONSHIP_FOLLOWING,
to_people__from_person=self,
from_people__status=RELATIONSHIP_FOLLOWING,
from_people__to_person=self)
def count_friends(self):
return len(self.get_friends())
class Relationship(models.Model):
from_person = models.ForeignKey(Person, related_name='from_people', on_delete=models.CASCADE)
to_person = models.ForeignKey(Person, related_name='to_people', on_delete=models.CASCADE)
status = models.IntegerField(choices=RELATIONSHIP_STATUSES)
def add_relationship(self, person, status):
relationship, created = Relationship.objects.get_or_create(
from_person=self,
to_person=person,
status=status)
return relationship
def remove_relationship(self, person, status):
Relationship.objects.filter(
from_person=self,
to_person=person,
status=status).delete()
return

Python convert string to class

I want to do a query on the django User table like this:
u = User.objects.filter(member__in = member_list)
where:
class Member(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField('Date of Birth', blank=True, null=True)
and member_list is a list of eligible members.
The query works fine but the problem is I do not actually know the model member is called member. It could be called anything.
I store the name of the model I want in a model called Category. I have a link to the name of the model through content_type.Category is defined as:
class Category(models.Model):
name = models.CharField('Category', max_length=30)
content_type = models.ForeignKey(ContentType)
filter_condition = JSONField(default="{}", help_text=_(u"Django ORM compatible lookup kwargs which are used to get the list of objects."))
user_link = models.CharField(_(u"Link to User table"), max_length=64, help_text=_(u"Name of the model field which links to the User table. 'No-link' means this is the User table."), default="No-link")
def clean (self):
if self.user_link == "No-link":
if self.content_type.app_label == "auth" and self.content_type.model == "user":
pass
else:
raise ValidationError(
_("Must specify the field that links to the user table.")
)
else:
if not hasattr(apps.get_model(self.content_type.app_label, self.content_type.model), self.user_link):
raise ValidationError(
_("Must specify the field that links to the user table.")
)
def __unicode__(self):
return self.name
def _get_user_filter (self):
return str(self.content_type.app_label)+'.'+str(self.content_type.model)+'.'+str(self.user_link)+'__in'
def _get_filter(self):
# simplejson likes to put unicode objects as dictionary keys
# but keyword arguments must be str type
fc = {}
for k,v in self.filter_condition.iteritems():
fc.update({str(k): v})
return fc
def object_list(self):
return self.content_type.model_class()._default_manager.filter(**self._get_filter())
def object_count(self):
return self.object_list().count()
class Meta:
verbose_name = _("Category")
verbose_name_plural = _("Categories")
ordering = ('name',)
So I can retrieve the name of the model that links to User but I then need to convert it into a class which I can include in a query.
I can create an object x = category.content_type.model_class() which gives me <class 'cltc.models.Member'> but when I them perform a query s = User.objects.filter(x = c.category.object_list()) I get the error Cannot resolve keyword 'x' into field.
Any thoughts most welcome.
The left hand side of the filter argument is a keyword, not a python object, so x is treated as 'x', and Django expects a field called x.
To get around this, you can ensure that x is a string, and then use the python **kwarg syntax:
s = User.objects.filter(**{x: c.category.object_list()})
Thanks to https://stackoverflow.com/a/4720109/823020 for this.

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)

Testing python methods that call class methods

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.

Categories