maybe I am exhausted and don't see something simple, but in Django 1.9.7, while doing the migration I found something strange, and I am looking for an explanation.
While getting a model class by apps (it is (django.db.migrations.state.StateApps) in RunPython operation I have AttributeError for the field which exists.
My model:
class Weight(models.Model):
INF = 2**31-1
minimum = models.PositiveIntegerField()
maximum = models.PositiveIntegerField()
carrier = models.ForeignKey(Carrier)
class Meta:
ordering = ['carrier__name', 'minimum']
in migration method runned from RunPython, I have:
Weight = apps.get_model('calc.Weight')
then have exception, but only for some fields.
from debugging (inside method runned by RunPython):
>>> Weight.maximum
Traceback (most recent call last):
File "<pudb command line>", line 1, in <module>
AttributeError: type object 'Weight' has no attribute 'maximum'
>>> Weight.minimum
Traceback (most recent call last):
File "<pudb command line>", line 1, in <module>
AttributeError: type object 'Weight' has no attribute 'minimum'
>>> Weight.INF
Traceback (most recent call last):
File "<pudb command line>", line 1, in <module>
AttributeError: type object 'Weight' has no attribute 'INF'
but:
>>> Weight.carrier
<django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x7f8dcca692d0>
>>> Weight._meta.fields
(<django.db.models.fields.AutoField: id>, <django.db.models.fields.PositiveIntegerField: minimum>,
<django.db.models.fields.PositiveIntegerField: maximum>, <django.db.models.fields.related.ForeignKey: carrier>)
type(Weight)
<class 'django.db.models.base.ModelBase'>
so somehow only carrier field is available, why?
update: my migration file is:
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
def add_weights(app, *args):
Carrier = app.get_model('calc.Carrier')
Weight = app.get_model('calc.Weight')
# import pudb;pu.db
carrier_obj = Carrier.objects.get(name='MainCarrier')
Weight.objects.create(carrier=carrier_obj, minimum=1, maximum=400) # OK, yes it works within `create`
Weight.objects.create(carrier=carrier_obj, minimum=401, maximum=800) # OK
Weight.objects.create(carrier=carrier_obj, minimum=800, maximum=Weight.INF) # here is AttributeError
class Migration(migrations.Migration):
dependencies = [
('calc', '0012_auto_20170622_1310'),
]
operations = [
migrations.CreateModel(
name='Weight',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('minimum', models.PositiveIntegerField()),
('maximum', models.PositiveIntegerField()),
('carrier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='calc.Carrier')),
],
options={
'ordering': ['carrier__name', 'minimum'],
},
),
migrations.RunPython(add_weights)
]
btw: after all I can place INF outside class body, and have workaround, but knowledge what is happening is more important to me.
The app.get_model(...) call will return a django.db.models.base.ModelBase instance, not your Weight model, that's why you can't see INF.
Import it with an alternative name (so it doesn't shadow your Weight variable), and you'll be able to use it:
from myapp.models import Weight as WeightModel
...
...
maximum = WeightModel.INF
FYI, I resolved my issue by putting non-model fields and custom methods into the definition of a new class
in general:
class MyModelInterface(object): ..
class MyModel(models.Model, MyModelInterface): ..
where the MyModelInterface is a mixin for the model, but if needed I can use that separately.
I found it as a good practice for Django models, so in special needs like migration, I can assess interface class directly. Also, it's helpful to avoid a very long model's body with many custom methods, properties, ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With