The drawbacks of using an apps sub-directory

For reasons similar to those of the author I have followed an approach similar to the one described in this blog post in which the developer deploys all of the Django applications from an “apps” subdirectory of the Django root directory. Though the post is from 2018 is it not the first nor only I have seen presenting this approach (see this one from 2024, for example.

On the other hand, when asked why their PyCharm Django project viewer did not work on my directory structure a developer or support specialist from Jetbrains PyCharm explained that mine is a non-standard project directory. And @KenWhitesell responded to another user

What are the issues of which I should be aware? I have been using this successfully for the last 6 months if not more. My development experience has not been without issues. However, there are any number of explanations for those issues - or some of these related to my “non-standard” project directory structure?

  1. Make migrations and migrate haven’t always worked smoothly though adding ‘–skip-checks’ seems to have addressed most of those.
  2. loading fixtures has been problematic and is currently not working (getting `DeserializationError: Group matching query does not exist for pk of custom user based on AbstractUser)

This is my first Django project and first experience using Django but not my first rodeo; I have a csci degree and about 8 years each developing Error: ASP.net | The ASP.NET Site web applications and then Java web applications. Undoubtedly some of my issues have been due to the learning curve. Other problems might be due to one or more of the third-party Django apps like Polymorphic and/or our non-trivial models or the fact that we are using SQL Server as our database with the mssql-django package.

Justin

Touched on briefly at Good idea to have a folder to keep apps together? - #3 by guzmanojero

Now for some context: In most cases here, people are raising this issue from the position that they think there’s some efficiency or esthetic improvement to be had by organizing their projects in a non-default structure, without having the knowledge necessary to address the side effects caused by doing so. I maintain that it’s a waste of time and energy to worry about the layout of the project until such time as you know what you’re doing - and why.

If you’ve got the knowledge to handle the issues, and you want to structure your project differently - great. But then you’re not likely going to be posting in a forum that you don’t understand why some import isn’t working as a result.

<opinion>
I’m still going to disagree with the perceived value of doing so - see the complete thread at Is the structure of the project correct?, but I will acknowledge that it is an architectural decision for which viable alternatives exist.

If I’ve learned anything from 40 years of working with software development frameworks, it’s “Don’t fight the framework”. I have found, without exception, that things work more smoothly by adopting the conventions and mindset on which a framework is built instead of trying to make the framework work the way that I may prefer.

Again - at its core it’s an architectural decision, and I can respect people making different choices, even though I don’t agree. Ten years of working almost exclusively with Django, and I’ve yet to encounter a situation where I’ve had any desire - or have seen any benefit - to implementing anything other than the standard project layout.
</opinion>

3 Likes

It seems that I have already defeated the most vicious of beasts woken by my flagrant defiance (though never intentional!) of the framework rules. At this stage of development it might be more difficult to undo my rule violations than just staying the course. Here, for the record, is a directory tree of my project layout.

└── myapp
    ├── data
    │   ├── db
    │   │   └── csv_loader_data.csv
    │   └── fixtures
    │       └── helloworld.json.gz
    ├── myapp
    │   ├── apps
    │   │   ├── helloworld
    │   │   │   ├── admin
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── model1.py
    │   │   │   │   ├── model2.py
    │   │   │   │   └── model3.py
    │   │   │   ├── forms
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── model1.py
    │   │   │   │   ├── common.py
    │   │   │   │   ├── model2.py
    │   │   │   │   └── model3.py
    │   │   │   ├── management
    │   │   │   │   ├── commands
    │   │   │   │   │   ├── __init__.py
    │   │   │   │   │   └── db_load_csv.py
    │   │   │   │   └── __init__.py
    │   │   │   ├── migrations
    │   │   │   │   ├── 0001_initial.py
    │   │   │   │   └── __init__.py
    │   │   │   ├── models
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── model1.py
    │   │   │   │   ├── model2.py
    │   │   │   │   └── model3.py
    │   │   │   ├── tests
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── test_model1_view.py
    │   │   │   │   └── test_model2_view.py
    │   │   │   ├── views
    │   │   │   │   ├── __init__.py
    │   │   │   │   ├── model1.py
    │   │   │   │   ├── common.py
    │   │   │   │   ├── model2.py
    │   │   │   │   └── model3.py
    │   │   │   ├── __init__.py
    │   │   │   ├── apps.py
    │   │   │   └── urls.py
    │   │   └── __init__.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── settings
    │   │   ├── __init__.py
    │   │   ├── base.py
    │   │   ├── local.py
    │   │   ├── production.py
    │   │   └── test.py
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── asgi.py
    │   ├── forms.py
    │   ├── managers.py
    │   ├── models.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── doc
    │   ├── images
    │   │   ├── myapp-models.svg
    │   │   └── pycharm_interpreter_setup.png
    │   ├── .order
    │   ├── README.md
    │   ├── azure-appsvc.md
    │   ├── databases.md
    │   ├── dev-tools.md
    │   ├── django-development.md
    │   ├── django-orm.md
    │   ├── django-project-files.md
    │   ├── django-settings.md
    │   ├── django-staticfiles.md
    │   ├── starthere.md
    │   └── technology.md
    ├── requirements
    │   ├── base.in
    │   ├── base.txt
    │   ├── development.in
    │   ├── development.txt
    │   ├── production.in
    │   ├── production.txt
    │   ├── test.in
    │   └── test.txt
    ├── run
    │   └── .gitignore
    ├── static
    │   ├── admin
    │   │   └── js
    │   │       └── admin.js
    │   ├── css
    │   │   ├── img
    │   │   │   ├── icon-no.svg
    │   │   │   └── tooltag-arrowright.svg
    │   │   ├── vendor
    │   │   │   ├── select2-4.0.13.min.css
    │   │   │   └── select2-bootstrap-5-theme.min.css
    │   │   ├── admin.css
    │   │   ├── admin_base.css
    │   │   ├── admin_extra.css
    │   │   ├── base.css
    │   │   ├── bootstrap-custom.min.css
    │   │   ├── bootstrap-custom.min.css.map
    │   │   └── login.css
    │   ├── images
    │   │   ├── 404.png
    │   │   ├── bootstrap-themeswitcher.svg
    │   │   ├── favicon.ico
    │   │   └── myapplogo.png
    │   └── js
    │       ├── vendor
    │       │   ├── bootstrap.bundle.min.js
    │       │   └── bootstrap.bundle.min.js.map
    │       ├── base.js
    │       └── color-modes.js
    ├── templates
    │   ├── admin
    │   │   ├── 404.html
    │   │   ├── 500.html
    │   │   ├── app_index.html
    │   │   ├── base_site.html
    │   │   ├── color_theme_toggle.html
    │   │   └── login.html
    │   ├── myapp
    │   │   ├── model1
    │   │   │   ├── model1_confirm_delete.html
    │   │   │   ├── model1_detail.html
    │   │   │   ├── model1_form.html
    │   │   │   ├── model1_list.html
    │   │   │   └── model1_update_form.html
    │   │   ├── model2_detail.html
    │   │   ├── model2_list.html
    │   │   ├── index.html
    │   │   └── model_update_form.html
    │   ├── pages
    │   │   ├── about.html
    │   │   └── home.html
    │   ├── registration
    │   │   ├── logged_out.html
    │   │   └── login.html
    │   ├── shared
    │   │   ├── color_theme_toggle.html
    │   │   ├── django_tables2_filter_template.html
    │   │   ├── django_tables2_template.html
    │   │   └── page_nav.html
    │   ├── 403.html
    │   ├── 403_csrf.html
    │   ├── 404.html
    │   ├── 500.html
    │   ├── README.md
    │   └── base.html
    ├── tests
    │   └── README.md
    ├── .artifactignore
    ├── .editorconfig
    ├── .env.dist
    ├── .env.sqlite
    ├── .gitignore
    ├── .mailmap
    ├── .pre-commit-config.yaml
    ├── README.md
    ├── azure-pipeline.yml
    ├── manage.py
    ├── mypy.ini
    └── ruff.toml

I overcame the last hurdle (loading fixtures) which had presented itself (though not for the first time and probably not for the last) probably in no small part due to our heavy use of Django Polymorphic. The only adverse side effect I see over which I do not have much control - but fortunately for which I do not have much interest either - is the PyCharm Django structure view which fails to capture my project. That is not terribly surprising.

Thanks for sharing your opinion; I was looking for this. But in giving yours you open the door for my own in return. :slight_smile:

I can very much appreciate the adage of “don’t fight the framework”. I’ve read a good number of answers you personally (assuming that you are only one person!) have provided on this forum alone and I trust your experience. As one developer to another, however, the problem with this adage is that it pre-supposes a level of knowledge that most beginners in any framework just don’t have; you can’t fight [the rules of] a framework that only lurk in the shadows of your awareness. I have worked in a least different frameworks in the last 20 years with an initial emphasis on ASP.NET following by Perl (arguably PSP is not a framework but a technology), Spring and various flavors of older Java-based web frameworks. All of these frameworks - not to mention the programming languages on which they are based - seem to have many unwritten opinions and understandings that often you only learn after many mistakes. The problem is that all of them - including Django’s own documentation - present a mixture of examples and tutorials with the frequent admonition “Don’t do this in a production environment!” (why would you ever write an example that you don’t expect people to use?!?) Framework rules seem to have a lot in common with rules of etiquette, without explicit training the only time you realize you broke one is when things go wrong.

Regards,
Justin

Another strike against attempting to decipher the hidden rules of the Django file-system layout: it is often difficult to see the root cause of issues that present themselves when one is trying to use the system “as intended”. A difference such as a file-system layout seems to be an easy answer to the question: “why isn’t this working?” because the difference is easy to spot. That doesn’t always mean that the file-system layout is to blame, however. In this case, I was seeing a django.db.utils.IntegrityError similar to the one reported by this poster on StackOverflow. While I cannot vouch for the accuracy of the explanation provided in one of the answers, the explanation makes sense and ultimately the solution addressed my issue. More importantly for this context, I am very certain that this exception would have presented itself regardless of my chosen directory structure.