Why my SQLAlchemy query.flter works only on some attribute? - python

I am learning SQLAlchemy of Python.
Below is an example I am useing.
First I generate a datafile contains puppy information like below:
class Puppy(Base):
__tablename__ = 'puppy'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
gender = Column(String(6), nullable = False)
dateOfBirth = Column(Date)
shelter_id = Column(Integer, ForeignKey('shelter.id'))
weight = Column(Numeric(10))
male_names = ["Bailey", "Max", ...just some names..., "Luke", "Henry"]
female_names = ['Bella', 'Lucy', ...just some names..., 'Honey', 'Dakota']
def CreateRandomAge():
today = datetime.today()
days_old = randint(0,540)
birthday = today - timedelta(days = days_old)
return birthday
def CreateRandomWeight():
return random.uniform(1.0, 40.0)
for i,x in enumerate(male_names):
new_puppy = Puppy(name = x, gender = "male", dateOfBirth = CreateRandomAge(), weight= CreateRandomWeight())
session.add(new_puppy)
session.commit()
for i,x in enumerate(female_names):
new_puppy = Puppy(name = x, gender = "female", dateOfBirth = CreateRandomAge(), weight= CreateRandomWeight())
session.add(new_puppy)
session.commit()
Now I want to filter some kinds of puppies as below:
testpuppy = session.query(Puppy).filter_by(name='Lucy')
print(testpuppy)
birthdate = datetime.today() - timedelta(days=180)
smallpuppy = session.query(Puppy).filter_by(dateOfBirth < birthdate)
print(smallpuppy)
Then it is strange, because the testpuppy passed, I can get Lucy, but the dateofBirth can not pass, every time I want to get these smallpuppies, I just got an error
NameError: name 'dateOfBirth' is not defined
I really can not understand, why my filter can only be operated on some attribute, where is wrong?

The problem is that you need to use filter instead of filter_by like this:
smallpuppy = session.query(Puppy).filter(Puppy.dateOfBirth < birthdate)
For filter, the criterion should use ClassName.propertyName to access the column, and you can use < or >.
For filter_by, the criterion could be use propertyName directly to access the column, but you cannot use < or >.
Please refer to this answer, it will give you more details about the difference between filter and filter_by.

Related

How do I insert into a Table one Primary Key and Two Foreign Keys?

I work with Rolls of plastic film in different legnth and width. And I'm creating a Database to store all the orders, and, in order to avoid repetition, I created separate tables for length(class(Comprimento)) and width(class(Largura)). I used UUID to create distinct ID's.
Now, I want to cross both tables in a Model class. Which is:
class Largura(Base):
__tablename__ = 'largura'
id = Column(GUID(), primary_key=True, default=lambda: str(uuid.uuid4()))
largura = Column(String)
modelos_l = relationship('Modelo', back_populates='larguras', cascade='all, delete')
def __repr__(self):
return f"<Largura {self.largura}>"
class Comprimento(Base):
__tablename__ = 'comprimento'
id = Column(GUID(), primary_key=True, default=lambda: str(uuid.uuid4()))
comprimento = Column(String)
modelos_c = relationship('Modelo', back_populates='comprimentos', cascade='all, delete')
def __repr__(self):
return f"<Comprimento {self.comprimento}>"
class Modelo(Base):
__tablename__ = 'modelo'
id = Column(GUID(), primary_key=True, default=lambda: str(uuid.uuid4()))
descricao = Column(String(50))
largura_id = Column(GUID(), ForeignKey("largura.id"), default=lambda: str(uuid.uuid4()))
comprimento_id = Column(GUID(), ForeignKey("comprimento.id"), default=lambda: str(uuid.uuid4()))
larguras = relationship('Largura', back_populates='modelos_l')
comprimentos = relationship('Comprimento', back_populates='modelos_c')
def __repr__(self):
return f"<Modelo {self.id}>"
Then, i created a file dedicated to my data insert on this table:
from DBModelPy3 import Comprimento,Largura,Modelo,session
from sqlalchemy import create_engine
import pandas as pd
#Pre Loading my CSV file
df = pd.read_csv("dataorged.csv", sep=',')
pd.set_option('display.float_format','{:.0f}'.format) #change the number format to hide the ','
cnx = create_engine('sqlite:///data_hub2.db', echo=True).connect()
df_modelo = df[['larg_ajustada', 'comp']] # My dataframe that contains the orders. I chose the specifics columns needed for this insertion.
#print(df_modelo)
# Loading the Tables from my database
df_largura = pd.read_sql_table('largura', cnx)
df_comprimento = pd.read_sql_table('comprimento', cnx)
With everything loaded I decided to combine all the legnths and widths i had already on my two tables (df_largura and df_comprimento), and then filtered using the original file which contains the orders.
# COMBINING ALL THE LENGTH AND WIDTH OF MY TABLES
model_num = []
for n_larg in range(len(df_largura)):
db_larg = str(df_largura['largura'][n_larg])
for n_comp in range(len(df_comprimento)):
db_comp = df_comprimento['comprimento'][n_comp]
combined = str(db_larg) + "x" + str(db_comp)
model_num.append([db_larg,db_comp,combined])
df_modelos_ex = pd.DataFrame(model_num)
df_modelos_ex.columns = ['larg','comp','combined']
With these, i had all possible combinations on my dataframe.
And created the combined variable to match later
modelos_existentes = []
# COMBINATIONS THAT APPEAR IN THE ORDER DATAFRAME #
for item in range(len(df_modelo)):
mod_larg = df_modelo['larg_ajustada'][item]
mod_comp = df_modelo['comp'][item]
mod_comb = str(mod_larg) + "x" + str(mod_comp)
modelos_existentes.append([mod_larg,mod_comp,mod_comb])
df_mod_existentes = pd.DataFrame(modelos_existentes)
df_mod_existentes.columns = ['ex_larg','ex_comp','ex_comb']
df_limpo = df_mod_existentes.drop_duplicates(subset=['ex_comb'])
df_limpo.reset_index(drop=True, inplace=True)
With all my elements, then the madness began.
I started a loop to run through all my Dataframes:
for l_row in range(len(df_limpo)): # For Each Row in my dataframe which contains the orders,
larg = df_limpo['ex_larg'][l_row] # create variable for width
comp = df_limpo['ex_comp'][l_row] # create variable for lenght
comb = df_limpo['ex_comb'][l_row] # create variable for combination of both
for n_row in range(len(df_largura)): # For each row in my width table from DB,
db_larg_id = df_largura['id'][n_row] # I create a Variable for the PK from width
db_larg_largura = df_largura['largura'][n_row] # Create a Variable with the value
lar = session.query(Largura).filter(Largura.id == db_larg_id).first()
if db_larg_largura == larg: # If the value on my table matches the value of the row in the order,
for m_row in range(len(df_comprimento)): # For each length in my table on the DB,
db_comp_id = df_comprimento['id'][m_row]
db_comp_comprimento = df_comprimento['comprimento'][m_row]
compr = session.query(Comprimento).filter(Comprimento.id == db_comp_id).first()
if db_comp_comprimento == comp: # If the value on my table matches the value of the row in the order
new_model = Modelo(descricao=df_limpo['ex_comb'][n_linha], larguras=lar, comprimentos=compr)
from here, i would only add the session.add(new_model) and session.commit() to finish my code.
But it's not adding.
What I would like is for my Modelo table be like:
MODELO Table
ID(PK) | DESCRIPTION (Combined values String) | Largura_id (width_id, FK) | Comprimento_id (length_id, FK)
Sorry about the long explanation. Tried my best!
If anyone have the same trouble:
##########################
# ADDING TO THE DATABANK #
##########################
lista_a = [] #Created an empty List
for n_linha in range(len(df_limpo)): #Ran through my dataframe
larg_a = df_limpo['ex_larg'][n_linha] #Extracted width and length from it
comp_a = df_limpo['ex_comp'][n_linha]
for m_linha in range(len(df_largura)): #Ran my width table from database
db_larg_id = df_largura['id'][m_linha]
db_larg_largura = df_largura['largura'][m_linha]
if larg_a == db_larg_largura: #Checked if the width from my dataframe matches the one on the table
lista_a.append([larg_a,comp_a,db_larg_id]) #appended to the list_a
#print(lista_a)
df_lista_a = pd.DataFrame(lista_a) #Created a new Dataframe
df_lista_a.columns = ['larg','comp','id_larg']
lista_b = [] #Created a new list
for n_row in range(len(df_lista_a)): #Ran through my new dataframe
larg_b = df_lista_a['larg'][n_row] #Extracted each column from it
comp_b = df_lista_a['comp'][n_row]
larg_b_id = df_lista_a['id_larg'][n_row]
#df_limpo_lrow = df_limpo['ex_larg'][n_row]
#df_limpo_crow = df_limpo['ex_comp'][n_row]
#df_limpo_cbrow = df_limpo['ex_comb'][n_row]
#print(larg_b,comp_b,larg_b_id,n_row)
for m_row in range(len(df_comprimento)): #Ran through my lenght table
db_comp_id = df_comprimento['id'][m_row]
db_comp_comprimento = df_comprimento['comprimento'][m_row]
if comp_b == db_comp_comprimento: #Check if the lenght from dataframe matches the lenght on my table on the database
#print(larg_b,comp_b,n_row,m_row,df_limpo_lrow)
lista_b.append([larg_b,comp_b,larg_b_id,db_comp_id]) #appended the lenght id to my list
break
#print(lista_b)
#print(len(df_lista_a),len(df_limpo),len(lista_b))
df_lista_b = pd.DataFrame(lista_b) #converted to Dataframe.
df_lista_b.columns = ['larg','comp','id_larg','id_comp']
# HERE's the ACTUAL INSERTION
for n_model in range(len(df_lista_b)): #For each model found on the list, extract the values and add to new_model.
mod_largura = df_lista_b['larg'][n_model]
mod_comprimento = df_lista_b['comp'][n_model]
mod_largura_id = df_lista_b['id_larg'][n_model]
mod_comprimento_id = df_lista_b['id_comp'][n_model]
lar = session.query(Largura).filter(Largura.id == df_largura['id'][1]).first()
compr = session.query(Comprimento).filter(Comprimento.id == df_comprimento['id'][1]).first()
new_model = Modelo(descricao=df_limpo['ex_comb'][n_model], larguras=lar, comprimentos=compr)
print("Modelo: " + df_limpo['ex_comb'][n_model] + " com Id's " + mod_largura_id + " e " + mod_comprimento_id + " adicionados!")
session.add(new_model)
session.commit()
Then it's done.

Updating data in a model from views Django

Hi i have a problem with updating data which is stored in a model. I would like to update the data which is stored in a model without a form, it is a function which sums up every user transaction and after every change I would like it to update.
my models:
class Portfolio(models.Model):
portfolio_id = models.AutoField(primary_key=True,blank=True)
portfolio_title = models.CharField(unique=True,max_length=200, null=True,blank=True)
user_name = models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL,blank=True)
p_shares_num_sum = models.DecimalField(decimal_places=2,default=0,max_digits=999,editable=True, null=True,blank=True)
p_last_mod_date = models.DateField(auto_now_add=False,null=True,editable=True,blank=True)
p_comp_num_sum = models.DecimalField(decimal_places=2,default=0,max_digits=999,editable=True, null=True,blank=True)
p_to_buy_percentage = models.CharField(max_length=200,editable=True, null=True,blank=True)
p_profit_earned = models.DecimalField(decimal_places=6,editable=True,default=0,max_digits=999, null=True,blank=True)
def __str__(self):
return self.portfolio_title if self.portfolio_title else ''
```
The data which I want to send to it after every entry on site, with function
shares_num = visdata.aggregate(Sum(('shares_number')))
shares_num_sum = (shares_num['shares_number__sum'])
shares_num_sum = format(shares_num_sum, ".0f")
#profit_earned = visdata.aggregate(Sum(('course')))
#profit_sum = (profit_earned['course__sum'])
fare_paid = visdata.aggregate(Sum(('fare')))
fare_sum = (fare_paid['fare__sum'])
mod_date = visdata.order_by('-date').first().date
to_buy = visdata.filter(buy_sell='+').count()
to_sell = visdata.filter(buy_sell='-').count()
to_buy_percentage = 0
to_buy_percentage = to_buy / comp_number
to_buy_percentage = (to_buy_percentage) * 100
to_buy_percentage = format(to_buy_percentage, ".0f")
to_buy_percentage = str(to_buy_percentage) + '%'
#for customer restriction delete object and change VisData to visdata
aggregated_data = visdata.annotate(
intermid_result=F('course') - F('fare')
).annotate(
record_total=F('shares_number') * F('intermid_result')
).aggregate(
total=Sum('record_total')
)
profit_earned = aggregated_data['total']
profit_earned = format(profit_earned, ".2f")
summary_data = [int(shares_num_sum),int(comp_number),mod_date,str(to_buy_percentage),float(profit_earned)]
The function is written and prints me:
[4, 2, datetime.date(2021, 12, 20), '100%', 0.9]
If you have that data from function in your view, just get your current portolio object and asign to it's fields values, then call save() method from that object.
For example:
portfolio_object = Portfolio.objects.get(pk=some_pk)
portfolio_object.some_field = summary_data[0]
... here the rest of values
portfolio_object.save()
Remember that it'll execute every time you open that view, so think about some optimalization.

peewee - change schema dynamically

I have the same question/problem than this post -> peewee - modify db model meta (e.g. schema) dynamically . I want to change the schema field in my Meta class dynamically. This is my code:
class GPSPosition(Model):
def __init__(self, esquema, vehiculo, fechaFrom):
self.esquema = esquema + '_org'
self.vehiculo = vehiculo
self.fechaFrom = fechaFrom
orgid = BigIntegerField()
id = BigIntegerField()
vehicleid = BigIntegerField()
driverid = BigIntegerField()
originaldriverid = BigIntegerField(null=False)
blockseq = IntegerField(null=False)
time = DateTimeField(null=False)
latitude = FloatField(null=False)
longitude = FloatField(null=False)
altitude = SmallIntegerField(null=False)
heading = SmallIntegerField(null=False)
satellites = SmallIntegerField(null=False)
hdop = FloatField(null=False)#float
ageofreading = IntegerField(null=False)
distancesincereading = IntegerField(null=False)
velocity = FloatField(null=False)
isavl = BooleanField(null=False)
coordvalid = BooleanField(null=False)
speedkilometresperhour = DecimalField(null=False)
speedlimit = DecimalField(null=False)
vdop = SmallIntegerField(null=False)
pdop = SmallIntegerField(null=False)
odometerkilometres = DecimalField(null=False)
formattedaddress = CharField(null=False)
source = CharField(null=False)
class Meta:
database = db
schema = esquema
db_table = 'test_gpspositions'
primary_key = CompositeKey("orgid", "id")
Can someone please show me the light about this? Thanks!
Well I'll answer my own question since I found the answer time ago and it's very simple, just add this 1-2 lines at the point you want to change the schema name:
schemaname = 'your_schema_name'
setattr(YourPeeweeModel._meta, "schema", schemaname)
Works fine.

counter method does not count well

I have a method that tells the times that a worker makes a sale, but it does not work because it only marks me that he has made 1 but has actually done 5. Next I leave an image and the code used to guide me.
**class Worker (models.Model):**
_name = 'project_rc.worker'
sales_counter = fields.Integer (string = "Sales made", compute = "get_sales_made")
document_ids = fields.One2many (comodel_name = 'project_rc.document',
inverse_name = 'worker_id', string = 'Invoice')
def get_sales_made (self):
count = self.env ['project_rc.type_movement']. search_count ([('type_movement', '=', 'sale')])
self.counter_sale = count
**class Document (models.Model):**
type_movement_id = fields.Many2one (comodel_name = 'project_rc.type_movement', string = "Movement type")
worker_id = fields.Many2one (asdel_name = 'project_rc.worker', string = "Worker")
**class Type_Movement (models.Model):**
type_movement = fields.Selection ([('purchase', 'Purchase'), ('sale', 'Sale'), ('merma', 'Merma')], string = "Movement type")
document_ids = fields.One2many (comodel_name = 'project_rc.document', inverse_name = 'type_movimiento_id', string = 'Document')
Sample picture: https://ibb.co/vs0dw5K
The problem came from your function get_sales_made
class Worker(models.Model):
_name = 'project_rc.worker'
sales_counter = fields.Integer(string="Sales made", compute="get_sales_made")
document_ids = fields.One2many('project_rc.document', 'worker_id', string='Invoice')
#api.depends('document_ids')
def get_sales_made(self):
for rec in self:
document = rec.document_ids.filtered(lambda r: r.type_movement_id and r.type_movement_id.type_movement == 'sale')
rec.sales_counter = len(document)
class Document(models.Model):
_name = 'project_rc.document'
type_movement_id = fields.Many2one('project_rc.type_movement', string="Movement type")
worker_id = fields.Many2one('project_rc.worker', string="Worker")
class Type_Movement(models.Model):
_name = 'project_rc.type_movement'
type_movement = fields.Selection([('purchase', 'Purchase'), ('sale', 'Sale'), ('merma', 'Merma')], string="Movement type")
document_ids = fields.One2many('project_rc.document', 'type_movement_id', string='Document')
You are searching in the wrong table it should be project_rc.document
self.env['project_rc.document'].search_count([('type_movement_id.type_movement', '=', 'sale')
('worker_id', '=', rec.id)
])
Or you can simply filter document_ids to count sales.

Django - can I do this without a raw query

I am learning Django, and have gotten quite a long way using the documentation and various other posts on StackOverflow, but I am a bit stuck now. Essentially, I want to query the database as follows:
SELECT
w.wname,
w.act_owner_id,
wi.act_code,
wi.act_provider,
SUM(ft.quantity) AS "position",
prices.Current,
prices.MonthEnd,
prices.YearEnd,
cost.avgcost,
sec.securityName AS "security"
FROM
finance_wrapperinstance as wi
INNER JOIN finance_wrapper as w ON
(w.id = wi.wrapperType_id)
INNER JOIN finance_security as sec ON
(ft.security_id = sec.id)
left outer JOIN finance_transaction as ft ON
(wi.id = ft.investwrapperID_id)
left outer Join
(SELECT
hp.security_id as secid,
max(Case when hp.date = '2019-11-18' then hp.price end) as 'Current',
max(Case when hp.date = '2019-10-30' then hp.price end) as 'MonthEnd',
max(Case when hp.date = '2018-12-31' then hp.price end) as 'yearEnd'
FROM finance_historicprice as hp
GROUP BY hp.security_id
) AS prices ON
(prices.secid =ft.security_id)
INNER JOIN
(SELECT
trans.security_id AS secid,
trans.investwrapperID_id as iwID,
SUM((CASE WHEN trans.buysell = 'b' THEN trans.quantity ELSE 0 END)* trans.price) /
SUM(CASE WHEN trans.buysell = 'b' THEN trans.quantity ELSE 0 END) AS avgCost
FROM
finance_transaction as trans
GROUP BY
trans.security_id,
trans.investwrapperID_id) AS cost ON
(cost.secid = ft.security_id and cost.iwID = wi.id)
GROUP BY
w.wname,
wi.wrapperType_id,
wi.act_code,
wi.act_provider,
ft.security_id
but I don't know how to use the Django ORM to get my prices subquery or cost subquery.
The models look like this:
class Wrapper(models.Model):
wname = models.CharField(max_length=50,null=False,verbose_name="Wrapper Name")
act_owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class Wrapperinstance(models.Model):
wrapperType = models.ForeignKey(Wrapper,on_delete=models.CASCADE)
act_code = models.CharField(max_length=50,null=False, verbose_name="Account Code")
act_provider = models.CharField(max_length=50,null=False,verbose_name="Account Provider")
class Security(models.Model):
securityName = models.CharField(max_length=200,null=False,verbose_name="Security Name")
securityType = models.ForeignKey(InstrumentType,on_delete=models.CASCADE)
class Transaction(models.Model):
BUY = 'b'
SELL = 's'
BUY_SELL_CHOICES = [
(BUY, 'Buy'),
(SELL, 'Sell'),
]
security = models.ForeignKey(Security,default=1,on_delete=models.CASCADE)
investwrapperID = models.ForeignKey(Wrapperinstance,default=1,on_delete=models.CASCADE)
quantity = models.DecimalField(max_digits=14, decimal_places=4)
buysell = models.CharField(max_length=2,choices=BUY_SELL_CHOICES, default = BUY)
price = models.DecimalField(max_digits=14, decimal_places=2)
class HistoricPrice(models.Model):
security = models.ForeignKey(Security,default=1,on_delete=models.CASCADE)
date = models.DateField()
price = models.DecimalField(max_digits=14, decimal_places=2)
Any help or pointers would be greatly appreciated. As an additional point, I have functions which will choose the correct dates to be entered for the SQL query. This again makes me think the RAW method may be the way to go.

Categories