I'm trying to get the html generated by web2py from a different module, so I can turn it into a pdf file:
html = response.render('path/myview.html', context_dict)
I've included this at the top of my file:
sys.path.append('/home/www-data/web2py')
from gluon import *
However, I'm still getting:
NameError: global name 'response' is not defined
Maybe there's an easier way to do this I haven't thought of?
You can execute web2py templates using the gluon.template.render function, but if the template was designed to be executed within a web2py application, it may include references to some web2py environment objects (e.g., HTML helper objects, the T translation object, the request and response objects, etc.), which would not be available when calling the render function outside of a web2py HTTP request.
If proper execution of the template requires a web2py environment, you can build one using the gluon.shell.env function:
import os
import gluon.shell
context_dict = {...} # The context to be passed to the template.
app = 'myapp'
app_path = os.path.join(os.path.dirname(os.path.abspath(gluon.shell.__file__)),
'..', '..', 'applications', app)
w2p_env = gluon.shell.env(app, dir=app_path, import_models=True)
context_dict.update(**w2p_env) # Update context with web2py environment objects.
response = w2p_env['response']
html = response.render(os.path.join(app_path, 'views', 'mycontroller', 'myview.html'),
context=context_dict)
gluon.shell.env returns a dictionary containing all the web2py execution environment objects. If import_models=True, as in the code above, it will also execute the application's models (and therefore, any objects defined in the models will also be included in the returned dictionary).
By updating context_dict with the full web2py execution environment, we ensure that any web2py objects referenced in the template (and any templates it extends or includes) will be available.
The w2p_env environment also includes a response object, so we can use the response.render method to render the template. Alternatively, as noted above, we could have used the gluon.template.render function.
An alternative approach is to simply run your Python script in the environment of your web2py application:
python web2py.py -S myapp -M -R '/path/to/myscript.py'
With this approach, myscript.py can make reference to any web2py environment objects that would be available from within the application code (no need to import anything or explicitly build a web2py environment).
Also, note that you can exclude the -M if you don't need the application's models to be run.
I was able to get it to work by adding my python script into the web2py "modules" folder. After doing this I had to modify my import statements that referenced the other files in the "modules" directory:
from applications.myapp.modules.mymodule import mystuff
from gluon import *
html = current.response.render('myview.html', context_dict)
Then to run it from the command line I used:
python /home/www-data/web2py/web2py.py -S myapp -R 'path/to/myscript.py' As described by Anthony
I plan to call this module from a cron job once it's completed. I assume the web2py cron will use the web2py environment, which won't require the same execution script I've used to test.
Related
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 ?
I have JSON data coming through various external API's into my Django project. I have two apps, one called 'products' and other 'extract'. Through product app, have created the database and rendered the views required. Objective of extract app is to parse the JSON data and create/update the fields in the Django database. I have this code saved in extract/views.py as shown below -
import json
import urllib2
from products.models import Product
url = " .........."
.............
for i in data[results]:
Product.objects.get_or_create (...........)
The issue I am having is this code is working well in python shell. It is extracting all the JSON data and updating the database. But, when I run the script on the command line outside of Python shell, it is giving error - 'Import error: No module named products.models'.
Django requires you to run python manage.py shell in order for the product app's models and Django internals to be initialized.
Check out this blog post by Stavros Korokithakis to convert your code into a standalone script.
I believe the error is with the directory set up. Make sure all your migrations are done as well as the initial folder has the paths to your apps. Possible you need to include the paths to each app in the other's settings.
I'm building an application in Pyramid and utilizing Jinja2 templates and traversal routing. In order to wire my view-callables with the templates I am using, I want to be able to reference my templates using the webapp:templates prefix. As an example:
#view_config(name='about-us', renderer='webapp:templates/pages/about-us.html', context=Root)
def static_pages(context, request):
... //more code
This decouples where the templates live from whats using them. In order to make the above functional, though, I had to put this inside the __init__.py in my webapp root folder:
config.add_static_view(name='templates', path='webapp:templates', cache_max_age=3600)
The add_static_view() causes the webapp/templates folder to be referenced as webapp:template in other configurations. However, it also makes it viewable from a url such as http://0.0.0.0:6543/templates/<some template file>. Is there a way to achieve the former goal without allowing the latter visibility as a static page?
add_static_view() is not supposed to cause the webapp/templates folder to be referenced as webapp:template in other configurations, if it does that it's just due to a weird side-effect.
The package:path syntax works because Pyramid uses pkg_resources API to resolve the paths. Here are some details.
This means that, in your example, webapp should be a python package located somewhere your app can find it.
I have a django application which uses a library.py file :
library.py
a=5
View 1:
import library
print library.a # prints 5
On a certain event after updating the library.py file and change a=10
View 2:
import library
reload(library) # Should refresh library for the entire project
Then I again execute view 1:
It still prints 5, why ?
Can someone explain, since it should have printed 10, since I updated the value and reloaded the library that was in a shared memory for a python manage.py runserver
Should refresh library for the entire project
No, it shouldn't. It will reload it in current module only. And don't use it for reloading the code of your project, use the proper way depending on your deployment scheme.
I have a couple of functions that I wrote that I need to use in my django app. Where would I put the file with them and how would I make them callable within my views?
I usually put such app specific helper function in file utils.py and use someting like this
from myapp.utils import my_cool_func
def view_coolness(request):
data = my_cool_func(request)
return render_to_response("xxx.html")
but it depends what you helper does, may be they modify request , the could be part of middleware, so you need to tell what exactly those helper functions do
create a reusable app that include your generic functions so you can share between projects.
use for example a git repo to store this app and manage deployments and evolution (submodule)
use a public git repo so you can share with the community :)
If they are related to a specific app, I usually just put them in the related app folder and name the file, 'functions.py'.
If they're not specific to an app, I make a commons app for components (tests, models, functions, etc) that are shared across apps.
I am using new python file service.py in app folder. The file contains mostly helper queries for specific app. Also I used to create a folder inside Django application that contains global helper functions and constants.