8

The command python manage.py makemigrations fails most of time due to the forms.py, in which new models or new fields are referenced at class definition level.

So I have to comment each such definitions for the migration to operate. It's a painfull task.

I don't understand why the migration process import the forms.py module. I think that importing models modules should be sufficient.

Is there a way to avoid those errors ?

albar
  • 3,020
  • 1
  • 14
  • 27
  • 1
    You haven't shown the traceback, but my guess is that the Django checks framework is loading the urls, which in turn loads the views and forms. You should be able to restructure your form to avoid the errors, but we can't help with that unless you show some code. [This question](http://stackoverflow.com/questions/37726396/upgrading-from-django-1-6-to-1-9-python-manage-py-migrate-failure/37747009#37747009) is similar. – Alasdair Sep 16 '16 at 16:38
  • Thanks @alasdair. If migrations import `url.py`, then I understand my problem. I still not understand why it imports the urls! I am surprised that other people does not claim about that. – albar Sep 16 '16 at 21:06
  • 1
    It's the system check that imports the URLs, not the migration. The [system checks](https://docs.djangoproject.com/en/1.10/topics/checks/#module-django.core.checks) run before the makemigrations command. – Alasdair Sep 16 '16 at 21:27
  • OK I understand. I am just surprised to be alone to get this problem. – albar Sep 16 '16 at 21:35
  • 1
    You're not alone. I linked to a similar question in the first comment. – Alasdair Sep 16 '16 at 21:39
  • I had the same troubles and Nate's answer saved my day, see below. – Edouard Thiel Apr 12 '20 at 11:33

6 Answers6

8

I was having this same issue and found the specific problem. When the migrate command was being called, Django's system checks made their way into my forms.py and then would fail when they encountered a line of code that made a query against a table that the migration was supposed to create. I had a choicefield that instantiated the choices with a database query like this:

university = forms.ChoiceField(
    choices=[('', '')] + [(university.id, university.name) for university in University.objects.all()],
    widget=forms.Select(
        attrs={
            'class': 'form-control',
            'placeholder': 'University',
        }
    ),
    required=True
)

The solution was to remove the query from choices (leaving it just as [('', '')] and then populate the choices in the class's init method instead.

class UniversityForm(forms.Form):

    university = forms.ChoiceField(
        choices=[('', '')],
        widget=forms.Select(
            attrs={
                'class': 'form-control',
                'placeholder': 'University',
            }
        ),
        required=True
    )


def __init__(self, *args, **kwargs):
    super(UniversityForm, self).__init__(*args, **kwargs)

    # Load choices here so db calls are not made during migrations.
    self.fields['university'].choices = [('', '')] + [(university.name, university.name) for university in University.objects.all()]
Nate
  • 321
  • 3
  • 6
  • 3
    This is the right answer! Django imports urls.py -> view.py -> forms.py, in which class declarations execute class instances declarations (like `choices=...` and `querysets=`), so these latters have to be moved in `__init__`. – Edouard Thiel Apr 12 '20 at 09:31
  • this is the right answer op, please review it. – rmindzstar Feb 02 '21 at 10:33
  • You shouldn't even do that, just use a [ModelChoiceField](https://docs.djangoproject.com/en/4.1/ref/forms/fields/#modelchoicefield). I don't know why people don't notice that there's a form field that perfectly fits their needs... – Abdul Aziz Barkat Nov 08 '22 at 11:58
3

Thanks to @alasdair I understood my problem and found a workaround: I replace the original code in the views.py file

from MyApp import forms

with

import sys
if 'makemigrations' not in sys.argv and 'migrate' not in sys.argv:
    from MyApp import forms

It works fine in my case, but I suppose there is a better way to know if the current process is a migration or not. If so, please advise.

albar
  • 3,020
  • 1
  • 14
  • 27
  • 3
    This is probably not the right way to go. The issue in the forms.py should be fixed - it sounds like it's probably querying the database at import time, which is not a good idea. See Nate's answer for a way to restructure a form to avoid this. – Adam Johnson May 14 '19 at 11:08
  • Let me point out one of the glaring disadvantages of not fixing the form, the queries won't run each time the form gets instantiated, which means the options you show to the user are stale / old (Might not even exist in the database anymore, and will be missing new entries). The error is in fact helpful I'd say since it points out this subtle and easily unnoticed bug. By using this hack you're solving one error but letting a bug stay. – Abdul Aziz Barkat Nov 09 '22 at 07:57
0

Exclude the new columns with .only in your query like this:

University.objects.only('id', 'name').all()

Then run your migration.

Soliman
  • 441
  • 1
  • 8
  • 15
0

I had a similar issue with ModelChoiceField in one of my forms. I had to comment out my forms code in order to be able to make migrations.

For me the solution was to move all form imports into their respective view method in views.py.

before:

from .forms import CalculatorForm

def calculator(request):            
    if request.method != 'POST':
        form = CalculatorForm()
        # ...

after:

def calculator(request):
    from .forms import CalculatorForm
    if request.method != 'POST':
        form = CalculatorForm()
        # ...
Erik Kalkoken
  • 30,467
  • 8
  • 79
  • 114
  • What a pain if you have a lot of views! Despite other comments, I think my solution is the best one because it takes only 3 more lines a the beginning of the module and avoids polluting the rest of the code. – albar Oct 06 '19 at 13:59
  • Well that is debatable. It's only one additional import for each view that has a form. – Erik Kalkoken Oct 06 '19 at 14:42
  • Huh, you had this issue with a `ModelChoiceField`? Are you sure you aren't evaluating the queryset in some way? Can you show your forms code? Usually a `ModelChoiceField` is the solution to this problem. – Abdul Aziz Barkat Nov 09 '22 at 07:58
0

Since Django>=3.0 some management commands could be called without using the checks framework in advance via --skip-checks (reference). So migrations could be applied, even if some code paths in forms, views, whatever are currently not in an ideal condition (again: see answer from @erik-kalkoken for a cleaner solution):

./manage.py migrate --skip-checks
tombreit
  • 1,199
  • 8
  • 27
-1

init via callable...

def get_provinces():
province_choices = []
for province in ProvinceCode.objects.filter(country_code_id=1).order_by('code'):
    province_choices.append((province.code, province.code))
return province_choices

class MemberForm(forms.Form):
    provinces = forms.ChoiceField(label='Provinces', 
    choices=get_provinces, required=True)

Refer here - Django relation error when running make migrations

Rajender Saini
  • 594
  • 2
  • 9
  • 24
  • Please don't post link-only answers to other Stack Exchange questions. Instead, include the essential portions of the answer here, and *tailor the answer to this specific question.* – Blue Sep 13 '18 at 17:33