peewee not using dynamic table name in select statement - python

Why does the select statement here have t1 instead of MyDynamicTable?
from peewee import *
database = SqliteDatabase(None)
class Base(Model):
class Meta:
database = database
class MyTable(Base):
FieldA = TextField()
FieldB = TextField()
mytable = type('MyDynamicTable', (MyTable,), {})
database.init('test.db')
mytable.select()
Leads to:
>>> mytable.select()
<class 'peewee.MyDynamicTable'> SELECT "t1"."id", "t1"."FieldA", "t1"."FieldB" FROM "mydynamictable" AS t1 []
But the name is correct:
>>> mytable
<class 'peewee.MyDynamicTable'>
>>> mytable._meta.db_table
'mydynamictable'

Peewee has aliased your table name. If you read the full query:
SELECT "t1"."id", "t1"."FieldA", "t1"."FieldB"
FROM "mydynamictable" AS t1
The "mydynamictable" AS t1 part aliases the table name to "t1", to make the query more compact. This is especially important when you have joins and need to disambiguate columns with the same name.

Related

Join request in django between three tables and display all attributes

I have three models
class A(models.Model):
field1 = models.IntegerField()
class B(models.Model):
id_a = models.ForeignKey(A,on_delete=models.CASCADE)
field1 = models.IntegerField()
field2 = models.IntegerField()
class C(models.Model):
id_a = models.ForeignKey(A,on_delete=models.CASCADE)
field1 = models.IntegerField()
field2 = models.IntegerField()
I want to write a request that looks like this: SELECT * FROM B,C,A WHERE B.id_a=C.id_a WHERE A.id_a=2 and display all the attributes of the two tablesHere is what I tried to do:
a_id_att = 1
data = B.objects.filter(id_a=C.objects.filter(id_a=a_id_att)[0])
It does not work. How to write the join and make to display all the attributes of the tables?
The SQL statement that you wrote seems strange.
SELECT * FROM B, C, A
WHERE B.id_a = C.id_a
AND A.id_a = 2
It seems that you want a single row from A and then all related rows from B and C, which your SQL query does NOT achieve.
Did you mean something like this:
SELECT * FROM B, C, A
WHERE A.id = 2
AND B.id_a = A.id
AND C.id_a = A.id
You can achieve something like that in Django using prefetch_related(), which builds a query so that the related rows are also loaded into memory in the first query and not in subsequent queries.
# this will return a queryset with a single element, or empty
qs = A.objects.prefetch_related('b_set', 'c_set').filter(id=2)
for elem in qs: # here the single DB query is made
print(elem.field1) # A.field1
for det in elem.b_set.all():
print(det.field1) # B.field1, does NOT make another DB query
print(det.field2) # B.field2, does NOT make another DB query
for det in elem.c_set.all():
print(det.field1) # C.field1, does NOT make another DB query
print(det.field2) # C.field2, does NOT make another DB query
Note: I use b_set here because that is the default for the ForeignKey field; this changes if the field would specify a different related_name.
Does this address and solve your issue?

Sqlalchemy: how to define constraint newid() for Guid column (mssql)

I want to create a column (Id) of type uniqueidentifier in sqlalchemy in a table called Staging.Transactions. Also, I want the column to automatically generate new guids for inserts.
What I want to accomplish is the following (expressed in sql)
ALTER TABLE [Staging].[Transactions] ADD DEFAULT (newid()) FOR [Id]
GO
The code in sqlalchemy is currently:
from sqlalchemy import Column, Float, Date
import uuid
from database.base import base
from sqlalchemy_utils import UUIDType
class Transactions(base):
__tablename__ = 'Transactions'
__table_args__ = {'schema': 'Staging'}
Id = Column(UUIDType, primary_key=True, default=uuid.uuid4)
AccountId = Column(UUIDType)
TransactionAmount = Column(Float)
TransactionDate = Column(Date)
def __init__(self, account_id, transaction_amount, transaction_date):
self.Id = uuid.uuid4()
self.AccountId = account_id
self.TransactionAmount = transaction_amount
self.TransactionDate = transaction_date
When I create the schema from the python code it does not generate the constraint that I want in SQL - that is - to auto generate new guids/uniqueidentifiers for the column [Id].
If I try to make a manual insert I get error message: "Cannot insert the value NULL into column 'Id', table 'my_database.Staging.Transactions'; column does not allow nulls. INSERT fails."
Would appreciate tips on how I can change the python/sqlalchemy code to fix this.
I've found two ways:
1)
Do not use uuid.uuid4() in init of your table class, keep it simple:
class Transactions(base):
__tablename__ = 'Transactions'
__table_args__ = {'schema': 'Staging'}
Id = Column(String, primary_key=True)
...
def __init__(self, Id, account_id, transaction_amount, transaction_date):
self.Id = Id
...
Instead use it in the creation of a new record:
import uuid
...
new_transac = Transacatins(Id = uuid.uuid4(),
...
)
db.session.add(new_transac)
db.session.commit()
Here, db is my SQLAlchemy(app)
2)
Without uuid, you can use raw SQL to do de job (see SQLAlchemy : how can I execute a raw INSERT sql query in a Postgres database?).
Well... session.execute is a Sqlalchemy solution...
In your case, should be something like this:
table = "[Staging].[Transactions]"
columns = ["[Id]", "[AccountId]", "[TransactionAmount]", "[TransactionDate]"]
values = ["(NEWID(), \'"+str(form.AccountId.data) +"\', "+\
str(form.TransactionAmount.data) +", "+\
str(form.TransactionDate.data))"]
s_cols = ', '.join(columns)
s_vals = ', '.join(values)
insSQL = db.session.execute(f"INSERT INTO {table} ({s_cols}) VALUES {s_vals}")
print (insSQL) # to see if SQL command is OK
db.session.commit()
You have to check where single quotes are really needed.

Peewee query with join doesn't work as expected

I'm new to peewee and currently trying to migrate from normal Python SQlite3 library.
While my code generate a valid SQL query that return result as expected using a SQlite DB browser, trying to get the value of a field return AttributeError: x object has no attribute y.
Model:
class TableShows(BaseModel):
sonarr_series_id = IntegerField(column_name='sonarrSeriesId', unique=True)
title = TextField()
class Meta:
table_name = 'table_shows'
class TableHistory(BaseModel):
sonarr_series_id = ForeignKeyField(TableShows, field='sonarr_series_id', column_name='sonarrSeriesId')
class Meta:
table_name = 'table_history'
Peewee Query:
data = TableHistory.select(
TableShows.title,
TableHistory.sonarr_series_id
).join(
TableShows
).order_by(
TableShows.title.asc()
)
Resulting SQL query:
SELECT "t1"."title", "t2"."sonarrSeriesId"
FROM "table_history" AS "t2"
INNER JOIN "table_shows" AS "t1" ON ("t2"."sonarrSeriesId" = "t1"."sonarrSeriesId")
ORDER BY "t1"."title" ASC
Resulting dicts():
{'title': u'Test title', 'sonarr_series_id': 1}
Why does running this:
for item in data:
print item.title
Return this:
AttributeError: 'TableHistory' object has no attribute 'title'
http://docs.peewee-orm.com/en/latest/peewee/relationships.html#selecting-from-multiple-sources
You access the data via item.sonarr_series_id.title
You might consider naming your fields something a bit more pythonic.

How to get table alias for inner join in django

I'm using django 2.1, python 3.6 and SQL Server 2012 as backend. I have following models:
class ModelA(models.Model):
name = models.CharField(...)
value = models.PositiveIntegerField(...)
class ModelB(models.Model):
name = models.CharField(...)
values = models.ManyToManyField(ModelA, through='ModelC')
class ModelC(models.Model):
model_a = models.ForeignKey(ModelA, ...)
model_b = models.ForeignKey(ModelB, ...)
info_a = models.CharField(...)
info_b = models.CharField(...)
How can I achieve following SQL query:
SELECT t1.model_a_id AS a_id, t3.value AS a_value
FROM ModelB AS t0
INNER JOIN ModelC t1 ON t1.model_b_id = t0.id
INNER JOIN ModelC t2 ON t2.model_b_id = t0.id
INNER JOIN ModelA t3 ON t3.id = t2.model_a_id
INNER JOIN ModelC t4 ON t4.model_b_id = t0.id
WHERE t1.model_a_id in (1,2) AND t2.model_a_id in (8,9,10,11) AND t4.model_a_id in (21,22)
What I have so far:
ModelB.objects.filter(values__in=[1,2]).filter(values__in=[8,9,10,11]).filter(values__in=[21,22])
Which produces the correct filtered QuerySet. But how can I get the correct fields?
I tried to use annotate function but I failed. Using django's Subquery as described in the docs generates database error, because SQL Server does not support subqueries in the SELECT part.
Any recommendations? Thanks!
I solved it without falling back to raw sql. I used django's FilteredRealtion objects in combination of additional annotate like this:
from django.db.models import Q, F, FilteredRelation
qs = ModelB.objects.filter(values__in=[21,22])
qs = qs.filter(values__in=[1,2])
qs = qs.filter(values__in=[8,9,10,11])
qs = qs.annotate(_a_id=FilteredRelation('modelc', condition=Q(values__in=[8,9,10,11])),
_a_value=FilteredRelation('modelc', condition=Q(values__in=[1,2])))
qs = qs.annotate(a_id=F('_a_id__model_a'), a_value=F('_a_value__model_a__value'))
qs = qs.values('a_id', 'a_value')
Your query is not optimal, but that's a different issue.
You can try raw query
So you can run:
query = """
SELECT t1.model_a_id AS a_id, t3.value AS a_value
FROM ModelB AS t0
INNER JOIN ModelC t1 ON t1.model_b_id = t0.id
INNER JOIN ModelC t2 ON t2.model_b_id = t0.id
INNER JOIN ModelA t3 ON t3.id = t2.model_a_id
INNER JOIN ModelC t4 ON t4.model_b_id = t0.id
WHERE t1.model_a_id in ({0}) AND t2.model_a_id in ({1}) AND t4.model_a_id in
({2})"""
t1_filters = ','.join(['1','2'])
t2_filters = ','.join(['8', '9', '10', '11'])
t4_filters = ','.join(['21', '22'])
results = ModelA.objects.raw(query.format(t1_filters, t2_filters, t4_filters))
for instance in results.all():
print(instance)

Get last insert ID in peewee ORM (python)

I would like to retrieve the last insert ID (for use in a second table as a relation), however, I do not know how to get it. I am using the peewee ORM.
The table 'readings' in the database 'nest' has a column 'id' (int (11) auto_increment, primary key).
import time
from peewee import *
database = MySQLDatabase('nest', **{'user': 'nest'})
class Readings(BaseModel):
time = DateTimeField()
class Meta:
db_table = 'readings'
dt = Readings.insert(time=time.strftime("%x %X"))
dt.execute();
print "Last insert id:", dt.last_insert_id(database, Readings);
The last line is where I am stuck. Thanks a lot for your help.
Thank you very much for the the help, the answer was really easy. Here is the correct code:
import time
from peewee import *
database = MySQLDatabase('nest', **{'user': 'nest'})
class Readings(BaseModel):
time = DateTimeField()
class Meta:
db_table = 'readings'
dt = Readings.insert(time=time.strftime("%x %X"))
print "Last insert id: %i" % dt.execute()
You can direct fetch id of that insert record like below
import time
from peewee import *
database = MySQLDatabase('nest', **{'user': 'nest'})
class Readings(BaseModel):
time = DateTimeField()
class Meta:
db_table = 'readings'
dt = Readings(time=time.strftime("%x %X"))
dt.save()
print "Last insert id:"+str(dt.id);
just in case , you can get the id right after insert
huey = User()
huey.username = 'Huey'
huey.save()
huey.id
Python doesn't require semi colons!
Just a note... InsertQuery.execute() should actually return the last insert id. If you want to call last_insert_id() you need to pass in the cursor that was used to execute the query.

Categories