integrating sqlalchemy in webapp2 - sqlalchemy

I'm developing an app using webapp2 outside google appengine, I want to integrate sqlalchemy and found an example on http://webpy.org/cookbook/sqlalchemy so I used the models.py from the example and in my main.py I defined my BaseHandler like this:
class BaseHandler(webapp2.RequestHandler):
#property
def db(self):
"""
returns ORM handle to the database
Usage: admin = self.db.query(User).filter_by(name='admin').first()
"""
return scoped_session(sessionmaker(bind=engine))
My question is that, is this the recommended way to do this? Is their other way?

Related

override SQlAlchemy context manager for all tests

I use FastApi with SqlAlchemy as context manager
#contextmanager
def get_session(): # need to patch all over tests files
session_ = SessionLocal()
try:
yield session_
except Exception as e:
session_.rollback()
router.py
#router.get('/get_users'):
def get_users(): # No dependencies
with get_session() as session:
users = session.query(Users).all()
return users
I need to override get_session during all my tests (I use pytest)
I could do it with #patch and patch all test. But it's not most effective way because i need to use decorator to patch in each test file, correct specify full path.
I wonder is there quick way to do it in one place or maybe use fixture?
You could try the approach in this answer to a similar question: define a pytest fixture with arguments scope="session", autouse=True, patching the context manager. If you need, you can also provide a new callable:
from contextlib import contextmanager
from unittest.mock import patch
import pytest
#pytest.fixture(scope="session", autouse=True, new_callable=fake_session)
def default_session_fixture():
with patch("your_filename.get_session"):
yield
#contextmanager
def fake_session():
yield ... # place your replacement session here
As a side note, I would highly recommend using FastAPI's dependency injection for handling your SQLAlchemy session, especially since it has built in support for exactly this kind of patching.

How to get Alembic to recognise SQLModel database model?

Using SQLModel how to get alembic to recognise the below model?
from sqlmodel import Field, SQLModel
class Hero(SQLModel, table=True):
id: int = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
One approach I've been looking at is to import the SQLalchemy model for Alembic but looking through the source code I can't find how to do that.
How to make Alembic work with SQLModel models?
There should be info about that in Advanced user guide soon with better explanation than mine but here is how I made Alimbic migrations work.
First of all run alembic init migrations in your console to generate migrations folder. Inside migrations folder should be empty versions subfolder,env.py file, script.py.mako file.
In script.py.mako file we should add line import sqlmodel somewhere around these two lines
#script.py.mako
from alembic import op
import sqlalchemy as sa
import sqlmodel # added
Then we should edit env.py file
#env.py
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from app.models import * # necessarily to import something from file where your models are stored
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None
# comment line above and instead of that write
target_metadata = SQLModel.metadata
While writing came up with an idea that you forgot to import something from your models.py (or anywhere else your models are stored). And that was the main problem
Also, an important note would be saving changes in your models by pressing ctrl(CMD) + S - there are some issues with that.
Finally,running
alembic revision --autogenerate -m "your message"
should generate a new .py file in versions folder with your changes.
And
alembic upgrade head
Applies your changes to DB.
Here you can find a fastapi-alembic and SQLmodel integration with async PostgreSQL database
https://github.com/jonra1993/fastapi-sqlmodel-alembic

Django MySQL Error (1146, "Table 'db_name.django_content_type' doesn't exist")

I am getting the error
django.db.utils.ProgrammingError: (1146, "Table 'db_name.django_content_type' doesn't exist")
when trying to do the initial migration for a django project with a new database that I'm deploying on the production server for the first time.
I suspected the problem might be because one of the the apps had a directory full of old migrations from a SQLite3 development environment; I cleared those out but it didn't help. I also searched and found references to people having the problem with multiple databases, but I only have one.
Django version is 1.11.6 on python 3.5.4, mysqlclient 1.3.12
Some considerations:
Are you calling ContentType.objects manager anywhere in your code that may be called before the db has been built?
I am currently facing this issue and need a way to check the db table has been built before I can look up any ContentTypes
I ended up creating a method to check the tables to see if it had been created, not sure if it will also help you:
def get_content_type(cls):
from django.contrib.contenttypes.models import ContentType
from django.db import connection
if 'django_content_type' in connection.introspection.table_names():
return ContentType.objects.get_for_model(cls)
else:
return None
As for migrations, my understanding is that they should always belong in your version control repo, however you can squash, or edit as required, or even rebuild them, this linked helps me with some migrations problems:
Reset Migrations
Answering my own question:
UMDA's comment was right. I have some initialization code for the django-import-export module that looks at content_types, and evidently I have never deployed the app from scratch in a new environment since I wrote it.
Lessons learned / solution:
will wrap the offending code in an exception block, since I should
only have this exception once when deploying in a new environment
test clean deployments in a new environment more regularly.
(edit to add) consider whether your migrationsdirectories belong in .gitignore. For my purposes they do.
(Relatively new to stackoverflow etiquette - how do I credit UMDA's comment for putting me on the right track?)
I had the same issue when trying to create a generic ModelView (where the model name would be passed as a variable in urls.py). I was handling this in a kind of silly way:
Bad idea: a function that returns a generic class-based view
views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.views.generic.edit import DeleteView
def get_generic_delete_view(model_name):
model_type = ContentType.objects.get(app_label='myapp', model=model_name)
class _GenericDelete(LoginRequiredMixin, DeleteView):
model = model_type.model_class()
template_name = "confirm_delete.html"
return _GenericDelete.as_view()
urls.py
from django.urls import path, include
from my_app import views
urlpatterns = [
path("mymodels/<name>/delete/", views.get_generic_delete_view("MyModel"),
]
Anyway. Let's not dwell in the past.
This was fixable by properly switching to a class-based view, instead of whatever infernal hybrid is outlined above, since (according to this SO post) a class-based view isn't instantiated until request-time.
Better idea: actual generic class-based view
views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.views.generic.edit import DeleteView
class GenericDelete(LoginRequiredMixin, DeleteView):
template_name = "confirm_delete.html"
def __init__(self, **kwargs):
model = kwargs.pop("model")
model_type = ContentType.objects.get(app_label='myapp', model=model)
self.model = model_type.model_class()
super().__init__()
urls.py
from django.urls import path, include
from my_app import views
urlpatterns = [
path("mymodels/<name>/delete/", views.GenericDelete.as_view(model="MyModel"),
]
May you make new and better mistakes.
Chipping in because maybe this option will appeal better in some scenarios.
Most of the project's imports usually cascade down from your urls.py. What I usually do, is wrap the urls.py imports in a try/except statement and only create the routes if all imports were successful.
What this accomplishes is to create your project's / app's routes only if the modules were imported. If there is an error because the tables do not yet exist, it will be ignored, and the migrations will be done. In the next run, hopefully, you will have no errors in your imports and everything will run smoothly. But if you do, it's easy to spot because you won't have any URLs. Also, I usually add an error log to guide me through the issue in those cases.
A simplified version would look like this:
# Workaround to avoid programming errors on greenfield migrations
register_routes = True
try:
from myapp.views import CoolViewSet
# More imports...
except Exception as e:
register_routes = False
logger.error("Avoiding creation of routes. Error on import: {}".format(e))
if register_routes:
# Add yout url paterns here
Now, maybe you can combine Omar's answer for a more sensible, less catch-all solution.

"Initializing geometry from JSON input requires GDAL" Error

I'm doing a Django project and I want to save polygons that represent areas of interest in a map. I am trying to use django-leaflet and django-geojson. The model for the shapes is:
#models.py
...
from django.contrib.gis.db import models as gismodels
...
class MushroomShape(gismodels.Model):
name = models.CharField(max_length=256)
geom = gismodels.PolygonField()
objects = gismodels.GeoManager()
def __unicode__(self):
return self.name
def __str__(self):
return self.name
I'm trying to create the polygon shapes in the admin, using a leaflet widget, to be added to the Database:
#admin.py
...
from leaflet.admin import LeafletGeoAdmin
from .models import MushroomShape
...
admin.site.register(MushroomShape, LeafletGeoAdmin)
Running the server in my computer, when I draw a polygon in the admin form and try to submit it:
The client side reports "Invalid geometry value." and the server side reports:
Error creating geometry from value
'{"type":"Polygon","coordinates":[[[-87.58575439453125,41.83375828633243],[-87.58575439453125,42.002366213375524],[-86.74942016601562,42.002366213375524],[-86.74942016601562,41.83375828633243],[-87.58575439453125,41.83375828633243]]]}'
(Initializing geometry from JSON input requires GDAL.)
A little push to help understand where I have to look, to solve this error, would be really awesome.
Sorry if this is bad etiquette (posting an answer to my question instead of deleting), but I've found my answer in the official Django page for geo libraries:
https://docs.djangoproject.com/el/1.10/ref/contrib/gis/install/geolibs/
I didn't know GDAL is necessary for some geojson features that I tried to use to work. I've followed their instructions and installed it with
sudo apt-get install binutils libproj-dev gdal-bin
and my error is gone.

sqlalchemy : how to define model objects that are excluded from schema creation?

In a flask application I create my schema with
db.create_all()
some of the tables should not be created, they are managed externally to the application, how can I tell SqlAlchemy to not create them ?
The command db.create_all() will only create models for those classes that you import into the script you use to run that command. For example, lets say that my models.py file has two classes:
class User(db.Model):
and
class Address(db.Model):
In the script where I run db.create_all, if my file looks like:
from models import User
db.create_all()
my app will only create the User model. Conversely, if my file looks like:
from models import User, Address
db.create_all()
both the User and Address models will be created.