Generate a spending report from the following tables - mysql

My current project is to generate a spending report from the following tables. The complete report is to show a user's spending $ breakdown by Franchise's retail category, such as
User 1
retail_category 1: $20
retail_category 2: $30
retail_category 3: $35
User 2
retail_category 1: $10
retail_category 2: $15
retail_category 3: $5
Here are the tables:
class User(models.Model):
id_user = models.AutoField(primary_key = True)
class Franchises(models.Model):
id_franchise = models.AutoField(primary_key=True)
retail_category = models.IntegerField(default=99) # values are 1 to 13
class Stores(models.Model):
id_store = models.AutoField(primary_key=True)
franchise = models.ForeignKey(Franchises, db_column='id_franchise')
class Receipts(models.Model):
id_receipt = models.AutoField(primary_key=True)
store = models.ForeignKey(Stores, db_column='id_store')
user = models.ForeignKey(User, db_column='id_user')
grand_total = models.DecimalField(max_digits=19, decimal_places=4)
I'd appreciate any raw mysql or django model query. Thanks,

Hopefully I'm translating your django schema spec to raw mysql correctly:
SELECT user, retail_category, SUM(grand_total)
FROM Receipts
INNER JOIN Stores ON Receipts.store = Stores.id_store
INNER JOIN Franchises ON Stores.franchise = Franchises.id_franchise
GROUP BY user, retail_category;

Related

Difference on field based on other field value?

So this is my model:
class InAndOut(models.Model):
quantity = models.FloatField()
date= models.DateField(null=True)
type = models.CharField(max_length=12)
id_product = models.ForeignKey(Products, on_delete=models.CASCADE)
I want to query distinct values based on id_product and for each one the difference quantity based on type field("in" or "out"):
So a real example would look like:
quantity = 1500
type = In
id_product = Gas
quantity = 300
type = Out
id_product = Gas
query - Gas 1200
please use this code.
qs =InAndOut.objects.filter(id_product='Gas')
diff_value=(qs.filter(types='In').annotate(Sum('quantity')).values()[0]['quantity__sum'] - qs.filter(types='Out').annotate(Sum('quantity')).values()[0]['quantity__sum'])
This code working for me.

Trouble with sqlalchemy join query - get the next date from child

Using the example below, i'm trying to make a single query that will get my list of offices, and pull the next upcoming visit from the child table.
class Office(db.Model):
id = db.Column(db.Integer, primary_key=True)
office_name = db.Column(db.String(100))
visits = db.relationship('Visit', backref='office', lazy='select', order_by='desc(Visit.visit_date)')
class Visit(db.Model):
id = db.Column(db.Integer, primary_key=True)
visit_date = db.Column(db.Date)
office_id = db.Column(db.Integer, db.ForeignKey('office.id'))
I've been able to create a query in raw SQL that will return what i need:
SELECT * FROM office
LEFT OUTER JOIN ( SELECT office_id, visit_date FROM visit WHERE visit_date >= date('now')
GROUP BY office_id )
AS next_vis ON id = next_vis.office_id
But i haven't been able to convert the above in SQLAlchemy.
Closest i've got to is this:
next_vis = db.session.query(Visit.office_id, Visit.visit_date).filter(
Visit.visit_date >= datetime.utcnow().date()).order_by(
Visit.visit_date.asc()).group_by(Visit.office_id).subquery()
offices = db.session.query(Office, next_vis.c.visit_date).outerjoin(
next_vis, Office.id == next_vis.c.office_id).order_by(
Office.office_name.asc())
But the only problem is it returns a tuple of (office, date) but ideally i want it returned as a single object. Is that not possible?
Thanks!
If anyone is interested i went about this a slightly different way.
I switched from a join query to adding a column property in my Office model:
class Office(db.Model):
id = db.Column(db.Integer, primary_key=True)
office_name = db.Column(db.String(100))
visits = db.relationship('Visit', backref='office', lazy='select',
order_by='desc(Visit.visit_date)')
next_vis = column_property(
select([Visit.visit_date]).where(
and_(Visit.office_id == id, Visit.visit_date >= db.func.current_date())).order_by(
Visit.visit_date.asc()).correlate_except(Visit))
Now when i do Office.query.all() i can do:
for i in Office.query.all():
print(i.next_vis)
If i've overlooked anything please let me know!
Thanks,

Subquerying in Django

I have a Django 1.9 project implementing small chat app. All messages from a certain recipient are grouped into dialogs, so the models are defined as follows:
class Dialog(models.Model):
# Some fields
class Message(models.Model):
dialog = models.ForeignKey(Dialog, ...)
text = models.TextField()
is_read = models.BooleanField(default = False)
My goal is to render a template with a table that renders dialogs. And for each dialog in the table, I need to see
the number of unread messages and
the text of the last message.
To illustrate, consider mock-data below:
Input:
id dialog_id message is_read
1 1 Hello, sir false
2 1 My name is true
3 1 Jack true
4 2 This site false
5 2 is perfect false
6 2 Cheers false
Desired output:
dialog_id last_message_in_dialog unread_messages_count
1 Jack 1
2 Cheers 3
In pure mysql, I would write a query like this:
select
a.dialog_id,
text as last_message_in_dialog,
(select count(*) from message
where dialog_id = a.dialog_id and is_read = false) as unread_messages_count
from message a
where id in (select max(id) from message group by dialog_id)
In Django terms, I have the code below:
max_id_qs = Message.objects.\
values('dialog__id').\
annotate(max_id = Max('id'),).values('max_id')
qs = Message.objects.filter(id__in = max_id_qs).\
values('dialog__id', 'text')
This code serves well to fetch the last message in each dialog. However, the problem is that I can't figure out how to implement the subquery (select count(*) from message where dialog_id = a.dialog_id and is_read = false) in Django. Maybe my total approach with max_id_qsis wrong, and there's more elegant and clear way to implement the query in Django ORM?
I've spent an entire day trying to solve this issue. help me please !
This will work :-
allDistinctIdWithNotReadMsg =
Message.objects.filter(is_read=False).values('id').annotate(the_count=Count('is_read',distinct('id')))
for ids in allDistinctIdWithNotReadMsg:
lastMsg = Message.objects.filter(dialog_id=ids['id']).order_by("-id")[0]
for msg in lastMsg:
print ids['id'] ,msg.message,ids['the_count']

Django , Query to get certain values with a distinct name and the latest date and time

The situation is i have a database full of test results split into different test sets. Each test has a name, result , start time , start date , ... , what currently happens is over the week test sets can be run multiple times , giving multiple test results under a test set
Currently when i want to get the latest result of each test under a test set i am querying for the distinct test names, and then for each distinct name i am querying for that name and ordering by startDate and startTime to get the latest. This is a pain because when i have a test set with over 100 different tests it degrades substantially.
What im trying to do is to perform what i want in one call of django.objects...
Here is the mysql to effectively represent what i want to achieve:
select testName,result,MAX(startDate),MAX(startTime),othertestinfo from testset where testset_id = 'UniqueID' group by testName;
Im having a hard time trying to figure this out in django , if its even possible.
Any help would be much appreciated.
Thanks
Update 23/1/12
Models for what i am using.
class testCase(models.Model):
id = models.AutoField(primary_key=True)
testName = models.CharField(max_length=50)
result = models.CharField(max_length=50)
precision = models.CharField(max_length=10)
fileLocation = models.CharField(max_length=150)
testset_id = models.ForeignKey(testset)
machine = models.CharField(max_length=15)
startDate = models.DateField()
startTime = models.TimeField()
class testset(models.Model):
testsetID = models.CharField(max_length=100, primary_key=True)
testsetName = models.CharField(max_length=40)
platformName = models.CharField(max_length=15)
osName = models.CharField(max_length=15)
executionName = models.CharField(max_length=40)
version = models.CharField(max_length=10)
software = models.CharField(max_length=20)
runType = models.CharField(max_length=20)
You can give a try to the following:
t = testset.objects.values('testName').annotate(Max('startDate'),Max('startTime'))
This would give you a list of the objects' values-dictionaries containing key-value pairs of testName, startDate, startTime for the required condition.
You would get the condition fulfilled in this but you can try experimenting with this to get all the columns.

How to set filter to get children in certain time period by eagerload_all() at SqlAlchemy

I have a table posts and it stores 3 types of post, Topic, Reply and Comment. Each one has its parent id.
# Single table inheritance
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('posts.id'))
discriminator = Column(String(1))
content = Column(UnicodeText)
added_at = Column(DateTime)
__mapper_args__ = {'polymorphic_on': discriminator}
class Topic(Post):
replies = relation("Reply")
__mapper_args__ = {'polymorphic_identity': 't'}
class Reply(Post):
comments = relation("Comment")
__mapper_args__ = {'polymorphic_identity': 'r'}
class Comment(Post):
__mapper_args__ = {'polymorphic_identity': 'c'}
And I'm using eagerload_all() to get all the replies and comments belong to one topic:
session.query(Topic).options(eagerload_all('replies.comments')).get(topic_id)
My question is, if I want to get only replies and those replies' comments in certain time period, for example, this week, or this month. How should I use filter to achieve this?
Thank you
The use of eagerload_all will only query for the children of an object Topic immediately rather on first request to the Replies and/or Comments, but since you load the Topic object into the session, all its related children will be loaded as well. This gives you the first option:
Option-1: Filter in the python code instead of database:
Basically create a method on the Topic object similar to
class Topic(Post):
...
def filter_replies(self, from_date, to_date):
return [r for r in self.replies
if r.added_at >= from_date
and r.added_at <= to_date]
Then you can do similar code on Replies to filter Comments or any combination of those. You get the idea.
Option-2: Filter on the database level:
In order to achieve this you need not load the Topic object, but filter directly on the Reply/Comment. Following query returns all Reply for a given Topic with a date filter:
topic_id = 1
from_date = date(2010, 9, 5)
to_date = date(2010, 9, 15)
q = session.query(Reply)
q = q.filter(Reply.parent_id == topic_id)
q = q.filter(Reply.added_at >= from_date)
q = q.filter(Reply.added_at <= to_date)
for r in q.all():
print "Reply: ", r
The version for the Comment is just a little bit more involved as you require an alias in order to overcome the SQL statement generation issue as all your objects are mapped to the same table name:
topic_id = 1
from_date = date(2010, 9, 5)
to_date = date(2010, 9, 15)
ralias = aliased(Reply)
q = session.query(Comment)
q = q.join((ralias, Comment.parent_id == ralias.id))
q = q.filter(ralias.parent_id == topic_id)
q = q.filter(Comment.added_at >= from_date)
q = q.filter(Comment.added_at <= to_date)
for c in q:
print "Comment: ", c
Obviously you can create a function that would combine both peaces into a more comprehensive query.
In order to achieve this week or this month type of queries you can either convert these filter into a date range as shown above or use the expression.func functionality of SA.