Django Foreign Keys Breaking with Multi-Table Inheritance - django

The following code works as I expect it to:
class General(Model):
pass
class Captain(Model):
general = ForeignKey('General',related_name='captains')
I can create a general, add captains, and doing "general.captains" works as expected.
But when both these classes inherit from a base class that might have extra info, disaster strikes.
class Officer(Model):
pass
class General(Officer):
pass
class Captain(Officer):
general = ForeignKey('General',related_name='captains')
>>> g = General()
>>> g.captains
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Python27\lib\site-packages\django\db\models\fields\related.py", line
391, in __get__
self.related.model._default_manager.__class__)
File "C:\Python27\lib\site-packages\django\db\models\fields\related.py", line
469, in create_manager
getattr(instance, attname)}
File "C:\Python27\lib\site-packages\django\db\models\fields\related.py", line
301, in __get__
raise self.field.rel.to.DoesNotExist
DoesNotExist
Any idea what might be happening here, and how I could fix it?

It should work if you define your Officer model explicitly as abstract
class Meta:
abstract = True
So as a test i slightly modified your base class:
class Officer(models.Model):
name = models.CharField(max_length=255)
class Meta:
abstract = True
And the following works:
>>> General(name='Warfield').save()
>>> G = General.objects.all()[0]
>>> Captain(name='Picard', general=G).save()
>>> C = Captain.objects.all()[0]
>>> C.general.name
u'Warfield'
>>> G.captains.all()[0].name
u'Picard'

Related

Django: debug AttributeError: can't set attribute

NOTE: I finally found the bug, so the below text was valuable perhaps only to me. The short answer: I decided to create a model field out of an attribute I had earlier defined as a #property-method. The only place where I hadn't removed the #property-method was in the Orchid model.
After some tweaked and poking to my code, I suddenly get this error: AttributeError: can't set attribute. I haven't changed any of the code for Orchid, but I now get this error:
>>> orc = Orchid.objects.get(id=1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/Users/cole/PycharmProjects/Sites/virtualenvs/orchidislandcapital.com/lib/python2.7/site-packages/django/db/models/manager.py", line 151, in get
return self.get_queryset().get(*args, **kwargs)
File "/Users/cole/PycharmProjects/Sites/virtualenvs/orchidislandcapital.com/lib/python2.7/site-packages/django/db/models/query.py", line 301, in get
num = len(clone)
File "/Users/cole/PycharmProjects/Sites/virtualenvs/orchidislandcapital.com/lib/python2.7/site-packages/django/db/models/query.py", line 77, in __len__
self._fetch_all()
File "/Users/cole/PycharmProjects/Sites/virtualenvs/orchidislandcapital.com/lib/python2.7/site-packages/django/db/models/query.py", line 854, in _fetch_all
self._result_cache = list(self.iterator())
File "/Users/cole/PycharmProjects/Sites/virtualenvs/orchidislandcapital.com/lib/python2.7/site-packages/django/db/models/query.py", line 230, in iterator
obj = model(*row_data)
File "/Users/cole/PycharmProjects/Sites/virtualenvs/orchidislandcapital.com/lib/python2.7/site-packages/django/db/models/base.py", line 347, in __init__
setattr(self, field.attname, val)
AttributeError: can't set attribute
The definition for Orchid is class Orchid(FinancialReturnMixin, PeerPerformance). I haven't changed the FinancialReturnMixin, whose code is:
class FinancialReturnMixin(models.Model):
exclude_special_dividend = True
round_to = 4
shares_outstanding = models.FloatField(blank=True, null=True)
stock_price = models.FloatField(
verbose_name='quarter-end stock price',
blank=True, null=True)
class Meta:
abstract = True
app_label = 'snippets'
The second part of the Orchid class definition is PeerPerformance from which I have commented out the one change I had made. The definition for PeerPerformance is class PeerPerformance(DividendBookValueMixin) and all I did here was add 1 additional field to the model. DividendBookValueMixin is an abstract model.
I deleted my Orchid migrations, datatable and relevant south_migrationhistory entries. With class Orchid(models.Model), the Orchid model sets up fine. With class Orchid(PeerPerformance) the Orchid error remains. All my tests against PeerPerformance run. I can read and save PeerPerformance objects just fine.
>>> from peer.models import PeerPerformance as PP
>>> pp1 = PP.objects.get(id=1)
>>> pp1.dividend = 0.135
>>> pp1.save()
DividendBookValueMixin is the parent class for PeerPerformance. With class Orchid(DividendBookValueMixin) the error remains. All my tests agains DividendBookValueMixin run.
Any thoughts where to look?
(On linux)Sometime this occurs when you don't have read write permissions to the project directory or in virtualenv directory. Make sure you have apropriate read write permissions to the directory.

Counting mongoengine ListField elements

I`m having a hard time migrating my models from standard Django orm with MySQL to mongoengine-odm.
I have the following model that works fine in the old structure:
class Place(Document):
name = StringField()
acronym = StringField()
parent = ReferenceField('self')
hierarchy = ListField(ReferenceField('self'))
hierarchy_size = IntField()
#classmethod
def preSave(instance, sender, **kwargs):
instance.hierarchy_size = len(instance.hierarchy)
signals.pre_save.connect(Place.preSave, sender=Place)
But when working with the new mongoengine, I'm facing problems to access the objects hierarchy size in the way I was doing before. Receiving:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/awesome_user/workspace/awesome_project/project/models.py", line 728, in importPlacesFromMySQL
myCountry.save()
File "/Users/awesome_user/workspace/awesome_project/site_env/lib/python2.7/site-packages/mongoengine/document.py", line 220, in save
signals.pre_save.send(self.__class__, document=self)
File "/Users/awesome_user/workspace/awesome_project/site_env/lib/python2.7/site-packages/blinker/base.py", line 267, in send
for receiver in self.receivers_for(sender)]
File "/Users/awesome_user/workspace/awesome_project/project/models.py", line 804, in preSave
instance.hierarchy_size = len(instance.hierarchy)
TypeError: object of type 'ListField' has no len()
Does anyone know how to access the properties of the instance, like the length in my case?
You have a mistake in defining preSave classmethod. You are trying to take a len from hierarchy ListField from Place class (not instance). This is because you are using the first argument of the method - it's not an instance, it's a class itself. Use the 3rd argument instead:
#classmethod
def preSave(cls, sender, document, **kwargs):
document.hierarchy_size = len(document.hierarchy)
len(object.field) will retrieve all child documents and then count. If you only want the length of the array without retrieving all the documents you can use len(object.to_mongo()["field"])

django add_to_class() making models inheritance/MRO work wrong

I have a problem with inheritance on my models when adding fields via add_to_class().
I have a models File(models.Model) and Image(File) - these come from django-filer.
In my app I'm importing them and adding fields and methods:
def method_x(self):
print "x"
File.add_to_class("expiration_date", models.DateField(null=True, blank=True))
File.add_to_class("method_x", method_x)
Image should inherit both of those but it gets only the method(s), not field(s):
>>> some_file = File.objects.get(id=8)
>>> some_image = Image.objects.get(id=8)
>>>
>>> print some_file.expiration_date # this works
... None
>>>
>>> some_image.metgod_x() # this works
>>> x
>>>
>>> print some_image.expiration_date # and this not
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Image' object has no attribute 'expiration_date'
Any clue?
Your model's add_to_class does not add the field as an attribute. it just calls contribute_to_class on your field:
django/db/models/base.py#L218
Your field's contribute_to_class does not do it either. It just adds the field to the model's _meta member: django/db/models/fields/__init__.py#L234

Django ManyToManyField error

I am using Django's ManyToManyField, and when i try to add data to it, i get the following error:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/purav/Desktop/socmov/fbcon/models.py", line 165, in getMovieInfo
movie.genre.add( G )
File "/usr/local/lib/python2.6/dist-packages/django/db/models/fields/related.py", line 503, in add
self._add_items(self.source_field_name, self.target_field_name, *objs)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/fields/related.py", line 563, in _add_items
(obj, self.instance._state.db, obj._state.db))
ValueError: Cannot add "<Genre: Genre object>": instance is on database "None", value is on database "default"
I am using Django 1.3, and i following is a part of my code:
class Genre(models.Model):
gid = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 20)
class Movie(models.Model):
mid = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 100)
genre = models.ManyToManyField("Genre")
This is where the error occurs:
G = Genre.objects.get(gid = obj[i]['id'])
print G.name, G.gid
movie.genre.add( G )
It is guaranteed that obj[i]['id'] will be found inside Genre. Can someone please help me?
You need to save the movie instance after creating it, before you can add m2m relations.

In Django, how do you retrieve a field of a many-to-many related class?

I have two classes with a ManyToMany relationship. I'd like to select one from the first class and access the fields of the related class. It seems like this should be easy. For example:
class Topping(models.Model):
name = models.CharField(max_length=40)
class Pizza(models.Model):
name = models.CharField(max_length=40)
toppings = models.ManyToManyField(Topping)
So I'd want to do something like:
Pizza.objects.filter(name = 'Pizza 1')[0].toppings[0]
But this doesn't work for me. Thanks for any help.
Try:
Pizza.objects.filter(name = 'Pizza 1')[0].toppings.all()[0]
It works for me (different models, but the idea is the same):
>>> Affiliate.objects.filter(first_name = 'Paolo')[0]
<Affiliate: Paolo Bergantino>
>>> Affiliate.objects.filter(first_name = 'Paolo')[0].clients
<django.db.models.fields.related.ManyRelatedManager object at 0x015F9770>
>>> Affiliate.objects.filter(first_name = 'Paolo')[0].clients[0]
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: 'ManyRelatedManager' object is unindexable
>>> Affiliate.objects.filter(first_name = 'Paolo')[0].clients.all()
[<Client: Bergantino, Amanda>]
>>> Affiliate.objects.filter(first_name = 'Paolo')[0].clients.all()[0]
<Client: Bergantino, Amanda>
For more on why this works, check out the documentation.