Is possible to customize the models package path in Django - django

The default folder structure of a project in Django is something like:
myproject/
- myapp/
- models.py
- myproject/
- settings.p
Would it be possible to change that structure?
I'd like to do something like:
myproject/
- myapp/
- infrastructure/
- models.py
- myproject/
- settings.py
or
myproject/
- myapp/
- infrastructure/
- models/
- __init__.py
- some_model.py
- myproject/
- settings.py
I know I can make a models package and split the models into different files. But I'm looking to change also the name/path of that package.

Yes and no. In that case, your app will be myapp.infrastructure and it will need to be added to INSTALLED_APPS in settings.
Having models.py file and models/ folder is mostly same for python. In this case, you will need to import different models in the __init__.py file inside the models/ folder.
The idea is that somehow your models and other codes should be imported somewhere in the project for django management command to see them (ex: for preparing database migrations - makemigrations command). So as long as makemigrations can see your model, you can place them anywhere. It is just a python's way, not directly related to django framework.
As for the name of basic things (models, views, apps, signals, admin and etc.) - I think the only one required to be in its name is models. As it is used directly by its name in ORM. Check this: Django Applications Documentation
So, it is possible thanks to python's syntax, but it is not recommended by django. Django's design practices were created to make it easier for everyone to write a readable app where you can easily understand where things supposed to be and work easily without learning internal rules of the project that someone else designed. It is very important for project where there are many developers and new members can join any time after the project's start. So, unless you are well aware what you are doing, you are free to develop any custom design you want - after all, all you need is for python to be able to import your functions and classes in other parts of the project.

Related

Do you think this is the right way to rename a django app?

I read different threads and tutorials on renaming and migrating a Django app (How to rename a Django app and migrate data from an app to the other for instance), I also tried to use the django package called django-rename-app but it didn't work at all.
I'm going to explain the way I renamed my app in my development environment and I would like to know if you expect troubles, as apparently everything is working just as it did before so I'm considering doing the same in production.
Here are the steps I took inside my root project directory and my virtualenv activated, using Django 3.2:
Create the destination app
django-admin startapp app2
Copy these files and directories
cp app1/*.py app2/
mkdir app2/templates
mkdir app2/static
cp app1/templates app2/templates
in this case I had a sub-folder named after the app so I renamed it like this
mv app2/templates/app1 app2/templates/app2
cp app1/static app2/static
Models.py
Change the related_name attributes of all ForeignKeys in my new models.py and update all their references inside my views, admin, form and templates
Make sure your apps won't imported by the same name
Look for the instanciation of an AppConfig class inside your app directory and if you find something like this:
from django.apps import AppConfig
class AnunciosConfig(AppConfig):
name = 'app1'
that's where you need to also change your app name from app1 to app2
Activate the new app
Add my app2 to the list of the enabled apps of settings.py
Delete __pycache__
Find and delete or empty all the __pycache__ directories in app2
Migrations
this will not work until all the overlapping bits are corrected, so this is how I tested everything was ok (it will not be aware of URLs problem at this stage)
python manage.py makemigrations
python manage.py migrate
Cloning data
Now I used the Django shell to migrate data from app1 to the app2
python manage.py shell
import app1
from app2.models import *
So I will have to use "app1.ModelName" to refer to the old app and simply "ModelName" to address the new app2 instances
Then I use the Models and QuerySet API to just create new objects in app2 with for loops for each app1 instances, with a corresponding app2 object that is saved (this needs be written individually and depends on each situation)
Deactivate the old app
Now I remove or comment out the old app1 reference in settings.py so that it is disabled
Rename the directory
At this point I needed to rename the app1/ directory to something unknown to Django
mv app1 app1_
Deal with the new errors
It seems some more errors appeared here with URLS and Views, so I had to just update app1 references to app2 until they disappeared
Delete Database table
Enter MySQL (in my case), and disable FOREIGN_KEY_CHECKS
USE yourowndjangodatabase;
SHOW TABLES;
SET FOREIGN_KEY_CHECKS=0;
so now I can delete them like this
DROP TABLE app1__yourfirstmodel;
DROP TABLE app1__yoursecondmodel;
and so on until none is left, then set the FOREIGN_KEY_CHECKS back to 1 so that it is enabled
SET FOREIGN_KEY_CHECKS=1;
Like I saw here Can't drop table: A foreign key constraint fails
CONCLUSION
And basically that was it, it worked perfectly for my development environment. I have a perfect copy of my previous app1 and web application that is just using app2 now.
I improvised so I'm not sure this is the right procedure but it worked so I would like to ask if you see any problem or suspect there will be issues in production.

How to make models of app available to other apps in a django project?

It seems like to be best practice (or at least one common way) to create a Django 3 based fullstack project which uses project specific Django apps (instead of standalone Django apps) with a structure like this (refer to e.g. here):
fullstack_project/
frontend/
... # frontend stuff goes into here
backend/ # Django project name
apps/
app1/
apps.py
logic.py
models.py
...
app2/
apps.py
logic.py
models.py
...
wsgi.py
...
manage.py
The apps (here: app1) are integrated in the most basic form (no ruls, views, etc.) via fullstack_project/backend/apps/app1/apps.py
class App1Config(AppConfig):
name = 'backend.apps.app1'
and fullstack_project/backend/settings.py
INSTALLED_APPS = [
...
'backend.apps.app1.apps.App1Config',
'backend.apps.app2.apps.App2Config',
]
Using logic of one app (e.g. app2/logic.py) in another app (e.g. app1/logic.py) works just fine (e.g. via from backend.apps.app2.logic import ... in app1/logic.py).
However if I try to access the models of app1 in the logic of app2 I get django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.. I could implement a database wrapper for an app to allow cross-app database access. However ass the apps are cross-project anyway I'd like to avoid that and allow access more easily. How can I fix this issue?
It is possible that there will be some code in __init__.py which is trying to access DB even before the app is registered and ready. so if you have any code in __init__.py comment it or make sure it will access database once the app is ready.

Migrate models in package hierachy without South

I'm on Django 1.8. Rather than all my models being in a single models.py file, I have them in a package that looks like this:
models/
- __init__.py
- Album.py
- Artist.py
But Django's makemigrations command appears to work only when an app has a single models.py file. Running django-admin help makemigrations does not bring up any hints on additional options that I can pass to the command to achieve what I want. Also, all other questions and answers I've seen here on SO involve South, which is incompatible with Django 1.8.
So what's the workaround?
Horizontally splitting django models across multiple files is a valid approach. A good explanation can be found here.
Django looks for your models in the models module which can be a file or a valid python package.
If you have a valid python package( a directory with a __init__.py file) it will try to discover the models based on the contents of __init__.py.
Make sure that your __init__.py file looks like this:
from Album import Album
from Artist import Artist
Then django should discover your models correctly.
In your models.py
add.
# myproject/app/models.py:
from models/Album.py import *
from models/Artist.py import *
Note about django-south: From django-1.7 migrations has been started supported. Please check Migrations.

PyCharm cannot resolve reference in __init__.py with Django project apps

I am at my wits end with this issue, and would love some help resolving this.
I have a Django project with a bunch of sub apps as such:
my_project/
manage.py
my_project/
settings.py
urls.py
wsgi.py
app_root/
__init__.py
app1/
__init__.py
models.py
views.py
urls.py
templates/
[various templates].html
app2/
__init__.py
models.py
[etc]
app3/
[etc]
in my django settings.py i have installed apps as such:
app_root.app1,
app_root.app2,
In PyCharm, I've tried various things but essentially have Content Root as the top "my_project/" and app_root, app1, app2, etc as Source Roots. I've tried just having app_root as the only Source Root, and I've tried having only app1, app2, etc only as Source Roots, but nothing makes any difference.
Everything functions fine. app runs and everything. However, PyCharm has an inability to resolve my apps.
However, if i try this:
import app_root
...
def some_function(self):
app_root.app1.models.My_Model.objects.all()
it will highlight app1 with the error "Cannot find reference 'app1' in '__init__.py'"
This also means it can't do autocomplete anywhere in the path while doing app_root.app1. - it has no idea about models, views, etc. despite having an (empty) __init__.py in every directory.
I also cannot use any refactoring because it always says "Function is not under the source root"
I've spent countless hours trying to get PyCharm to behave but simply cannot find a way to do it. Is there any way this can be done so PyCharm will autocomplete my apps and not keep giving inspection warnings?
I had some similar issues. My solution; within the PyCharm preferences I added a path to app_root in my active Python Interpreter.
After an exchange with the PyCharm folks, here is what I learned:
Django imports all apps in INSTALLED_APPS variable and their models using __import__ for its own purposes.
In your case, it runs
__import__("app_root.app1")
__import__("app_root.app1.models")
After that, you call import app_root and obtain module app_root with app_root.app1 and app_root.app1.models already imported by internal Django code
Fact that Django imports apps and models is Django internals, it is undocumented and may be changed in future releases. We believe you should not rely on it in your production code, nor PyCharm should.
Here is example in bare python (no django):
__import__("encodings.ascii")
import encodings
print (encodings.ascii.Codec) # this code works, but PyCharm marks "ascii" as "unknown module"
So basically, it's not supposed to work as import app_root, but Django funkiness is masking that.

django app file structure

Below is a common app file structure:
app/
views.py
forms.py
urls.py
tests.py
I found it's getting hard to maintain the code once the code base is getting big. I am thinking to organize the code based on individual web page, so each web page will have its own view.py, form.py and test.py.
app/
views/
page1_view.py
page2_view.py
forms/
page1_form.py
page2_form.py
tests/
page1_test.py
page2_test.py
Is there any big issue I will run into?
No. Django understands packages and files.
Example if you use
app/
views/
page1_view.py
page2_view.py
__init__.py
you can reference the views as app.views.page1_views without any issues.
The short answer is no. In fact, I've often split views, tasks, and tests as you are doing.