Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - unavailable field of model while doing migration

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?

  • a syntax and names are OK,
  • prepared migration (by Django) is OK as well (have all fields)

--------------------

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.

like image 215
Sławomir Lenart Avatar asked Jul 04 '17 13:07

Sławomir Lenart


2 Answers

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
like image 160
DiegoG Avatar answered Oct 03 '22 09:10

DiegoG


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, ...

like image 38
Sławomir Lenart Avatar answered Oct 03 '22 09:10

Sławomir Lenart