Graphql get API is not getting updated - sqlalchemy

I have attached by crud and graphql file. The problem is if i do any mutation using graphql , then the get query fetches data correctly. However if i update my db manually then the updated record is not fetched for the graphql get query.
Am i doing the resolver right?
I updated many fields through database and it didnt reflect. How ever when i update the db using update mutation it works.
Crud file:-
def get_all_record_by_filter(self, query: models.Record) -> Any:
""" Get A Single Company """
try:
log.info("retriving all record data")
data = query.order_by(models.Record.record_id.desc())
return data
except SQLAlchemyError as e:
log.debug(f"failed to retrive Record data with {e}")
return None
Graphql file:-
class Query(graphene.ObjectType):
get_all_record_by_filter = graphene.List(Record,
operation_id=graphene.Argument(type=graphene.String,
required=False),
buy_id=graphene.Argument(type=graphene.String,
required=False),
Owner=graphene.Argument(type=graphene.String,
required=False),
Responsibles=graphene.Argument(type=graphene.String,
required=False),
informed=graphene.Argument(type=graphene.String,
required=False),
Action=graphene.Argument(type=graphene.String,
required=False),
documentversion=graphene.Argument(
type=graphene.String,
required=False)
def resolve_get_all_record_by_filter(self, info,
operation_id='', buy_id='', Owner='', Responsibles='',
informed='', Action='', documentversion='',
):
"""
Get All Record with pagination and filters
"""
query = Record.get_query(info=info) # SQLAlchemy query
if operation_id:
query = query.filter(models.Record.operation_id== operation_id)
if buy_id:
query = query.filter(models.Record.buy_id== buy_id)
if Owner:
query = query.filter(models.Record.Owner == Owner)
if Responsibles:
query = query.filter(models.Record.Responsibles.contains(Responsibles))
if informed:
query = query.filter(models.Record.informed.contains(informed))
if Action:
query = query.filter(models.Record.Action == Action)
if documentversion:
query = query.filter(models.Record.documentversion == documentversion)
return crud_record.get_all_record_by_filter(query=query)

Related

SQLALchemy update ARRAY column [duplicate]

I'm working on a project using Flask and a PostgreSQL database, with SQLAlchemy.
I have Group objects which have a list of User IDs who are members of the group. For some reason, when I try to add an ID to a group, it will not save properly.
If I try members.append(user_id), it doesn't seem to work at all. However, if I try members += [user_id], the id will show up in the view listing all the groups, but if I restart the server, the added value(s) is (are) not there. The initial values, however, are.
Related code:
Adding group to the database initially:
db = SQLAlchemy(app)
# ...
g = Group(request.form['name'], user_id)
db.session.add(g)
db.session.commit()
The Group class:
from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy.dialects.postgresql import ARRAY
class Group(db.Model):
__tablename__ = "groups"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
leader = db.Column(db.Integer)
# list of the members in the group based on user id
members = db.Column(ARRAY(db.Integer))
def __init__(self, name, leader):
self.name = name
self.leader = leader
self.members = [leader]
def __repr__(self):
return "Name: {}, Leader: {}, Members: {}".format(self.name, self.leader, self.members)
def add_user(self, user_id):
self.members += [user_id]
My test function for updating the Group:
def add_2_to_group():
g = Group.query.all()[0]
g.add_user(2)
db.session.commit()
return redirect(url_for('show_groups'))
Thanks for any help!
As you have mentioned, the ARRAY datatype in sqlalchemy is immutable. This means it isn’t possible to add new data into array once it has been initialised.
To solve this, create class MutableList.
from sqlalchemy.ext.mutable import Mutable
class MutableList(Mutable, list):
def append(self, value):
list.append(self, value)
self.changed()
#classmethod
def coerce(cls, key, value):
if not isinstance(value, MutableList):
if isinstance(value, list):
return MutableList(value)
return Mutable.coerce(key, value)
else:
return value
This snippet allows you to extend a list to add mutability to it. So, now you can use the class above to create a mutable array type like:
class Group(db.Model):
...
members = db.Column(MutableList.as_mutable(ARRAY(db.Integer)))
...
You can use the flag_modified function to mark the property as having changed. In this example, you could change your add_user method to:
from sqlalchemy.orm.attributes import flag_modified
# ~~~
def add_user(self, user_id):
self.members += [user_id]
flag_modified(self, 'members')
To anyone in the future: so it turns out that arrays through SQLAlchemy are immutable. So, once they're initialized in the database, they can't change size. There's probably a way to do this, but there are better ways to do what we're trying to do.
This is a hacky solution, but what you can do is:
Store the existing array temporarily
Set the column value to None
Set the column value to the existing temporary array
For example:
g = Group.query.all()[0]
temp_array = g.members
g.members = None
db.session.commit()
db.session.refresh(g)
g.members = temp_array
db.session.commit()
In my case it was solved by using the new reference for storing a object variable and assiging that new created variable in object variable.so, Instead of updating the existing objects variable it will create a new reference address which reflect the changes.
Here in Model,
Table: question
optional_id = sa.Column(sa.ARRAY(sa.Integer), nullable=True)
In views,
option_list=list(question.optional_id if question.optional_id else [])
if option_list:
question.optional_id.clear()
option_list.append(obj.id)
question.optional_id=option_list
else:
question.optional_id=[obj.id]

How to get data correctly from a django model to Django REST framework

I'm working with Python 3.9 and Django 3.1.6. I have a quite long model in models.py. Short version is something like this:
class Check (models.Model):
#property
def total_body_fat_percentage(self):
if self.client_gender == "M":
total_body_fat_percentage = (self.Yuhasz * 0.097) + 3.64
pass
else:
total_body_fat_percentage = total_body_fat_percentage = float((self.Yuhasz * 0.1429) + 4.56)
return total_body_fat_percentage
#property
def muscle_weight_percentage(self):
muscle_weight_percentage =self.muscle_weight*100/self.weight
return muscle_weight_percentage
These fields return a float number. Something like 14.407.
Of course there are other fields in there such as Yuhasz, client_gender, etc.
I need these values into JSON. I tried to use the following serializer on serializer.py:
class PercentagesSerializer(ModelSerializer):
class Meta:
model = Check
fields = ('total_body_fat_percentage', 'muscle_weight_percentage')
And I have the following class on views.py:
class PercentagesAPI(APIView):
authentication_classes = []
permission_classes = []
serializer = PercentagesSerializer
def get(self, request, format=None):
lista = Check.objects.all()
print(lista)
response = self.serializer(lista)
print(response)
json_response= json.dumps(response.data)
print(json_response)
data = {
'percentages': json_response
}
return Response(response.data)
But it returns no data. Shell console states that it gets the queryset, it gets the fields, but it doesn't seems to get the values:
<QuerySet [<Check: Check object (1)>]>
PercentagesSerializer(<QuerySet [<Check: Check object (1)>]>):
total_body_fat_percentage = ReadOnlyField()
muscle_weight_percentage = ReadOnlyField()
{}
Thank you in advance for your advices and help!
You populate data variable but never use it. Instead you return response.data
response = self.serializer(lista)
data = {
'percentages': json_response
}
return Response(response.data)
I reckon this is the solution:
response = self.serializer(lista, many=True) # <-- many
data = {
'percentages': json_response
}
return Response(data) # <-- data
The query does return something - print shows 1 record/object: Check object (1)

Optimization Django ORM

I'm running my own smart house project, using django backend with MySql on raspberry Pi.I've got SensorsData table in DB with thousands records with data from sensors. In my REST API I'm using view which looks like this:
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def list_of_sensors_data(request, format=None):
"""
Get list of all sensors data, only for authenticated users
:param request: GET
:return: list of all sensors data if ok http 200 response
"""
sensors_data = SensorsData.objects.all()
serializer = SensorsDataSerializer(sensors_data, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I've run perfomance test with locust simulating 10 users trying to use my endpoints. After some time, Django keeps returning 504 Timeout using this particular endpoint. My quest is, how can I optimize this queryset? I need to make it faster.
EDIT SensorsData model:
class SensorsData(models.Model):
sensor = models.ForeignKey(Sensors, on_delete=models.CASCADE)
delivery_time = models.DateTimeField(auto_now_add=True)
sensor_data = models.CharField(max_length=20)
class Meta:
verbose_name = "Sensor data"
verbose_name_plural = "Sensors data"
def __str__(self):
return f"{self.sensor.id}: {self.sensor.name}"
SensorsData Serializer:
class SensorsDataSerializer(serializers.ModelSerializer):
sensor = serializers.SlugRelatedField(read_only=False, many=False, slug_field='name', queryset=Sensors.objects.all())
class Meta:
model = SensorsData
fields = ("sensor", "delivery_time", "sensor_data")
This will introduce an N+1 problem, for each SensorsData object, you will make an additional query to fetch the related Sensor object. The good news is that you can use .select_related(…) [Django-doc] to let Django retrieve all related sensors in the same query:
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def list_of_sensors_data(request, format=None):
sensors_data = SensorsData.objects.select_related('sensor')
serializer = SensorsDataSerializer(sensors_data, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)

Persist one object from one database to another using sqlalchemy

I have two databases (both Mysql) that have exactly the same tables, and I want to copy some data from one to another using Sqlalchemy.
I can copy simple objects following the answer given in this question:
Cannot move object from one database to another
The problem is when the object has dependencies from another table, and I want to copy the dependencies as well.
So to make it more clear, this is my model (the same for both databases but using a different bind_key that points to a different database):
db1 = SQLAlchemy()
Class Payment(db.Model):
__tablename__ = 'payments'
__bind_key__ = 'db1'
id = db1.Column(db.Integer, primary_key=True)
paymethod_id = db1.Column(db.Integer(), db1.ForeignKey(PaymentMethod.id))
payment_method = db1.relationship(PaymentMethod)
What I would like to do is the following:
from models1 import Payment as Payment1
from models2 import Payment as Payment2
# query from one database
payment1 = db1.session.query(Payment1).first()
# create and add it to the other database
payment2 = Payment2(**payment1.__dict__.copy())
db2.session.add(payment)
db2.session.commit()
But in this case the foreign key fails because I don't have the PaymentMethod stored yet.
Is there a different approach to do that or I would have to do this procedure for every dependency of my object and be sure that I store the children beforehand?
Any help is appreciated :)
I came up with a solution that remaps the object to the right model and stores all its children. You call the method save_obj and pass the object you want to map. It will then retrieve a table with the same name but then from the model you want to remap the object to and it will recursively do the same for all its children. You have to define the right model in the method get_model.
To run this is necessary to disable autoflush to prevent committing before the object is correctly formed and it is also necessary to commit after calling the method. I'm using flask-sqlalchemy.
Hope this can help or give some insight to someone that faces a similar problem :)
def save_obj(obj, checked=[]):
if obj in checked:
# if the object was already converted, retrieve the right object
model = get_model(obj.__mapper__.mapped_table.name)
return get_obj(obj, model)
checked.append(obj)
children = []
relations = obj.__mapper__.relationships.items()
# get all the relationships of this model (foreign keys)
for relation in relations:
model = get_model(relation[1].table.name)
if model:
# remove the cascade option for this object, so the children are not stored automatically in the session
relation[1]._cascade = CascadeOptions('')
child = getattr(obj, relation[0])
if not child:
continue
# if the child is a set of children
if isinstance(child, list):
new_children = []
for ch in copy(child):
# convert the child
new_child = save_obj(ch, checked)
new_children.append(new_child)
children.append((relation[0], new_children))
else:
new_child = save_obj(child, checked)
children.append((relation[0], new_child))
# get the model of the object passed
model = get_model(obj.__mapper__.mapped_table.name)
new_obj = get_obj(obj, model)
# set all the children in this object
for child in children:
if child[1]:
setattr(new_obj, child[0], child[1])
checked.append(new_obj)
session.add(new_obj)
return new_obj
def get_model(table_name):
# get the right model for this object
for table in db.Model._decl_class_registry.values():
if hasattr(table, '__tablename__') and table.__tablename__ == table_name:
return table
return None
def create_new_obj(obj, model):
params = obj.__dict__.copy()
params.pop('_sa_instance_state')
return model(**params)
def get_obj(child, model):
# check if the object is already stored in the db
child_in_db = session.query(model).get(child.id)
if child_in_db:
return child_in_db
# check if the object is already in the session
for s in session.new:
if type(s) == model and s.id == child.id:
return s
return create_new_obj(child, model)

Build dropdown from database table names

I have this application where I want to deploy some web interface to a syslog server.
So the syslogserver does write his stuff into a mysql database. I already have build
some parts for the application except for this specific part where I want to build a dropdown select form, to select the hosttables inside the database.
Actually I am using flask, flask-sqlalchemy and wtforms. So I tried to implement this over the 'QuerySelectField', somehow I only get a dropdown with no table name entries shown.
I should mention that the tables inside the database itself are created on the fly. For my model I used the automap_base() Feature from sqlalchemy:
model.py
Base = automap_base()
engine = create_engine("mysql://sumuser:tehpass#127.0.0.1/syslog")
Base.prepare(engine, reflect=True)
session = Session(engine)
This is whats inside my forms:
forms.py
def factoryHelper():
return session.query("information_schema.tables.table_name from information_schema.tables where information_schema.tables.table_name like 'messages_hostname0'")
class HostSelectForm(Form):
title = TextField('Fooblah')
hostTables = QuerySelectField(query_factory=factoryHelper,allow_blank=True)
and this inside the views:
views.py
#app.route('/index/', defaults={'page':1})
#app.route('/index/page/<int:page>')
def index(page):
form = HostSelectForm()
count = session.execute("select host,facility,level,msg from messages_hostname0").rowcount
pagination = Pagination(page, PER_PAGE, count)
return render_template('index.html', pagination=pagination, form=form)
So is there anyway I can create a dropdown menu from dynamically created table names? Also if I use the automap feature? Thanks in advance.
Somehow i managed to solve this issue with this in the model.py:
def reflectTables():
for i in Base.classes.items():
yield i[0]
def stripTables():
tablelist = []
datelist = []
re0 = r"^(?P<prefix>[a-zA-Z]*)\_(?P<mid>[a-zA-Z,0-9]*)\_(?P<suffix>[0-9]*)"
myre = re.compile(re0, re.MULTILINE)
for x,table in enumerate(reflectTables()):
striptablename = re.match(myre, table.strip("\n"))
if striptablename:
tablelist.append((x, striptablename.group(2)))
datelist.append((x, striptablename.group(3)))
return dicht(tablelist,datelist)
the forms.py:
AVAILABLE_CHOICES = stripTables()
class HostSelectForm(Form):
tableSelect = SelectField('LogHost', choices=AVAILABLE_CHOICES, default=0)
and finnaly inside the views.py:
if request.method == "GET":
count = session.query("* from mytable_monitory_counts")
items = session.execute("select * from mytable_%s_%s limit %s, %s" % \
(tableslector[int(request.values['tableSelect'])][1],\
datelist[int(request.values['tableSelect'])][1], my_start_range, PER_PAGE)).fetchall()
pagination = Pagination(page=page, total=count.count(), search=search, record_name='hosts')
if not items and page != 1:
in_error()
return render_template('index.html', pagination=pagination, form=form, items=items)