Get choices from a DataBase query in wtforms and flask-sqlalchemy - flask

I'm developing a web app using Flask, SQLAlchemy and WTForms. I would like to get my choices in a SelectField from a query through my DB.
With more details.
my_query = my_table.query.with_entities(My_Entities).all()
Result
[(u'1',), (u'2',), (u'3',)]
My class
class MyForm(Form):
My_Var = SelectField(choices=RIGHT_HERE)
Is there any way ?

In this situation what you can do is use the extensions that are in WTForms. What you do is import the QuerySelectField that you need:
from wtforms.ext.sqlalchemy.fields import QuerySelectField
Then you create a function that will query the database and return the stuff you need:
def skill_level_choices():
return db.session.query(SkillLevel).all()
After you have the query object you can place it into your QuerySelectField by using the query_factory parameter
skill_level = QuerySelectField(u'Skill level',
validators=[Required()],
query_factory=skill_level_choices)

Solution:
I had quite simmilar problem with wtforms and peewee, and here is my workaround
class MyForm(FlaskForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.my_select_field.choices = [
(el.id, el.description) for el in MyModel.select()
]
my_select_field = SelectField("Field", coerce=int)
Explanation:
We modified original FlaskForm, so that it executs database query each time when it is being created.
So MyForm data choices stays up to date.

Populate QuerySelectField with values from Database
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from flask_sqlalchemy import SQLAlchemy
name=QuerySelectField('Name',query_factory=lambda:my_table.query,get_label="username")

options = SelectField('optionName', default='None', choices=[(option.name, option.name) for option in Options.query.all()])
Where Options is the db.Model.

Related

Django query from paypalipn table

I have connected my django-paypal and I have managed to make payments but it seems I can make queries from paypal_ipn table or I'm making mistakes somewhere. The below are the currect snippets of what I have done.
from paypal.standard.ipn import models as paypal_models
from .serializers import PaymentSerializer
#api_view(['GET'])
def getPaymentStatus(request):
postedRef = PaymentSerializer(data=request.data)
print(postedRef)
paypalTxn = paypal_models.PayPalIPN #On here I'm trying to query
# I want something like this
#paypalTxn = paypal_models.PayPalIPN.objects.filter(invoice=postedRef).first()
serializer = PaymentSerializer(paypalTxn)
return Response(serializer.data)

Flask-Admin returns "ValueError: Invalid format string" on clicking create for any model

I have an existing Flask project which uses SQLAlchemy and I wanted to interate an Admin dashboard. Everything worked fine, I managed to enable authentication by using the ModelView Class however if I try to edit or if I try to create a new object of any database model then Flask-Admin throws out the following error:
ValueError: Invalid format string
Here's my Flask-Admin Code:
from flask_admin import Admin
from flask_login import current_user
from flask import redirect, url_for, request
from app import app, db, login
from flask_admin.contrib.sqla import ModelView
from app.auth.models import User
from app.forum.models import thread, post
from app.course.models import Courses
from flask_admin.model import typefmt
from datetime import date
app.config['FLASK_ADMIN_SWATCH'] = 'cerulean'
def date_format(view, value):
return value.strftime('%d.%m.%Y')
MY_DEFAULT_FORMATTERS = dict(typefmt.BASE_FORMATTERS)
MY_DEFAULT_FORMATTERS.update({
type(None): typefmt.null_formatter,
date: date_format
})
class adminmodelview(ModelView):
column_type_formatters = MY_DEFAULT_FORMATTERS
def is_accessible(self):
return (current_user.is_authenticated and current_user.is_admin)
def inaccessible_callback(self, name, **kwargs):
return redirect(url_for('home.index'))
admin = Admin(app, name='celis', template_mode='bootstrap3')
admin.add_view(adminmodelview(User, db.session))
admin.add_view(adminmodelview(post, db.session))
admin.add_view(adminmodelview(thread, db.session))
admin.add_view(adminmodelview(Courses, db.session))
Here's the User Model:
class User(UserMixin,db.Model):
id=db.Column(db.Integer,primary_key=True)
username=db.Column(db.String(64),index=True,unique=True)
email=db.Column(db.String(120),index=True,unique=True)
user_role=db.Column(db.String(20))
is_admin=db.Column(db.Integer, default=0)
Region=db.Column(db.String(20))
password_hash=db.Column(db.String(128))
threads=db.relationship('thread',backref='creator',lazy='dynamic')
posts=db.relationship('post',backref='Author',lazy='dynamic')
last_seen=db.Column(db.DateTime,default=datetime.utcnow)
twitter=db.Column(db.String(120),default="N/A")
facebook=db.Column(db.String(120),default="N/A")
instagram=db.Column(db.String(120),default="N/A")
birthdate=db.Column(db.String(120),default="N/A")
Interests=db.Column(db.String(200),default="N/A")
provides_course=db.relationship('Courses',backref="Teacher",lazy='dynamic')
def __repr__(self):
return '<Role:{} Name:{} Id:{}>'.format(self.user_role,self.username,self.id)
def set_password(self,password):
self.password_hash=generate_password_hash(password)
def check_password(self,password):
return check_password_hash(self.password_hash,password)
def get_reset_token(self, expires_sec=1800):
s = Serializer(app.config['SECRET_KEY'], expires_sec)
return s.dumps({'id': self.id}).decode('utf-8')
On searching I found out it could be an issue due to the DateTime presentation, but could not figure out the solution.
bro I had a similar issue to yours, where the exception stemmed from the "date_posted" field in my "Threads" table as by default flask admin reads all data object as a String object so you have to override it as follows in your adminmodelview for example:
form_overrides=dict(date_posted=DateTimeField)

use dependency injection with flask-sqlalchemy

I am looking for a way to use simple dependency injection in combination with flask-sqlalchemy
Here's an example setup
extensions.py
db = SQLAlchemy()
my_model.py
from extensions import db
class MyModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
// ... some more fields
my_model_repository.py
from my_model import MyModel
class MyModelRepository:
def get_all():
MyModel.query.all()
Now I would like to somehow inject the db into MyModelRepository so that when I unit test it I can inject a mock or an in-memory database without having to deal with any global config or flags.
Here's an example of how I would want it to look
from my_model import MyModel
class MyModelRepository:
def __init__(self, db):
self.db = db
def get_all():
db.query(MyModel).all()
This however seems to not be possible because MyModel inherits from db.Model and db already is a specific instance of SQLAlchemy.
What is the appropriate way to make the database injectable to any component that depends on it?
For me it worked something like this:
In my_model_repository.py add the inject decorator to the init of your class
from my_model import MyModel
from flask_sqlalchemy import SQLAlchemy
class MyModelRepository:
#inject
def __init__(self, db: SQLAlchemy):
self.db = db
def get_all():
db.query(MyModel).all()
and in your app.py or another source file where you create your flask app add:
import flask_injector
from extensions import db
app = Flask(__name__) #something like this should already be in your code
db.init_app(app)
#this function could potentially be moved to it's own file
def configure_dependencies(binder):
binder.bind(SQLAlchemy, to=db, scope=flask_injector.singleton)
flask_injector.FlaskInjector(app=app, modules=[configure_dependencies])
Data sources:
https://pypi.org/project/Flask-Injector/
https://www.pythonfixing.com/2022/04/fixed-circular-import-of-db-reference.html

Override Django Object Serializer to get rid of specified model

I need to convert a Django Queryset Object into a Json string. The built in Django Serialization library works great. Although it specifies the name of the Model from where it was created. Since I don't need this, how do I get rid of it? What else do I need to override to be able to use the overridden end_object method below?
class Serializer(PythonSerializer):
def end_object(self, obj):
self.objects.append({
"model" : smart_unicode(obj._meta), # <-- I want to remove this
"pk" : smart_unicode(obj._get_pk_val(), strings_only=True),
"fields" : fields
})
self._current = None
Sorry I had totally forgot about this question. This is how I ended up solving it (with thanks to FunkyBob on #django):
from django.core.serializers.python import Serializer
class MySerialiser(Serializer):
def end_object( self, obj ):
self._current['id'] = obj._get_pk_val()
self.objects.append( self._current )
# views.py
serializer = MySerialiser()
data = serializer.serialize(some_qs)
Here's a serializer that removes all metadata (pk, models) from the serialized output, and moves the fields up to the top-level of each object. Tested in django 1.5
from django.core.serializers import json
class CleanSerializer(json.Serializer):
def get_dump_object(self, obj):
return self._current
Serializer = CleanSerializer
Edit:
In migrating my app to an older version of django (precipitated by a change in hosting provider), the above serializer stopped working. Below is a serializer that handles the referenced versions:
import logging
import django
from django.core.serializers import json
from django.utils.encoding import smart_unicode
class CleanSerializer148(json.Serializer):
def end_object(self, obj):
current = self._current
current.update({'pk': smart_unicode(obj._get_pk_val(),
strings_only=True)})
self.objects.append(current)
self._current = None
class CleanSerializer151(json.Serializer):
def get_dump_object(self, obj):
self._current['pk'] = obj.pk
return self._current
if django.get_version() == '1.4.8':
CleanSerializer = CleanSerializer148
else:
CleanSerializer = CleanSerializer151
Serializer = CleanSerializer
Override JSON serializer class:
from django.core.serializers.json import Serializer, DjangoJSONEncoder
from django.utils import simplejson
class MySerializer(Serializer):
"""
Convert QuerySets to JSONS, overrided to remove "model" from JSON
"""
def end_serialization(self):
# little hack
cleaned_objects = []
for obj in self.objects:
del obj['model']
cleaned_objects.append(obj)
simplejson.dump(cleaned_objects, self.stream, cls=DjangoJSONEncoder, **self.options)
In the view:
JSONSerializer = MySerializer
jS = JSONSerializer()

JSON Serializing Django Models with simplejson

I'd like to use simplejson to serialize a Django model. Django's serializer doesn't support dictionaries... and simplejson doesn't support Django Querysets. This is quite a conundrum.
In the model there's sponsors that have a Foreign Key to sponsor level, I'm trying to group all the sponsors that belong to a certain sponsor level together. Here's the code that generates the list:
from django.shortcuts import get_list_or_404
from special_event.models import Sponsor, SponsorLevel
sponsor_dict = {}
roadie_sponsors = get_list_or_404(Sponsor, level__category = SponsorLevel.ROADIE_CHOICE)
for item in roadie_sponsors:
try:
sponsor_dict[item.level.name].append(item)
except KeyError:
sponsor_dict[item.level.name] = [item]
Here's what sponsor_dict looks like once it's "made"
{
'Fan': [<Sponsor: Fan Sponsor>],
'VIP': [<Sponsor: VIP Sponsor>],
'Groupie': [<Sponsor: Groupie Sponsor>],
'Silver': [<Sponsor: Silver Sponsor>],
'Bronze': [<Sponsor: Another Bronze Sponsor>, <Sponsor: Bronze Sponsor>]
}
I only added one sponsor in each level, except for bronze, just to show how it works. All I want to do is get it "all" into JSON so jQuery can interpret it easily. Can Django's other serializers (like XML or YAML) accomplish this? Can I "extend" the Django JSON Serializer to handle dictionaries or "extend" simplejson to handle Django QuerySet objects?
I would go with extending simplejson. Basically, you want to plug in django's serialization when the JSON encoder encounters a QuerySet. You could use something like:
from json import dumps, loads, JSONEncoder
from django.core.serializers import serialize
from django.db.models.query import QuerySet
from django.utils.functional import curry
class DjangoJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, QuerySet):
# `default` must return a python serializable
# structure, the easiest way is to load the JSON
# string produced by `serialize` and return it
return loads(serialize('json', obj))
return JSONEncoder.default(self,obj)
# partial function, we can now use dumps(my_dict) instead
# of dumps(my_dict, cls=DjangoJSONEncoder)
dumps = curry(dumps, cls=DjangoJSONEncoder)
For more info on default method, have a look at simplejson documentation. Put that in a python module, then import dumps and you're good to go. But note that this function will only help you serializing QuerySet instances, not Model instances directly.
A really flexible way to serialize most structures in django is to use the serializer class found here
based on Clement's answer, I did this to get models into JSON as well.
def toJSON(obj):
if isinstance(obj, QuerySet):
return simplejson.dumps(obj, cls=DjangoJSONEncoder)
if isinstance(obj, models.Model):
#do the same as above by making it a queryset first
set_obj = [obj]
set_str = simplejson.dumps(simplejson.loads(serialize('json', set_obj)))
#eliminate brackets in the beginning and the end
str_obj = set_str[1:len(set_str)-2]
return str_obj