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