If I have a sqlalchemy query object
query = db.session(Category)
and Im trying to filter the query further as below
query = query.filter(Category.tree_id.asc())
but instead of Category, I want it to dynamically retrieve the model from the query to use in the filter.
query = query.filter(query.model.tree_id.asc()) #the goal im trying to achieve.
My main goal is to extend wtforms_sqlalchemy.fields.QuerySelectField where I am able to filter the sqlalchemy_mptt model further.
class TreeQuerySelectField(QuerySelectField):
...
def _get_object_list(self):
if self._object_list is None:
query = self.query if self.query is not None else self.query_factory()
if hasattr(self.query.model, 'tree_id'):
query = query.order_by(self.query.model.tree_id, self.query.model.left)
# self.query.model should be the current query model
get_pk = self.get_pk
self._object_list = list((str(get_pk(obj)), obj) for obj in query)
return self._object_list
def iter_choices(self):
if self.allow_blank:
yield ("__None", self.blank_text, self.data is None)
for pk, obj in self._get_object_list():
yield (pk, self.get_label(obj), obj == self.data)
Related
I am new to ORM's and trying to query a table with a timestamp column. My results however are empty and understandably so since I am querying a timestamp field with a date. I read and found out I can use 'sqlalchemy.sql import func' but my filter is dynamically created based on query params so I was wondering how to go about it.
Code for query model:
def merch_trans_sum_daily_summaries(db_engine, query_params):
query_filters = get_filters(query_params)
page, limit, filters = query_filters.page, query_filters.limit, query_filters.filters
strict_limit = query_filters.strict_limit
with Session(db_engine) as sess:
results = paginate(sess.query(MerchTransSumDaily)
.filter_by(**filters).yield_per(1000),
page, limit, strict_limit)
metadata = results.metadata
query_data = results.data
if not query_data:
raise exc.NoResultFound
data = [record._asdict() for record in query_data]
return data, metadata
Here is my get_filters function
def get_filters(query_parameters, strict_limit=100, default_page=1):
if query_parameters and "batch_type" in query_parameters:
query_parameters.pop('batch_type')
limit = int(query_parameters["limit"]) if query_parameters and "limit" in query_parameters else strict_limit
page = int(query_parameters["page"]) if query_parameters and "page" in query_parameters else default_page
filters = ""
if query_parameters:
filters = {key_: value_ for key_, value_ in query_parameters.items() if key_ not in ["page", "limit", "paginate", "filter"]}
return QueryFilters(limit, page, filters, strict_limit)
Currently working on a web page and I want to show two different tables of information one is for individual foods and the other is for recipes, however I can only get the first class to pull up any information I've tested to see if the first class can pull up the recipe database and it, in fact, currently out of ideas on what to try next.
class SearchFoodResultsView(ListView):
model = Food
template_name = 'nutrihacker/search.html'
context_object_name = 'food_list'
# overrides ListView get_queryset to find names containing search term and pass them to template
def get_queryset(self):
query = self.request.GET.get('term')
if (query == None):
return Food.objects.all()
else: # If there are any foods containing the query, they will be in the resulting object_list which is used by search.html in a for loop
food_list = Food.objects.filter(
Q(name__icontains = query)
)
return food_list
class SearchRecipeResultsView(ListView):
model = RecipePreset
template_name = 'nutrihacker/search.html'
context_object_name = 'recipe_list'
# overrides ListView get_queryset to find names containing search term and pass them to template
def get_queryset(self):
query = self.request.GET.get('term')
if (query == None):
return RecipePreset.objects.all()
else: # If there are any recipes containing the query, they will be in the resulting object_list which is used by search.html in a for loop
recipe_list = RecipePreset.objects.filter(
Q(name__icontains = query)
)
return recipe_list
I am writing a new app which connects to an old database. For now, I'm reflecting the database objects rather than define them manually in SQLA ORM classes. I've set up my flask app like so:
config = Config.get_config_for_env(env)
app = Flask(name)
app.config.from_object(config)
metadata = MetaData()
db = SQLAlchemy(metadata=metadata)
db.init_app(app)
app.db = db
app.app_context().push()
# Reflect DB
db.metadata.reflect(bind=db.engine, views=True)
The call above, reflects the entire database. My apps normally touch a few tables at a time so it makes sense to reflect database tables lazily. This can be done like so:
db.metadata.reflect(bind=db.engine, schema='MySchema', only=['MyTable'])
To do this, I'll need to insert a layer that ensures before a query is executed, the schema and table have been reflected. This adds complexity as all queries will have to go through another layer of code. Is there a way to reflect database schema+table implicitly on demand as the query is made?
Well, if know the name of the table you need, then you can do:
table_to_work_with = Table("tablename", db.metadata, bind=db.engine, autoload=True)
And you can use sqlalchemy.inspect to get the table names, as described here: List database tables with SQLAlchemy
AFAIK, there is no way to do this. It can be done via a test class. Something like this, where self.clone() clones an object:
class TempDbApp(BaseApp):
def __init__(self, env_src, name='TempDbApp', *args, **kwargs):
super().__init__('t', name, *args, **kwargs)
self.env_src = env_src
self.logger = logging.getLogger(__name__)
self.schemas = ['dbo']
self.metadata = MetaData()
def setup(self):
super().setup()
self.test_db_name = self.create_db()
def teardown(self):
# Do not drop db at end because pool
super().teardown()
self.metadata.drop_all(self.db.engine)
for schema in self.schemas:
if schema != 'dbo':
self.db.engine.execute(DropSchema(schema))
self.drop_db()
def create_db(self):
url = copy(self.db.engine.url)
engine = create_engine(url, connect_args={'autocommit': True}, isolation_level='AUTOCOMMIT')
res = engine.execute(f"exec dbo.usp_createShortLivedDB")
tempdbname = res.fetchone()[0]
res.close()
engine.dispose()
self.db.engine.url.database = tempdbname
return tempdbname
def drop_db(self):
url = copy(self.db.engine.url)
db = url.database
url.database = 'master'
engine = create_engine(url, connect_args={'autocommit': True}, isolation_level='AUTOCOMMIT')
if database_exists(url):
assert db != 'master'
res = engine.execute(f"EXEC dbo.usp_deleteshortliveddb #dbname = '{db}'")
res.close()
def fetch_schemas(self):
results = self.db.engine.execute('SELECT name FROM sys.schemas')
for schema in results:
self.schemas.append(schema[0])
results.close()
def create_schema(self, schema):
with self.session() as sess:
sess.execute(CreateSchema(schema))
self.schemas.append(schema)
#lru_cache(maxsize=2048)
def clone(self, table, schema):
if schema not in self.schemas:
self.create_schema(schema)
self.metadata.reflect(self.engine_src, schema=schema, only=[table])
self.metadata.drop_all(self.db.engine) # precautionary in case previous test didn't clean things up
self.metadata.create_all(self.db.engine)
#lru_cache(maxsize=2048)
def get_table(self, table, schema, db=None):
self.clone(table, schema)
return super().get_table(table, schema, db)
#lru_cache(maxsize=2048)
def get_selectable(self, table, schema, db=None):
self.clone(table, schema)
return Table(table, self.db.metadata, schema=schema, autoload=True, autoload_with=self.db.get_engine(bind=db))
#lazy
def engine_src(self):
conn_string = f'mssql+pymssql://user:pass#{self.env_src}-slo-sql-ds/mydb?charset=utf8'
return create_engine(conn_string, isolation_level='AUTOCOMMIT')
def start(self):
raise Exception("You must not call this method - this app is for testing")
Then a test class can be created using multiple inheritance:
#final
class MyRealWorldClassTest(TempDbApp, MyRealWorldClass):
pass
Here is my views.py:
class my4appCompanyData(generics.ListAPIView):
serializer_class = my4appSerializer
def get_queryset(self,request):
"""Optionally restricts the returned data to ofa company,
by filtering against a `id` query parameter in the URL. """
queryset = companies_csrhub.objects.all()
#url_id = self.request.query_params.get('id', None)
url_id = request.GET.get('id', None)
if id is not None:
queryset = queryset.filter(id=url_id)
elif id is ALL:
queryset = companies_csrhub.objects.all()
else:
queryset = "Error data not found"
return queryset
And my urls.py:
router.register(r'api/my4app/company/$', views.my4appCompanyData.as_view(),base_name="company")
URL used for checking: mywebsite/api/my4app/company/?id=100227
Planning to add multiple filters with default values but not working. Please help.
class my4appCompanyData(generics.ListAPIView):
serializer_class = my4appSerializer
def get_queryset(self,request):
"""Optionally restricts the returned data to ofa company,
by filtering against a `id` query parameter in the URL. """
queryset = companies_csrhub.objects.all()
url_id = request.query_params.get('id', None)
if id is not None:
queryset = queryset.filter(id=url_id)
elif id is ALL:
queryset = companies_csrhub.objects.all()
else:
queryset = []
return queryset
Delete the return id since id is not a queryset, therefore it'd give an error. Also in the else part of if statement, you return string but you can't do that also since string is not a queryset.
According the official docs (http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-query-parameters)
I think your code is not working because you are using:
url_id = request.query_params.get('id', None)
Instead of:
url_id = self.request.query_params.get('id', None)
In the documentation you can find that get_queryset function just receives self param, you must remove request param.
I want to use Django REST framework to create an API to call different methods. I read the guide of [django-rest-framework][1] to work with this framework, but I still have some questions.
I have no model I get my data from an external database. I want to try first something simple:
Get all list of project
Get the data from one project
For that I create new app I include in the setting file and in my view.py I include that for the fist case
def connect_database():
db = MySQLdb.connect(host='...', port=, user='...', passwd='...', db='...')
try:
cursor = db.cursor()
cursor.execute('SELECT * FROM proj_cpus')
columns = [column[0] for column in cursor.description]
# all_rows = cursor.fetchall()
all_rows = []
for row in iter_row(cursor):
all_rows.append(dict(zip(columns, row)))
finally:
db.close()
return all_rows
def iter_row(cursor, size= 1000):
while True:
results = cursor.fetchmany(size)
if not results:
break
for item_result in results:
yield item_result
class cpuProjectsViewSet(viewsets.ViewSet):
serializer_class = serializers.cpuProjectsSerializer
def list(self, request):
all_rows = connect_database()
name_project = []
for item_row in all_rows:
name_project.append(item_row['project'])
name_project = list(sorted(set(name_project)))
serializer = serializers.cpuProjectsSerializer(instance=name_project, many=False)
return Response(serializer.data)
my serializers file I have this
class cpuProjectsSerializer(serializers.Serializer):
project = serializers.CharField(max_length=256)
def update(self, instance, validated_data):
instance.project = validated_data.get('project', instance.project)
return instance
Now when I execute this http://127.0.0.1:8000/hpcAPI
I obtain this error
Got AttributeError when attempting to get a value for field `project` on serializer `cpuProjectsSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `list` instance.
Original exception text was: 'list' object has no attribute 'project'.
I look for in google and I change this
serializers.cpuProjectsSerializer(instance=name_project, many=False) for
serializers.cpuProjectsListSerializer(instance=name_project, many=False)
but I obtain the same error!
any idea about that!
Thanks in adavances
From docs here.You don't have to have a model for create a Serializer class.You can define some serializers field then use them. You should not import CPUProjectsViewSet and also define it in below
from mysite.hpcAPI.serializers import CPUProjectsViewSet
class CPUProjectsViewSet(viewsets.ViewSet):
"""
return all project name
"""
all_rows = connect_database()