user926352
user926352

Reputation:

Django 1.10 - App models in a subdirectory not working as expected

Following the tutorial for Django 1.10 with a few small deviations:

    mysite/
    |
    |- apps/
    |  |- polls/
    |  |  |- models/
    |  |  |  |- __init__.py
    |  |  |  |- Question.py
    |  |  |  |- Choice.py

apps/polls/models/__init__.py is importing both Question and Choice, and both models are extending from django.db.models.Model.

The issue
Running makemigrations throws an exception:

Traceback (most recent call last):
  File "/mysite/lib/python3.5/site-packages/django/db/models/fields/related.py", line 742, in __init__
    to._meta.model_name
AttributeError: module 'apps.polls.models.Question' has no attribute '_meta'

However, the Question model clearly does have a _meta attribute, as it is extending from django.db.models.Model. It is my understanding that as of Django 1.7, defining _meta.app_label is not necessary (in any case, defining app_label doesn't work). Any idea what I need to do to be able to use models from a subdirectory without causing additional refactoring throughout my project?

Additional resources

Upvotes: 1

Views: 488

Answers (1)

knbk
knbk

Reputation: 53679

You are importing the Question module instead of the Question model. You need to change it like this:

from django.db import models

from .Question import Question


class Choice(models.Model):
    question = models.ForeignKey(Question,
                                 on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=250)
    votes = models.IntegerField(default=0)

Here the first .Question refers to the module, and the second Question refers to the model. The same holds for the import statements in apps/polls/models/__init__.py.

It's a good idea to use a naming scheme that differentiates between modules and classes. This avoids confusion about what you are actually importing. The usual convention is to use lowercase for module names, and CamelCase for class names. That would give you questions.py and class Question(models.Model):.

mysite/
|
|- apps/
|  |- polls/
|  |  |- models/
|  |  |  |- __init__.py
|  |  |  |- question.py
|  |  |  |- choice.py

And:

# apps/polls/models/choice.py
from django.db import models

from .question import Question


class Choice(models.Model):
    question = models.ForeignKey(Question,
                                 on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=250)
    votes = models.IntegerField(default=0)

By following such a convention, the distinction between the module question and the model Question is apparent at first sight.

Upvotes: 1

Related Questions