Django settings: sharing ORM with a standalone module - python

Introduction:
I am learning Django as I develop my first project using PyCharm with Python 3.8 and Postgresql.
I have a problem with the settings.
Environment description:
My aim is to have a Django project for a web site connected to a database (let's call it "web_db"), which is fed at regular intervals with data from an external source (another database, let's call it "source_db").
For this ETL process from one database to the other one, I am developing a Python module called "source2web". I read the content of source_db using psycopg2.
I am developing everything inside one PyCharm project.
Now, since I will be using Django ORM to access the content of web_db from the Django application server, I concluded I might as well use Django ORM in source2web to load my content into web_db.
This means I am trying to share some code between the Django project and source2web: the ORM models of Django.
source2web is simply launched as a python module ( python source2web.py).
Inside my PyCharm project, I have a src folder inside which are all my python modules, with a tree like this (folders are noted with parenthesis) :
(src)
|--(project_name)
|--(module1)
| |--source2web.py
|
|--(django)
|--(django_project)
|--(django_project)
|--(another_app)
The folders django_project/django_project and django_project/another_app have been created classicaly by Django with the commands django-admin startproject django_project and python3 manage.py startapp another_app.
The ORM models I want to share are the model classes inside the file django_project/another_app/models.py
The problem:
I tinkered to find a way to use the Django ORM models from source2web. I thought I had it : inside the code of source2web, I added these lines :
os.environ['DJANGO_SETTINGS_MODULE'] = 'project_name.django.django_project.django_project.settings'
import django
django.setup()
But then, I realize that the Django server and the standalone module source2web can both work, but not with the exact same content inside project_name.django.django_project.django_project.settings.py : the name of another_app in the list of INSTALLED_APPS must be different.
Django requires the app to be listed as just another_app
source2web requires the app to be listed as project_name.django.django_project.another_app
I am sure I could find a 'dirty way' to solve this, but I also think I am not the first one to face this problem (sharing Django ORM code between a Django project and a standalone program) and I hope there is cleaner way to manage this.
What I can think of is duplicating the module project_name.django.django_project.django_project.settings and then just change the settings call inside source2web ( os.environ['DJANGO_SETTINGS_MODULE'] = ... ) but I never like duplicating code and maybe there is a cleaner than that ?

I have found at least one decently clean workaround :
In the project_name.django.django_project.django_project.settings file, I have left the value of INSTALLED_APPS that work with Django, i.e., the list of default apps created with the Django Project plus another_app.
Then in the source2web module, I have changed my code for initializing Django ORM :
In module source2web, I added 2 lines :
import project_name.django.django_project.django_project.settings as djangosettings
djangosettings.INSTALLED_APPS = ['project_name.django.django_project.another_app',]
os.environ['DJANGO_SETTINGS_MODULE'] = 'project_name.django.django_project.django_project.settings'
import django`
django.setup()
This way, I leave untouched the settings for the Django server, and only override what's necessary in the source2web module (i.e. the value of INSTALLED_APPS, getting rid at the same time of all the default apps I don't need for my ETL process).
It works and seems robust enough.
Still, it brought me to question the whole project structure :
Because obviously, the whole problem is the result of the PyCharm project considering for source root src/, while the Django project considers for root project_name/django/django_project/ .
There should be a way to reconcile them, but how ?

Related

Django 3.2.7 - Detect Currently Running Command From Code

TL;DR:
Is it possible from within a django project to detect which command is currently running?
DETAILS:
We have a Django project (running 3.2.7) running which migrations are a little mixed up.
There is a config model which has been developed way after the initial migrations has been done. This config model is basically a Django ORM model mapped to a DB object and has some caching features (which I will not go into details).
The actual code running on servers never experience any problems, since config model is already provisioned on DB. But right now I'm trying to dockerize the whole project and I need to apply migrations from scratch. When I try to apply the migrations to a fresh db,
It reads settings.py, which contains ROOT_URLCONF variable, that is being directed to another .py file
this .py file contains urlpatterns list, which contains references to various django models
Some of these models includes reference to the config model that I've mentioned earlier. When they are imported, those models are trying to pull configs from this config model, which is not initialized yet on the DB, causing psycopg2.errors.UndefinedTable: relation "core_config" does not exist error.
Model is so deeply integrated in the system so I cannot modify the depending models to use anything non dependent on this model.
My main idea is to detect if makemigrations command is running and returning an empty urlpatterns object if this is the case.
Not sure if this is the best approach, but this is the best I've been able to come up with. If you have any other solution, I'm all ears.

Django single website structure

I want to start a new django project. This will just be a relatively simple website to test django itself. So the way all tutorials structure their project is this:
mysite
mysite
some_app
...
For me it's not very clear what does what. I don't really need any app right now. Could I implement my website without using any app? Should I use an app? What would it be called for a simple website?
Have you read Django doc already?
They have a nice tutorial on how to get started.
As for apps, in Django, you have a project containing one or more app. If you just want to try to build a simple website, your project called mysite will contain only one app.
I think you don't need more than the main mysite, BUT, at the django documents, have a session that says:
If your background is in plain old PHP (with no use of modern
frameworks), you’re probably used to putting code under the Web
server’s document root (in a place such as /var/www). With Django, you
don’t do that. It’s not a good idea to put any of this Python code
within your Web server’s document root, because it risks the
possibility that people may be able to view your code over the Web.
That’s not good for security.
Put your code in some directory outside of the document root, such as
/home/mycode.
This model may have been designed with focus on safety.
Some files in mysite could be used to control things in another apps, so i think that is a 'best pratice' to follow this model.
Font
if u aren't in need of an app you can avoid creating one. just give your view in views.py of your project set the url and u r all set to go.
here the first mysite folder is just an usual folder u can change the name in ur convenience next mysite is your project folder and recommended not to change it and next some_app is the application name(you can name your app anything relevant but in proper convention.)
and follow django documentation and djangogirls tutorial for better understanding.
https://docs.djangoproject.com/en/2.0/
https://tutorial.djangogirls.org/en/

Django: closest possible to the project name

I am creating a reusable app that needs to know the name of the project from which it is being used. Getting the AppConfig (or just the app name) of the app with the settings module in it would be already sufficient. Other approaches are welcome as well.
Why would I ever want this:
Different projects will have portions of their data shared in the same db schema. I still need to figure out to which project does some of it belong. Project names are considered to be unique at this point, default app names are the same as the corresponding project names (django-admin startproject behavior).
Based on the previous experience, expecting the users of my app to specify something extra in their project settings is just not reliable enough.
You can get the project package name like this
import os
os.environ.get('DJANGO_SETTINGS_MODULE').split('.')[0]

Does Django's singleton architecture make it unworkable as a standalone ORM in a library?

I like the Django ORM. It's simple, easy to use, and reasonably powerful.
I'm currently developing some internal sites for the VFX company I work for, for which I've used Django. In the meantime, we are developing other python applications and libraries to be used in various contexts in production. There's a number of places in which our core library needs to be interacting with some databases, and using an ORM like Django would really help things. I'm aware of other options like SqlAlchemy or PeeWee, but I'd like to see if Django will work since I use it on the websites and I like its API better.
Using Django as an ORM in a library is tricky (as I explored in a previous question), because Django expects to be used as a website with "apps". In a library, I might want to define any number of data models, which would exist in appropriate places in the library but not inside any Django app (as we're not using any other parts of the framework). So far so good.
I can create a baseclass for my models anywhere in the library as follows:
from django.db import models
from django.apps import apps
import django.conf
django.conf_settings.configure(
DATABASES = ...
)
apps.populate((__name__,))
class LibModel(models.Model):
class Meta:
abstract = True
app_label = __name__
Then anywhere in the library I can create my own models with this baseclass. Since I'm not relying on the "app" for the database names, I need to state them explicitly.
class SpecificModel(LibModel):
# fields go here
class Meta(LibModel.Meta):
db_table = "specific_model_table_name"
This gets around my concern of having to simulate the structure of an "app". The name property in the base class supplies Django with all it needs, and then Django quits whining about not finding an app. The other model files can live wherever they want.
However, there is a glaring use case where this all falls apart. Say that my Django web application wants to use some functionality from the company core python library, which now uses the Django ORM for various things. Since I make a call to django.conf.settings.configure in the library, Django is going to scream about defining the settings more than once when it tries to run the main application.
So basically, a library using the Django ORM is incompatible with Django. Wonderful.
Is there any way around this? I mean, it's a lovely ORM - is it really this impossible to use in a standalone modular way? Is the Django architecture utterly singleton in nature, making this impossible?
*Not a duplicate
I'm trying to have a company python library that uses Django as an ORM. Some of the things that could depend on it might be Django websites themselves. How do I get around Django's singleton insistence on only setting the settings config once? Or is it possible? None of these answers address this!
You can check if django has already been configured.
from django.apps import apps
from django.conf import settings
if not apps.ready:
settings.configure()
django.setup()
When starting Django application - core python library can be configured as separate app an be loaded on startup.
Also, check this answer on dynamic app loading at runtime.
A simple answer is how to initialize Django in a standalone application and do it compatible with Django applications.
import os
import django
if not 'DJANGO_SETTINGS_MODULE' in os.environ:
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysettings'
# # or without DJANGO_SETTINGS_MODULE directly
# from django.conf import settings
# settings.configure(DATABASES=... other...)
django.setup()
# this shouldn't be before DJANGO_SETTINGS_MODULE or settings.configure(...)
from myapp.models import MyModel
# this shouldn't be called before django.setup()
queryset = MyModel.objects.filter(...)
This is more compatible with Django then the answer by Oleg Russkin (where a risk of cyclic dependency at django.setup() is possible if the code is called inside inside a setup() initiated by another similar code or a normal Django project started by manage. It is similar to manage.py where django.setup() is also called internally by execute_from_command_line(sys.argv). The setup initializes all modules related to INSTALLED_APPS all urls modules and consequently all views etc. Many of them are lazy, but still. If any code called by setup() depends on this then neither the condition not apps.ready doesn't help. The setup() is not reentrant and the startup fails.)
Much more general answer
An important concept of Django is to support writing reusable parts of code ("applications" in Django terminology, that can be developed and tested independently. There can be also a tree of dependencies, but uncontrolled mutual dependencies should be avoided if possible) Reusable applications are expected that they can be easier combined to whole project ("project" in Django terminology is with all settings necessary to run it by Python.)
The only unavoidable and useful "singleton" in Django ORM are database connections django.db.connections and django.conf.settings especially INSTALLED_APPS. Only one connection should be used to the same database from the same process or thread.
Django is very configurable. An extreme example: It is possible to write a single file project where all code like settings, models, URL configs and views is defined in one file. That extreme that is probably useful only for some special tests or very short demos or as an exercise. It is even possible to define a project by one file with two "reusable" applications :-)
Django supports also "legacy databases" where the database structure is shared with existing non Django applications and models can be created by inspectdb command. Table names in such models are explicit and don't contain the app name. On the other hand the app name prefix is useful to prevent a possible conflict of the same table names from independent "applications". An important decision is if you can use it as a "legacy" database or a normal Django database.
You can decide between following two solutions or to combine them:
Use e.g. foo_models or bar.models and import all models from them e.g. to app.models and add only that "app" to INSTALLED_APPLICATIONS. This can be viable if it is only for one company and never otherwise and central name assigment is possible. (easiest but little naive)
Use some coarse separation of namespaces to several apps. You should probably use not more than one app with simple names without app name prefix.
Think ahead about migrations**. They will be probably very complicated and very soon impossible if you will create later more projects for the same database and different subsets of database tables without separating them to more apps and without app namespace.
There is really no "singleton" in Django ORM except of django.db.connections itself. If you use more databases you can direct some tables to a specific database by DATABASE_ROUTERS, even with two different models that use the same table name without a prefix.

link django with modules of other applications

I would like to link django with an existing python application, reusing the modules, the classes and some configurationfiles that were defined in this application. All modules reside in the ./bin directory of this application. In fact the application is a forecasting system, and django will be used as the frontend to visualise the results.
The current directory structure looks like this.
./application/bin/module1.py
/module2.py
/config.txt
/datadir/...
/webresults/run1/myfig.png
/run2/myfig.png
/...
./django/manage.py
/project1/urls.py
/settings.py
/wsgi.py
/app1/views.py
/models.py
/forms.py
/...
/templates/base.html
/showResults.html
/...
An apacheserver is set up to serve static pages and images from the directory ./application/webresults/ and django pages from /django/project1/wsgi.py.
For the moment I have copied all relevant modules from /application/bin/ to django/app1/ so I can reuse them eg. in views.py and models.py. With respect to maintenance of the system not an optimal solution.
So I am looking for a more elegant solution to solve this. Some of the things we would like to achieve:
moving around the backendapplication is harder than moving around django instance, so preferable django is relocated (it was not coded in the most efficient way :-))
reuse classes of application in django
reuse models of django in the application
reuse SQL_objectmapping in the application
use one configfile for settings relevant to both django and the application
The solution we are thinking of, would be to merge all djangocode into /application/bin/ and remap the djangopath in apacheconfiguration
./application/bin/manage.py
/module1.py
/module2.py
/project1/urls.py
/settings.py
/wsgi.py
/app1/views.py
/models.py
/forms.py
/...
/templates/base.html
/showResults.html
/...
Are there any recipes on how this could be handled? Any advice appreciated.
There's nothing wrong with tossing your Django project within another module. So long as your PYTHONPATH's are setup correctly, and your WSGI is configured right, everything should work normally.
My only comment would be that, tautologically, "separate things should be separate."
When I have similar problems, I usually create a simple UNIX socket server (using Tornado), that Django can communicate with for API calls, etc.
If I need to expose the functionality of the socket server, I'll throw in a cheap WSGI server (again, with Tornado) that serves up requests, and stick it under a different subdomain within my hosting configuration.
I think my original question was not very clear. Since then I stumbled upon some other answers and pages that helped me , mainly on how to reuse django functionality in external modules and applications.
http://www.b-list.org/weblog/2007/sep/22/standalone-django-scripts/
why 'list index out of range' in my django code;
https://docs.djangoproject.com/en/dev/ref/models/options/#app-label
With this information I managed to access my models (in apps-subfolder) from the pythonscripts on the root of the application (eg. module1.py).
In short the code to access app1.models.Mymodel from module1.py looks now like this :
os.environ['DJANGO_SETTINGS_MODULE'] = 'project1.settings'
from django.core.management import setup_environ
from project1 import settings
setup_environ(settings)
import app1.models
class MyChildmodel(app1.models.Mymodel):
class Meta:
app_label = 'app1'
def myfunction():
....

Categories