Sum felds with with sqlalchemy and marshmallow - python-3.x

I'm using the python's framework pyramid, sqlalchemy for the data and marshmallow to serialize.
I'm want to send a sum of value to the client
I have Challenge and player whom use this challenge, each actions of the player is an event and each events has a duration.
For each challenge, i want to send the sum's duration for each player and also send this value only for one player. I try to use hybrid attribute or pre_dump function but i didn't succeeded
First method : with hybrid attribute
__tablename__ = "Challenge"
id = Column(Integer, primary_key=True)
name = Column(String(255), unique=True, nullable=False)
description = Column(TEXT(length=65535))
map_url = Column(String(255))
end_date = Column(DateTime(timezone=False))
alone_only = Column(Integer)
level = Column(String(255))
scalling = Column(Integer)
draft = Column(Boolean, server_default=text("0"))
admin_id = Column(Integer, ForeignKey("User.id"))
admin = relationship("User", backref="challenge_manager")
event_sum_user = relationship("Events")```
#hybrid_property
def event_sum(self):
return sum(Events.duration for Events in self.event_sum_user)
But i have sum for all user, not by user or for one user
Second method :with pre_dump method
id = fields.Int()
name = fields.Str()
description = fields.Str()
end_date = fields.DateTime()
alone_only = fields.Int()
level = fields.Str()
scalling = fields.Int()
draft = fields.Bool()
admin = fields.Nested(UserSchema)
admin_id = fields.Int(load_only=True)
event_sum = fields.Int(dump_only=True)
#pre_dump
def get_eventsum(self,data, **kwargs):
data["event_sum"] = DBSession.query(func.sum(Events.duration)).filter(Events.challenge_id==data["id"]).filter(Events.user_id==1).first()
return data```
With this method, i've have an error TypeError: 'Challenge' object is not subscriptable'
The purpose of this is to send with each challenge the total duration realise by a user or for each user on the challenge : id_user and total duration.
Thanks for your help
Marilyn

Related

How to access model field value before edit form presented to user in flask-admin?

I have model User:
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(50), unique=True, nullable=False)
token_life_time = db.Column(db.Integer, nullable=False)
and ModelView for it from flask-admin:
class UserModelView(ModelView):
column_list = ('id', 'username', 'token_life_time')
form_create_rules = ('username', 'token_life_span') # custom field, that doesn't exist in actual model
form_edit_rules = ('username', 'token_life_time', 'new_token_life_time')
form_extra_fields = {
'token_life_span': StringField(default='1-0-0-0', validators=[DataRequired()]),
'new_token_life_time': StringField() # how to put here actual value from model.token_life_time as default value in EDIT form ???
}
column_formatters = dict(
token_life_time=lambda v, c, m, p: UserModelView.convert_seconds_to_human_readable(m.token_life_time)
)
def on_model_change(self, form, model, is_created):
token_time = form.data.get('token_life_span', None)
if token_time:
model.token_life_time = self.convert_to_seconds(token_time) # my custom function that converts string of format (days-hours-minutes-seconds, example: 1-8-23-15) into seconds for storing in DB int type
I want to access model.token_life_time value before EDIT form presented to user and convert it from seconds to human readable string (with days, hours etc.). Then to put converted value as default value in custom field in edit form (new_token_life_time).
The question is - how to access actual value from model and then put converted value to custom field as default value in EDIT form ???
Override the view's edit_form method - see documentation.
Something like the following:
class UserModelView(ModelView):
def edit_form(self, obj=None):
form = super().edit_form(obj=obj)
# calculate token_life_span
_token_life_span = get_token_life_span(obj.token_life_time)
form.token_life_span.data = _token_life_span
return form

Flask backref returing empty list

I did ask this question but i am gonna ask it again since didn't get any response
I am developing a simple flask app with Projects and Tickets tables and they look like following
class Projects(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable = False)
description = db.Column(db.Text, nullable = False)
created_by_id = db.Column(db.Integer, nullable = False)
expert_id = db.Column(db.Integer, db.ForeignKey('users.id'))
def __repr__(self):
return f"{self.title}"
class Tickets(db.Model):
id = db.Column(db.Integer,primary_key=True)
title = db.Column(db.String(100),nullable=False)
ticket_text = db.Column(db.Text,nullable=False)
date_posted = db.Column(db.DateTime,nullable=False,default=datetime.utcnow)
created_by_id = db.Column(db.Integer, nullable=False)
expert_id = db.Column(db.Integer, db.ForeignKey('users.id'),nullable=False)
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'),nullable=False)
projects = db.relationship('Projects', backref='ticketso', lazy=True)
def __repr__(self):
return f"Tickets('{self.title}','{self.date_posted}')"
Basically it is one Project can have many Tickets but when I query it
#app.route('/project/<project_id>')
def project(project_id):
project = Projects.query.get_or_404(project_id)
return render_template('project.html',title=project.title, project=project)
and trying to display it
<div class="jumbotron">
<h1>{{ project.title }}</h1>
<p>{{ project.description }}</p>
<p><a class="btn btn-primary btn-md">{{ project.created_by_id }}</a></p>
<p><a class="btn btn-primary btn-lg">{{ project.ticketso }}</a></p>
</div>
Here project.ticketso supposed to return list of tickets assigned to this Project but instead it returns emtpy list
My tickets.html page can succecfully display Project_id which assigned to that particular ticket
this is how I add ticket to the project
#app.route('/createTicket',methods=['GET','POST'])
def createTicket():
users = Users.query.all()
form = TicketForm()
if form.validate_on_submit():
ticket = Tickets(title=form.title.data,ticket_text=form.ticket_text.data,created_by_id=current_user.username,expert_id= str(form.user_id.data),project_id= str(form.project.data))
db.session.add(ticket)
db.session.commit()
flash('Your ticket has been created', 'success')
return redirect(url_for('index'))
return render_template('createTicket.html',title='Create a Ticket',form=form, users=users)
#app.route('/createProject',methods=['GET','POST'])
def createProject():
users = Users.query.all()
form = ProjectForm()
if form.validate_on_submit():
project = Projects(title = form.title.data, description = form.description.data, created_by_id = current_user.username, expert_id = str(form.user_id.data))
db.session.add(project)
db.session.commit()
flash('You project has been created', 'success')
return redirect(url_for('index'))
return render_template('createProject.html',form = form, users = users)
EDIT:
I am gonna ask this question different way in order to make it clear.
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
addresses = db.relationship('Address', backref='person', lazy=True)
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), nullable=False)
person_id = db.Column(db.Integer, db.ForeignKey('person.id'),
nullable=False)
The code above I got from flask documentation https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/
It states
backref is a simple way to also declare a new property on the Address class. You can then also use my_address.person to get to the person at that address
So I can access to the person who is in that address, but how do we access to the list of addresses that person has. Because it is one to many relationship, it means one person can have many addresses correct? how do we access them?
You need to "connect" between the 2 tables. You don't need to define expert_id when you create a row. instead you need to point to the Tickets Table by the backref you set.
Change this line
project = Projects(title = form.title.data, description = form.description.data, created_by_id = current_user.username, expert_id = str(form.user_id.data))
To this:
project = Projects(title = form.title.data, description = form.description.data, created_by_id = current_user.username, ticketso=form)
assuming form is the Tickets object you want to point to. Then, you'll be able to get the ID of the Tickets with expert_id
basically, this line projects = db.relationship('Projects', backref='ticketso', lazy=True) creates a "virtual column" in the table you set. In this case is the Projects table (You can also access the Projcts table from Tickets by using the relationship column). Also this two columns are useless. I assume you know how you can access the data without them.
expert_id = db.Column(db.Integer, db.ForeignKey('users.id'),nullable=False)
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'),nullable=False)

Posting Nested models SQLAlchemy-Marshmallow

I am trying to wrap my head around SQLAlchemy in combination with Marshmallow. I had a Flask API that contains some Assets and Trading Pairs. I want bidirectional One-to-Many relationships between these models. I have the following code:
class Asset(db.Model):
__tablename__ = 'asset'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), unique=True, nullable=False)
abbreviation = db.Column(db.String(20), unique=True, nullable=True)
trading_bases = relationship("TradingPair", back_populates="base_asset", foreign_keys="TradingPair.base_id")
trading_quotes = relationship("TradingPair", back_populates="quote_asset", foreign_keys="TradingPair.quote_id")
class TradingPair(db.Model):
__tablename__ = 'trading_pair'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), unique=True, nullable=False)
# One to many (a pair can have only one base, but 1 asset can be the base of many pairs)
base_id = db.Column(db.Integer, db.ForeignKey("asset.id"), nullable=False)
base_asset = relationship("Asset", foreign_keys=[base_id], uselist=False, back_populates="trading_bases")
# One to many (same reasoning)
quote_id = db.Column(db.Integer, db.ForeignKey("asset.id"), nullable=False)
quote_asset = relationship("Asset", foreign_keys=[quote_id], uselist=False, back_populates="trading_quotes")
With the following resource for trading pair POST:
def post(self, name):
pair = TradingPair.query.filter_by(name=name).first()
if pair:
return {"Error": "This resource already exists"}, 409
data = request.get_json()
schema = TradingPairSchema()
try:
pair = schema.load(data, session=db.session)
if not pair.name == name:
return {"Error": f"{name} does not correspond to name in data"}
db.session.add(pair)
db.session.commit()
return {"Success":f"Added: {pair.name}"}
except ValidationError as e:
return {"Error": e.messages}, 400
except:
return {"Error":"Database error"}, 500
I expect SQLAlchemy to add new Assets that are POSTed as part of a new trading pair. However, if I want to post new pairs via the API using the following JSON:
{'name': 'DASHUSDT',
'base_asset': {
'name': 'Dash',
'abbreviation': 'DASH'},
'quote_asset': {
'name': 'Tether',
'abbreviation':
'USDT'}}
This works properly and the pair gets added to the DB as expected. The problem occurs when I try to add another pair that contains Dash or Tether. The pair is added again to the DB and my uniqueness constraint on the Asset table is violated. How can I ensure that a new instance is not created but the existing asset is used?
I ended up with checking whether the assets exist and adding them to the database if they do not yet exist. The code I used in the POST function of the trading pair is:
loaded = schema.load(data, session=db.session)
if not loaded.name == name:
return {"Error": f"{name} does not correspond to name in data"}
base = Asset.query.filter_by(abbreviation=loaded.base_asset.abbreviation).first()
if not base:
base = Asset(name=loaded.base_asset.name , abbreviation=loaded.base_asset.abbreviation)
db.session.add(base)
quote = Asset.query.filter_by(abbreviation=loaded.quote_asset.abbreviation).first()
if not quote:
quote = Asset(name=loaded.quote_asset.name, abbreviation=loaded.quote_asset.abbreviation)
db.session.add(quote)
pair = TradingPair(name=name, base_asset=base, quote_asset=quote)
db.session.add(pair)
db.session.commit()
This seems to work properly when the Asset already exist, but also does not crash when a new Asset is inserted via the POST of a trading pair. I could not find any documentation in either SQLAlchemy, Flask-SQLAlchemy or Marshmallow-SQLAlchemy on how this should be handled properly but for now, this works.

how to allow users to Create Classes and link their Database in Python Flask?

I want to allow users to Create Class and link their database from the front-end to add few Inputs like ( column definitions )
for example I want the users to pick
1- column name
2- type of column
3- primary key ( True or False )
4- character count
5- unique ( True or False )
i want pyton to create something like this
class Users(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
location = db.Column(db.String(25))
name = db.Column(db.String(80))
title = db.Column(db.String(25))
def __init__(self,email,password,active,confirmed_at,location,name,title):
#self.id = id
self.email = email
self.password = password
self.location = location
self.name = name
self.title = title
also i want python to allow user to link there database ( 1 for every session )
app.config['SQLALCHEMY_DATABASE_URI'] = '< user_input_goes_here >'
i can't find a title to what i'm looking for but i guess the code might be similar to the back end of the "adminer.cs50.net" because it can read the database of whatever input we
just googling and i'm not sure what i should be looking for
still its an idea i have no code yet
app.config['SQLALCHEMY_DATABASE_URI'] = '< user_input_goes_here >'
class Users(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
location = db.Column(db.String(25))
name = db.Column(db.String(80))
pos = db.Column(db.String(25))
def __init__(self,email,password,active,confirmed_at,location,name,pos,entryNo,userCode):
#self.id = id
self.email = email
self.password = password
self.location = location
self.name = name
self.pos = pos

Django Haystack return related insances

Hi I´m using django with haystack search. I have one model for Events. Thats the model I'm doing the search on. I have a second model to count the hits/views for the Events. I wan't to return the number of hits for every event additional to the search results.
my view:
def event_search(request):
if request.method == 'POST':
query = str(request.POST['search'])
events = SearchQuerySet().auto_query(query).models(Event).order_by('date')
return render_to_response('event_search.html', {"events": events}, context_instance=RequestContext(request))
else:
return render_to_response('event_search.html', context_instance=RequestContext(request))
my models:
class Event(models.Model):
name = models.CharField(max_length = 70)
date = models.DateTimeField()
description = models.TextField()
active = models.BooleanField(default=True, editable=False)
featured = models.BooleanField(default=False)
class EventHitcount(models.Model):
hit = models.ForeignKey(Event)
ip = models.CharField(max_length=40)
session = models.CharField(max_length=40)
created = models.DateTimeField(default=datetime.datetime.now())
By giving the ForeignKey field a related name it can call and count the related objects.
class Foo(models.Model):
fk = models.ForeignKey(Event,related_name='foofk')
some more fields...
In the template:
{{ foo.foofk.count }}

Resources