Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trace this? AttributeError: 'NoneType' object has no attribute 'is_relation' during makemigrations

I'm getting a confusing error for the second time since yesterday. Last time I just flattened my whole migrations, but I've never actually found what caused the problem.

So this comes up when I try to makemigrations for my python project. Where should I look for errors? I have feeling it's not actually about the migrations, but rather about errors in views.py or models.py even though I absolutely don't understand why this influences db migration.

Anyway, none of theses errors points to code that I have written. It's all in Django. So how to find the error that causes that?

(testenv1) C:\Users\user\eclipse_workspace\test1\test1>python manage.py makemigrations --trace
Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    execute_from_command_line(sys.argv)
  File "C:\Python27\testenv1\lib\site-packages\django\core\management\__init__.py", line 363, in execute_from_command_line
    utility.execute()
  File "C:\Python27\testenv1\lib\site-packages\django\core\management\__init__.py", line 355, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\Python27\testenv1\lib\site-packages\django\core\management\base.py", line 283, in run_from_argv
    self.execute(*args, **cmd_options)
  File "C:\Python27\testenv1\lib\site-packages\django\core\management\base.py", line 330, in execute
    output = self.handle(*args, **options)
  File "C:\Python27\testenv1\lib\site-packages\django\core\management\commands\makemigrations.py", line 150, in handle
    loader.project_state(),
  File "C:\Python27\testenv1\lib\site-packages\django\db\migrations\loader.py", line 323, in project_state
    return self.graph.make_state(nodes=nodes, at_end=at_end, real_apps=list(self.unmigrated_apps))
  File "C:\Python27\testenv1\lib\site-packages\django\db\migrations\graph.py", line 409, in make_state
    project_state = self.nodes[node].mutate_state(project_state, preserve=False)
  File "C:\Python27\testenv1\lib\site-packages\django\db\migrations\migration.py", line 92, in mutate_state
    operation.state_forwards(self.app_label, new_state)
  File "C:\Python27\testenv1\lib\site-packages\django\db\migrations\operations\fields.py", line 148, in state_forwards
    delay = not old_field.is_relation
AttributeError: 'NoneType' object has no attribute 'is_relation'

(testenv1) C:\Users\user\eclipse_workspace\test1\test1>

Here is my models.py. It's currently WIP:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models
#from wheel.metadata import unique
from datetime import datetime
from django.urls import reverse
# Create your models here.
from django.shortcuts import redirect

class Task(models.Model):
    name = models.CharField(max_length=255,blank=True)
    code = models.SlugField(max_length=255, unique=True)
    sender = models.ForeignKey("Sender", default=0)
    client = models.ForeignKey("Client", null=True,blank=True)
    datetime = models.DateTimeField(default=datetime.now)
    duration = models.IntegerField(default=0,blank=True)
    photo = models.ImageField(null=True, blank=True)
    route = models.TextField(blank=True)
    km = models.FloatField(default=0, blank=True)
    notes = models.TextField(blank=True)
    milage_receipt = models.ForeignKey("MilageReceipt", null=True, blank=True, on_delete=models.SET_NULL)

    def date(self):
        return self.datetime.date()
    def status(self):
        try: 
            if self.receipt and self.milage_receipt:
                return "done"
            elif not self.receipt:
                return "new"
            elif not self.milage_receipt:
                return "ok"
        except Receipt.DoesNotExist:
            return "new"
        except MilageReceipt.DoesNotExist:
            return "processing"
        return str(self.receipt)

    def save(self, *args, **kwargs):
        if self.duration == None:
            self.duration = 0
        if self.km == None:
            self.km = 0
        super(Task, self).save(*args, **kwargs) # Call the "real" save() method.

    def get_absolute_url(self):
        return reverse('task_update', args=[str(self.id)])

    def __unicode__(self):
            return self.code


class Client(models.Model):
    name = models.CharField(max_length=255)
    name2 = models.CharField(max_length=255, blank=True)
    slug = models.SlugField(unique=True, max_length=255)
    email = models.EmailField()
    handle = models.CharField(max_length=255, blank=True)
    contact_name = models.CharField(max_length=255, blank=True)
    street = models.CharField(max_length=255)
    zip = models.IntegerField()
    city = models.CharField(max_length=255)
    street2 = models.CharField(max_length=255,blank=True)
    zip2 = models.CharField(max_length=255,blank=True)
    city2 = models.CharField(max_length=255,blank=True)
    default_vat = models.IntegerField(default=7)
    km_price = models.FloatField(default=0)
    active = models.BooleanField(default=False)
    def __unicode__(self):
            return self.slug

class Sender(models.Model):
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    email = models.EmailField()
    street = models.CharField(max_length=255)
    zip = models.IntegerField()
    city = models.CharField(max_length=255)
    phone = models.CharField(max_length=255)
    website = models.URLField()
    tax_id = models.CharField(max_length=255)
    vat_id = models.CharField(max_length=255)
    bank = models.ForeignKey("BankAccount")

    def __unicode__(self):
        return self.slug

class BankAccount(models.Model):
    name = models.CharField(max_length=255)
    iban = models.CharField(max_length=255)
    bic = models.CharField(max_length=255)
    holder = models.CharField(max_length=255)

class ReceiptNumber(models.Model):
    id = models.AutoField(primary_key=True, unique=True)
    number = models.IntegerField(blank=True)

    def __unicode__(self):
        return str(self.id)

class MyQuerySet(models.query.QuerySet):
    def delete(self):
        print self
        raise

class SingleDeleteManager(models.Manager):
    def get_query_set(self):
        return MyQuerySet(self.model, using=self._db)  

class Receipt(models.Model):
    number = models.OneToOneField("ReceiptNumber", blank=True, related_name="receipt", null=True)
    vat = models.IntegerField(blank=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2, blank=True)
    body = models.TextField(blank=True)
    date = models.DateField(default=datetime.now, blank=True)
    task = models.OneToOneField("Task", related_name="receipt")
    objects = SingleDeleteManager()
    def total(self):
        a = float(self.amount) * (self.vat / 100.0) + float(self.amount)
        return "%.2f" % round(a,2)
    def vat_amount(self):
        a = float(self.amount) * (self.vat / 100.0)
        return "%.2f" % round(a,2)

    def save(self,*args, **kwargs):
        if self.date == None:
            self.date = datetime.now()
        if self.amount == None:
            self.amount = 0  
        if self.vat == None:
            self.vat = self.task.client.default_vat
        super(Receipt, self).save(*args, **kwargs)
        #if not hasattr(self, "number") or self.number == None:
        #    new_number = ReceiptNumber.objects.create()
        #    new_number.number = ReceiptNumber.objects.filter(Q(receipt__task__sender_id=self.sender.id) | Q(milage_receipt__task__sender_id=self.sender.id)).latest('id').number+1
        #    new_number.save()
        #    self.number = new_number 
        #    super(Receipt, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        if hasattr(self,"number") and self.number.id:
            self.number.delete()
        return super(self.__class__, self).delete(*args, **kwargs)
    def get_absolute_url(self):
        return reverse('receipt_create_for_task', args=[str(self.task.id)])
    def __unicode__(self):
        try:
            return str(self.number) + ": " + self.task.code
        except Task.DoesNotExist:
            return "should be gone"

class ReceiptTemplate(models.Model):
    name = models.CharField(max_length=255)
    vat = models.IntegerField()
    amount = models.FloatField()
    body = models.TextField()

class MilageReceipt(models.Model):
    number = models.OneToOneField("ReceiptNumber", null=True)
    sender = models.ForeignKey("Sender")
    client = models.ForeignKey("Client")

It's Version 1.11!

like image 751
JasonTS Avatar asked May 03 '17 16:05

JasonTS


2 Answers

I found an extremely useful way of debugging this on this ticket on Django by Radek.

If you feel brave, you can edit the state_forwards() method on django/db/migrations/operations/fields.py adding a debug print on the first line.

def state_forwards(self, app_label, state):
    # THIS IS THE LINE TO ADD
    print app_label + " " + self.model_name_lower + " " + self.name
    # END OF THE LINE TO ADD
    new_fields = []
    old_field = None
    for name, instance in state.models[app_label, self.model_name_lower].fields:
        if name != self.name:
            new_fields.append((name, instance))
        else:
            old_field = instance
    state.models[app_label, self.model_name_lower].fields = new_fields
    # Delay rendering of relationships if it's not a relational field
    delay = not old_field.is_relation
    state.reload_model(app_label, self.model_name_lower, delay=delay)

Then run again ./manage.py makemigrations. Before crashing, you should see the name of the model that is making your migration fail.

Notice on the example below that the last model to be processed before crashing is adsummary on the core app, more specifically the account field. By the way, after finding this I just searched for adsummary on the migrations folder on the core app, then found out that there was a command for removing this column in one of the migration files, though the column wasn't on the database anymore, deleted this command and makemigrations started working again.

Felipes-MacBook-Air:backend felipe$ ./manage.py makemigrations -v 3
contenttypes contenttype name
core adsummary account
Traceback (most recent call last):
  File "./manage.py", line 22, in <module>
    execute_from_command_line(sys.argv)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/management/__init__.py", line 363, in execute_from_command_line
    utility.execute()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/management/__init__.py", line 355, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/management/base.py", line 283, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/management/base.py", line 330, in execute
    output = self.handle(*args, **options)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/management/commands/makemigrations.py", line 150, in handle
    loader.project_state(),
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/db/migrations/loader.py", line 323, in project_state
    return self.graph.make_state(nodes=nodes, at_end=at_end, real_apps=list(self.unmigrated_apps))
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/db/migrations/graph.py", line 409, in make_state
    project_state = self.nodes[node].mutate_state(project_state, preserve=False)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/db/migrations/migration.py", line 92, in mutate_state
    operation.state_forwards(self.app_label, new_state)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/db/migrations/operations/fields.py", line 149, in state_forwards
    delay = not old_field.is_relation
AttributeError: 'NoneType' object has no attribute 'is_relation'
like image 97
Felipe Ferri Avatar answered Oct 08 '22 16:10

Felipe Ferri


The same thing happened to me. I think there's a bug in Django 1.11. I downgraded to Django 1.10.7 and ran my migrations without problems and re-upgraded to 1.11 again after that. There is an open ticket:

EDIT: IT's indeed a bug and it's still there in Django 2.1.4

Found a way to fix it:

Open /django/db/migrations/operations/fields.py and go to the line indicated in the exception in the state_forwards method and change the line delay = not old_field.is_relation for delay = old_field is None or not old_field.is_relation

Found the fix on Markus Holtermann answer on https://code.djangoproject.com/ticket/28073:

I'm not entirely sure how you got to that line delay = not old_field.is_relation with old_field being None as this means there's no field with that name in state.models[app_label, self.model_name_lower].fields, but I think it's safe to go with delay = old_field is None or not old_field.is_relation.

like image 10
Wessi Avatar answered Oct 08 '22 15:10

Wessi