2

Research Preamble: What have I tried?

I'm new to Django and learning my way through while trying to follow best practices. I'm working my way through Two Scoops of Django 3.x. Any questioning of structure should be directed to the authors.

I'm hopeful that the question is solid enough to not be deleted in the first few minutes as I'm really stuck on this one. If this question is to be deleted I would appreciate guidance on how to improve how I ask questions in the future. I've read the how to ask good questions blog and am hopeful I'm following this guidance. If I did miss something please do more than provide a link to the blog.

  • Please note that I have researched this question and the closest I came was a Q&A for Django 1.7 here: Django 1.7 app config ImportError: No module named appname.apps: This solution did not work for me (although it did give me a good path to try). Interestingly, they chose the exact same application structure as I did, over 5 years earlier. Please note that providing the project name in config did not solve this for me as it did the OP.
Other questions that either did not yield the needed solution:
  • django error - ImportError: No module named apps - I tried this answer and it did not work for me
  • Django Mezzanine ImportError: No module named apps - I validated that I am pointing to the specifically created Virtual Environment in PyCharm. As per Two Scoops of Django 3.2 I installed a separate envs folder and have activated a dedicated lanesflow_env for this project. PyCharm appears to have completed this since MIDDLEWARE in base.py (settings.py) has all django middleware apps imported successfully.
Other questions that were not directly related (at least as far as I can tell given my limited experience):

Relevant Information to the Question

This is the project tree:

Project tree for the Question

Contents of articles/__init__.py:
default_app_config = 'LanesFlow.apps.articles.apps.ArticlesConfig'
Contents of articles/apps.py:
from django.apps import AppConfig

class ArticlesConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.articles'
class ArticlesConfig(AppConfig):

    default_auto_field = 'django.db.models.BigAutoField'
    name = 'LanesFlow.apps.articles'  # This was checked per the answer to the researched question
Installed app in base.py (settings.py):
INSTALLED_APPS = [
    'apps.articles.ArticlesConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
Traceback on error:
    Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\threading.py", line 954, in _bootstrap_inner
    self.run()
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\threading.py", line 892, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\commands\runserver.py", line 110, in inner_run
    autoreload.raise_last_exception()
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\autoreload.py", line 87, in raise_last_exception
    raise _exception[1]
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\__init__.py", line 375, in execute
    autoreload.check_errors(django.setup)()
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\apps\registry.py", line 91, in populate
    app_config = AppConfig.create(entry)
  File "C:\Users\Carewen\AppData\Local\Programs\Python\Python39\lib\site-packages\django\apps\config.py", line 221, in create
    raise ImportError(msg)
ImportError: Module 'apps.articles' does not contain a 'ArticlesConfig' class.

Any assistance would be welcomed.

EDIT

I've reverted to the last working version once more. I've found where it goes wrong. When I try to add 'articles.app.ArticleConfig' to INSTALLED_APPS things go wrong. The thing is, this leaves me more confused. I'm trying to add templates. The process I'm following is:

  • Create templates directory within the root directory, with articles as the sub-directory (following the recommended structure in Two Scoops - The logic behind this is to allow the creation of base templates that can be used for all applications - which is what I want to accomplish with this approach).
  • At this point I get the ImportError. However, the apps.py file does contact the class ArticlesConfig(AppConfig)

Also in base.py (settings.py):

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [BASE_DIR / 'apps.templates'],  # Two scoops add. Was []
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
        ],
    },
},
]

I remain confused. My inexperience with Django is really showing here. I'm not certain what I'm doing wrong with the addition to INSTALLED_APPS. My instinct is pointing me to exploring other aspects of settings.py to see if I'm pointing someone incorrectly.

markwalker_
  • 12,078
  • 7
  • 62
  • 99
Carewen
  • 143
  • 2
  • 14
  • Your project structure looks a strange. You have your project `LanesFlow` then within that you have a sub-directory called `apps` with an app in called `articles`? Why have you rearranged the Django project structure? – Anthony Jul 08 '21 at 19:17
  • As per the post (literally the first sentence), I am following the suggested approach in the book, Two Scoops of Django 3.1: https://www.feldroy.com/products/two-scoops-of-django-3-x Please direct questions on project structure to the authors. – Carewen Jul 08 '21 at 19:32
  • I'm editing the original question based on further troubleshooting. – Carewen Jul 08 '21 at 21:05
  • 1
    I have a project structured like this, it's a nice way of doing it. This is a well put together question as well, so the down vote is harsh. Is your question really how to form your app configs and define your installed apps? – markwalker_ Jul 08 '21 at 22:12

2 Answers2

2

The problem seems to be related to:

INSTALLED_APPS = [
    'apps.articles.ArticlesConfig',

It should be: 'apps.articles.apps.ArticlesConfig',

Also your Contents of articles/apps.py should be only:

from django.apps import AppConfig

class ArticlesConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.articles'
bchen
  • 21
  • 3
  • That's a fair observation. A month later and this app has evolved considerably. Good eye. Thanks for taking the time on this. – Carewen Aug 21 '21 at 20:09
1

I work with a project structured very similar to what you're building.

It's a different structure to the default django setup or what many people use, but after 10 years of doing this, I now prefer this setup.

Project structure

I've got an app called accounts and the config for this is setup like this;

apps.accounts.__init__.py

default_app_config = 'apps.accounts.apps.AccountsConfig'

apps.accounts.apps.py

from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _


class AccountsConfig(AppConfig):
    """
    Accounts app, integrating with django-allauth
    """
    label = 'accounts'
    name = 'apps.accounts'
    verbose_name = _('Accounts')

    def ready(self):
        """
        App ready, hook up the signals
        """
        from . import signals    # NOQA

Then in settings we use the older style from before the introduction of the AppConfig classes;

INSTALLED_APPS = [
    'apps.accounts',
]

Setting paths for your settings file tends to come down to 2 useful paths;

PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.abspath(os.path.join(PROJECT_DIR, '../'))

Thats the path of the module where your settings are (PROJECT_DIR), and the directory above which tends to be the root of the project (BASE_DIR)

You can then use these, in things like the template setting;

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
            os.path.join(PROJECT_DIR, 'templates'),
        ],

EDIT (From the OP)

This didn't warrant an answer of its own, so I'm going to beg forgiveness rather than ask permission on editing/adding to the answer above)

The approach above was VERY helpful and gave me the momentum to cross the line. I've ended up with an interesting outcome that still warrants research/investigation/learning on my part, but it is progress in that the project structure I was going for is (mostly) as intended. I'm going to follow up with the authors on Two Scoops with an issue to highlight what happened in this and if they might provide a deeper clarify on how to deviate from the project structure.

When running following the edit to apps.articles.apps.py I got the following error:

ImportError: cannot import name 'signals' from 'apps.articles' (C:\Users\Carewen\Python\projects\LanesFlow\apps\articles\__init__.py).  

I commented out the def ready(self) function on the assumption that I didn't need to hook up the signals just yet.

This was progress as I could now run the server. In effect, this allowed me to maintain the existing INSTALLED_APPS declaration as you noted. Now came the interesting part. I could get this working if I moved the templates folder into the config directory. This isn't quite what I was intending. As this now means I've got config/templates/articles as the source for html files. HOWEVER, this is certainly an improvement since this is working...

I suspect that what this comes down to is how DIRS is set in base.py (settings.py) in my project setup. In looking at the traceback at first I could see a call to templates/templates which showed me the recommendation in Two Scoops wasn't quite right. In the end, I made a tweak to 'DIRS':

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / ''],  # Two scoops add. Was [] -- [BASE_DIR / 'templates']
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

For future readers, what I'm clarifying in the comment is that I started with the approach recommended in Two Scoops and then shifted to BASE_DIR / ''. While I got this working, this is where my inexperience with Python shows as I don't fully understand how this works.

And so in order, here are the remaining code snippets that provide the answer, building off the above, with the additional tweaks.

articles\urls.py:
from django.urls import path

from . import views

urlpatterns = [
    path('', views.home, name='articles-home'),
    path('about/', views.about, name='articles-about'),
]
articles/apps.py:
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _


# class ArticlesConfig(AppConfig):
#     default_auto_field = 'django.db.models.BigAutoField'
#     name = 'apps.articles'

class AccountsConfig(AppConfig):
    """
    Articles app, integrating with django-allauth
    """
    label = 'articles'
    name = 'apps.articles'
    verbose_name = _('Articles')
articles\views.py:
from django.shortcuts import render


def home(request):
    return render(request, template_name='templates/articles/home.html')


def about(request):
    return render(request, template_name='templates/articles/about.html')
articles/init.py:
default_app_config = 'apps.articles.apps.AccountsConfig'

Thanks again to the answer above. This gave me what I needed to get me over the the line! May this combined answer support future readers.

markwalker_
  • 12,078
  • 7
  • 62
  • 99
  • Thanks very much for your time (and sharing your experience of the approach). I'm reverting to the previous working version and I'm going to step through this until I've got a result. – Carewen Jul 09 '21 at 00:41
  • 1
    @Carewen Yeah the `ready()` in my example wasn't necessarily for your project as well, just an example of what you can do. If you've not got signals, you don't need that. – markwalker_ Jul 09 '21 at 01:36
  • 1
    @Carewen I've also added 2 useful paths to have in settings and an example of how you can use them in your `TEMPLATES` setting given that perhaps could be clearer to you. – markwalker_ Jul 09 '21 at 01:41
  • I tried those paths and I'm getting: TypeError: unsupported operand type(s) for /: 'str' and 'str'. It leads to a break in DATABASES. Changing BASE_DIR has cascading impacts through settings.py. I'm going to push the working solution to github, and see if I can circle back to refine to what you're suggesting. – Carewen Jul 09 '21 at 02:01