Making smarter queries with advanced ORM resources
CC Attribution - NoDerivatives 4.0 International:
You are free to use, copy, distribute and transmit the work or content in unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Lesche, Mariana Bedran
Django ORM has lots of resources for making complex database queries and the documentation brings good examples on how to apply each one of them, but understanding how to orchestrate all of those resources on real-life projects may not be so simple. My goal with this talk is to show through examples how to combine some of the QuerySet Methods, Query Expressions and other optimization techniques to make the most of DB resources when processing information inside the code is not an option. When we start modelling an application, we don’t always know how it’s models will evolve and it may be even more difficult to foresee their behaviour when big amounts of data are stored in their respective tables. With tables getting bigger through the project’s life apparently harmless operations may become impossible to make. Thankfully, databases are already prepared for dealing with large amounts of data and resource consuming operations and Django’s ORM provides solutions for most of them. For this talk I’m going to build an example Django project, populate it’s tables with big enough datasets and formulate complex questions to demand the most on the ORM’s possibilities. For each question I’m writing at least one solution using the resources described in the QueryExpressions section of Django’s Docs to then analyse the SQL generated by it and the pros and cons between operational costs and code complexity. The following topics will be covered: - Using F() expressions for filtering, ordering and annotate operations - Using Max, Min, Avg with annotations - Compare Subquery expressions to queryset equivalents - Present the new Window functions for partitioned operations - Using Union queries
 Hi, I'm Mariana. I work at Labco Software Studio in Recife. Those are the great people that work with me. As I have a pretty different name, that's my nickname everywhere, so anywhere you're going to find me. So I'm from Brazil. I come from this town right there, near Rio de Janeiro. They say the best thing about it is the view from Rio. Well, it's probably two. Good. I think I got all the technical problems from the conference. So if other speakers can be okay, nothing is going to happen to you. So then I come from the city around there in Rio. I recently moved to Recife. That's up there north. Both very beautiful places. If you ever come to Brazil, come visit us. Lots of beaches and warm and music. Great places. So then that's me. And let's talk about why I came here. So you know when you go to Django documentation and you go through the orim stuff and all of those pages you never get to. Well, I used to look at them and say, hmm, never use those stuff. Let's make a talk to talk about them and learn them on the way. That's my biggest motivation to be here. Have a reason to learn of those things I didn't know about. Then, yeah, I just wanted to test all of those things I only saw on the documentation and never got to use. So a while ago I worked on a PHP project, a legacy PHP project variable. But the good thing about it is that as we had to just write raw SQL all the time, I ended up learning some interesting SQL stuff. And at some point I got to a place where I could implement some things in SQL directly that I had no idea how to do in Django. So I was, I've been wondering for a long time, well, how do I get those things together? How do I implement the things I already know in SQL with the ORM? Because while we're writing Django software, we want to use Django. So could I put the two of them together? And when would be the good situation to have that knowledge to use those things? While Django documentation has a whole section about improving your queries, so about optimization strategies, so, well, you've already added indexes to your tables. You have already paginated your requests. You have added select-related and prefetch-related to your query sets. You have tried using values, values list only, defer on your query sets. As you might know, all of those are methods that reduce the number of columns they get from the database. You have set up your database cache. You can use assert queries on your methods, on your tests to guarantee that you're not doing extra queries that you don't have to. You have done all of that, but still sometimes that's not enough, and we still have to improve our database queries. So how do we do that? There are some interesting tricks that we could use. So how did I choose to organize that? Choose a big dataset, because, well, I could just try to implement code on fake data, but then I wouldn't get the real feel of knowing if what I was doing was actually working, or it was just pretty cold, so I wanted to have real data. Then I would come up with some interesting questions and try to answer that question using those methods I'd never got to use. Not all of that came as expected to. So from the beginning, I just took too long to start it. That was my biggest problem. And while dealing with real data, it means that you don't have the perfect data. So if the data wasn't created with Django, it's a great chance that it's not going to be perfect for your Django models. So I had to validate the data while importing it. And my machine, as you can see, it's not a very great one. So I had to create an importing script that, at the same time, didn't kill my machine, consuming all of its memory, and it killed my machine hundreds of times. I just got used to rebuilding it all the time. And at the same time, didn't take forever to import all of the data. I was already using Book Create, and it's still like I was creating 100,000 entries each time, and it was still not enough to import all of the data. And then, well, after I had the data, it turns out that putting all of these things together are not quite easy. So when you look at the documentation, everything looks very simple. But when you try to put it together with real data, then, well, things get a little bit scarier. But in the end, I got some results I want to show you. So what's the data I'm going to talk about? Open data from Brazil. We have a law in Brazil that says that all public data must be available. There's an information access bill. But what's the problem with that? In real life, things don't work that well. So the data is there. It's not that accessible. So not every government agency complies. Sometimes people have to go to justice to get the data. Well, each government agency has its own data, so you have to run around asking each one of them. They're not always in open format, so some of them provide the data in PDFs or such things. And the biggest problem is that, well, people that could benefit from the data don't have the technical skills to analyze it to get it. Then a friend of mine called Avalu, he has a great project, BrazilAU, to gather all this data and make it available, clean it, so that people can go to a single source and get clean data so they can analyze it. Then while as I was preparing my talk, I asked Calvary, well, do you have any interesting data for me so I could use on my talk? He provided me with some nice data sets. I chose some of them, and why not prepare a talk and then in the end come up with a nice database that could be used somewhere else to give something interesting to other people. Then while I chose two data sets, one of them is from all the partners from companies in Brazil. It contains basically the CNPG that's a unique identifier for companies. The partner's name, the category of the partners, if it's a legal entity, if it's an actual person or it's a foreigner. The partnership category, there are many of them. The partners CNPG, it means if the partner is a company, then we have the number identifying the company that owns that other company. That's one that is set, and it became those tables. I have a partnership table that relates companies to their partners, so the company can have a foreign partner, a natural partner, or another company as a partner. They all belong to states. Then another data set is the chamber of deputies' expenses. We have a national congress. Each deputy has the right to spend a certain amount of money on their activities. The data set describes how they spent that money. I have a unique identifier for the deputies, the party, his name, the date, the reference for the month and year, the amount, description, and the company they spend the money on. Some other fields. Those become my tables. I have an expense. It's related to a deputy. The deputy is related to a party and a state, and the expense can be related to a company that can by its turn be related to a state, too. Good. Then let's go to the interesting part, the queries. First thing I want to talk about is perfection. I just discovered it like two months ago, and it changed my life. I have been using them at work for retrieving related objects given a certain context. I'm specifically using that on a project right now, where we have a small system for us to control the finances of the company, so that Fernando can pay the other partners their salaries. When we retrieve the data from a user, we want all the related data from that user, so how much time he worked on that month, the expenses he had on that month, the days he didn't work, et cetera. So we have an API. I get the user's information from that API and all the related data. Well, but when I get all the related data from a user, I don't want the data from last year that doesn't interest me. So I have an app that calculates the salary for a month, so the only thing that interests me is the data in the context of that specific month. So the best way I found out and turned out to get those related data in a context is with the professional objects. And so in our case here with the data I presented to you, how would we do that? Imagine we have a serializer, pretty simple model serializer. I want to describe the deputy of his fields with the depth of two. That means I go one, two levels of relations from that model. So then I create a list view to list all my deputies. What would we do normally on a prerecet? I get a select related to get all the party to which this deputy belongs to, the state where he was elected from with the select related. So then this select related will create joins with on statement on the database. And then the prefetch related will create another query that will get all the expenses filtered by the deputies IDs. Okay, that's what you need to show related models. But then I would get all the deputies and all the expenses from my database. And it would be a huge query. I don't want all the data. So how would I do to get only the data specific to a context? So I'm going to refactor my view to, well, first I'm going to set a month and a year as a default parameter. So you're only allowed to get data from a year, from a month. Okay. Then I get first my deputies, prerecet. So I get all my deputies and all the parties and states related to it. I set the default month and year. And then what I do is there's a last line missing there, but I think it's okay. So I'm going to get all of the query parameters that the user passes to me and pass to that method called prefetch expenses. I'm going to, like, you should never do that in real life. Don't get the query parameters from your user and just put them on your model. That's just, for example, you would normally use Django filter or something like that. I just created that method here to show how it would work. So I get all those filters, pass them directly to my query set, and that's when I prefetch the data. So yeah, it's a little bit unformatted here, but it's okay. Well, the prefetch object creates a prefetch for me. So instead of prefetching by the related name, I prefetch with a prefetch object, and I pass query for that prefetch object. So I will filter all the expenses by the parameters I got. And after this, the expenses are filtered, I pass them as a prefetch. That means when my user types in that he wants all the deputies and he wants all the deputies with their expenses on the third month of 2018, and they want the expenses that are bigger than 100 instead of each deputy with all the expenses from four years ago, I only get the expenses that match that exact parameter. So it creates a single query. I only fetch the data I have. I never had to once make a loop over the data. I just retrieve them directly from the database exactly the way I want them to be. So that's the first useful thing I've discovered recently. Then second thing, and it was very funny to do, we can filter aggregates. I didn't know that before I was planning that talk. So you know Django aggregate functions, we can sum, we can average, we have max variance, standard deviation. We have lots of aggregate functions. But we can also aggregate on filtered things. So I'm going to annotate all of my deputies. And I want a field on my deputy object that says which was how much did he spend on a month and a year? So have you used f strings? I love f strings. I'm like using them everywhere. Love them. Just beautiful. So I'm going to annotate my deputy with a field with the expenses of a particular month. How am I going to do that? I'm going to use the sum. That's the aggregate expression. Everybody knows. And I can then filter that sum. So instead of summing up all the values, whoa, ready? No. Instead of summing the values of all of the expenses, I'm going to just sum the ones that match my filter. So I get the sum of a specific field. And when you look at the query they make, they make a filter on that sum expression with a join on the expenses. Okay? Well, that does alter the filtered relations. They can do the same thing. Not that beautiful, but they do almost the same thing. I can get a filtered relation. It's the same thing. I'm going to get my deputies. I'm going to filter the expenses. Use the same condition. As you see, the condition is basically the same one. Filter them by month and year. I'm going to then create the first annotation of that that says all the expenses that were made in that month and year. And then I'm going to do a second annotate. Just going to sum up all of them. Okay? They both do the same things. If you look at the query they make, the conditions of month and year instead of appearing on a filter, like here they appear inside that where statement, and here they appear inside the on condition. That's the different performance. That's the first query. I don't really understand all of the things they do around there. But the main thing is the number up here. It shows how efficient the query is. That's the first one. And that's the second one. It's incredibly faster. Like, they do basically the same thing. End of filtering here and here. But the fact that the filter is on an on condition makes a huge difference. So the code is not that beautiful. Like, when you look at it, it's like bigger and has a strange thing in the middle, but makes a big difference in real life. And you can also aggregate annotations. That's also great. So I did the same annotation as before, but for all the months from 2009 to 2018, going to do the same annotation. So I'm going to have a spent for month annotated for each deputy. And then I can aggregate those annotations. So each deputy will have a field that says how much he spent on a specific month. And then I can do the average of that sum. So that field that says gosh to year month will be aggregated. So each deputy will have an attribute for each month in the last lots of months. And I'm going to calculate the average for each one of those. We end up with a huge dictionary, and we could also use those to build graphs. Pipelots is beautiful. We don't know what would I use that thing for, but if someone is interested in how they vary their expenses, like with two, three Python methods, I got this beautiful plot. And the nicest thing, subqueries. I've always wondered how to do that in Django. Now we can do two versions. So I want to know which deputies have companies. We cannot do no that for sure, because I don't have any unique identifier for people. The only way to identify them is by the name, so that's not very accurate. They can be people with the same names. But anyway, I wanted to at least know which deputies could have companies in their names. So I make a query on the companies. And I want to know which companies have partnerships that belong to natural people. And I want to know the name of the people that owns these companies. And I want to compare that name to something else. What's that something else? A reference to the query that's outside of it. That's how we use the alt ref. So the documentation normally shows you how to use the alt ref with primary keys. But what's the fun in using primary keys? We already have the relationships. We can use reference to any keys. So I use the reference to a name. So I have companies, the partnerships they have, the name of their partners. And I want to compare them with the names that appear in the query that's outside of it. And I want to compare the states. So it's probably, like, as I don't have any unique identifier, I want to know if they belong to the same state. So I do the same thing with the state. I make a reference to the query that's outside of it. So they match. And we can annotate that. And I discovered, I think I forgot to put here, I discovered that one tenth of our deputies have probably companies in their names. And that's the query they make. So it's basically a select on the company's table with the joins to the partnerships. And we can reference the columns from the inner query and the alpha query. Then I think, yeah, I think that's it. Help you get something from it. Thank you.
 Hi, I'm Mariana.  I work at Labco Software Studio in Recife.  Those are the great people that work with me.  As I have a pretty different name, that's my nickname everywhere, so anywhere you're  going to find me.  So I'm from Brazil.  I come from this town right there, near Rio de Janeiro.  They say the best thing about it is the view from Rio.  Well, it's probably two.  Good.  I think I got all the technical problems from the conference.  So if other speakers can be okay, nothing is going to happen to you.  So then I come from the city around there in Rio.  I recently moved to Recife.  That's up there north.  Both very beautiful places.  If you ever come to Brazil, come visit us.  Lots of beaches and warm and music.  Great places.  So then that's me.  And let's talk about why I came here.  So you know when you go to Django documentation and you go through the orim stuff and all  of those pages you never get to.  Well, I used to look at them and say, hmm, never use those stuff.  Let's make a talk to talk about them and learn them on the way.  That's my biggest motivation to be here.  Have a reason to learn of those things I didn't know about.  Then, yeah, I just wanted to test all of those things I only saw on the documentation and  never got to use.  So a while ago I worked on a PHP project, a legacy PHP project variable.  But the good thing about it is that as we had to just write raw SQL all the time, I  ended up learning some interesting SQL stuff.  And at some point I got to a place where I could implement some things in SQL directly  that I had no idea how to do in Django.  So I was, I've been wondering for a long time, well, how do I get those things together?  How do I implement the things I already know in SQL with the ORM?  Because while we're writing Django software, we want to use Django.  So could I put the two of them together?  And when would be the good situation to have that knowledge to use those things?  While Django documentation has a whole section about improving your queries, so about optimization  strategies, so, well, you've already added indexes to your tables.  You have already paginated your requests.  You have added select-related and prefetch-related to your query sets.  You have tried using values, values list only, defer on your query sets.  As you might know, all of those are methods that reduce the number of columns they get  from the database.  You have set up your database cache.  You can use assert queries on your methods, on your tests to guarantee that you're not  doing extra queries that you don't have to.  You have done all of that, but still sometimes that's not enough, and we still have to improve  our database queries.  So how do we do that?  There are some interesting tricks that we could use.  So how did I choose to organize that?  Choose a big dataset, because, well, I could just try to implement code on fake data, but  then I wouldn't get the real feel of knowing if what I was doing was actually working,  or it was just pretty cold, so I wanted to have real data.  Then I would come up with some interesting questions and try to answer that question  using those methods I'd never got to use.  Not all of that came as expected to.  So from the beginning, I just took too long to start it.  That was my biggest problem.  And while dealing with real data, it means that you don't have the perfect data.  So if the data wasn't created with Django, it's a great chance that it's not going to  be perfect for your Django models.  So I had to validate the data while importing it.  And my machine, as you can see, it's not a very great one.  So I had to create an importing script that, at the same time, didn't kill my machine,  consuming all of its memory, and it killed my machine hundreds of times.  I just got used to rebuilding it all the time.  And at the same time, didn't take forever to import all of the data.  I was already using Book Create, and it's still like I was creating 100,000 entries  each time, and it was still not enough to import all of the data.  And then, well, after I had the data, it turns out that putting all of these things together  are not quite easy.  So when you look at the documentation, everything looks very simple.  But when you try to put it together with real data, then, well, things get a little bit  scarier.  But in the end, I got some results I want to show you.  So what's the data I'm going to talk about?  Open data from Brazil.  We have a law in Brazil that says that all public data must be available.  There's an information access bill.  But what's the problem with that?  In real life, things don't work that well.  So the data is there.  It's not that accessible.  So not every government agency complies.  Sometimes people have to go to justice to get the data.  Well, each government agency has its own data, so you have to run around asking each one  of them.  They're not always in open format, so some of them provide the data in PDFs or such things.  And the biggest problem is that, well, people that could benefit from the data don't have  the technical skills to analyze it to get it.  Then a friend of mine called Avalu, he has a great project, BrazilAU, to gather all this  data and make it available, clean it, so that people can go to a single source and get clean  data so they can analyze it.  Then while as I was preparing my talk, I asked Calvary, well, do you have any interesting  data for me so I could use on my talk?  He provided me with some nice data sets.  I chose some of them, and why not prepare a talk and then in the end come up with a nice  database that could be used somewhere else to give something interesting to other people.  Then while I chose two data sets, one of them is from all the partners from companies in  Brazil.  It contains basically the CNPG that's a unique identifier for companies.  The partner's name, the category of the partners, if it's a legal entity, if it's an actual  person or it's a foreigner.  The partnership category, there are many of them.  The partners CNPG, it means if the partner is a company, then we have the number identifying  the company that owns that other company.  That's one that is set, and it became those tables.  I have a partnership table that relates companies to their partners, so the company can have  a foreign partner, a natural partner, or another company as a partner.  They all belong to states.  Then another data set is the chamber of deputies' expenses.  We have a national congress.  Each deputy has the right to spend a certain amount of money on their activities.  The data set describes how they spent that money.  I have a unique identifier for the deputies, the party, his name, the date, the reference  for the month and year, the amount, description, and the company they spend the money on.  Some other fields.  Those become my tables.  I have an expense.  It's related to a deputy.  The deputy is related to a party and a state, and the expense can be related to a company  that can by its turn be related to a state, too.  Good.  Then let's go to the interesting part, the queries.  First thing I want to talk about is perfection.  I just discovered it like two months ago, and it changed my life.  I have been using them at work for retrieving related objects given a certain context.  I'm specifically using that on a project right now, where we have a small system for us to  control the finances of the company, so that Fernando can pay the other partners their salaries.  When we retrieve the data from a user, we want all the related data from that user, so  how much time he worked on that month, the expenses he had on that month, the days he  didn't work, et cetera.  So we have an API.  I get the user's information from that API and all the related data.  Well, but when I get all the related data from a user, I don't want the data from last  year that doesn't interest me.  So I have an app that calculates the salary for a month, so the only thing that interests  me is the data in the context of that specific month.  So the best way I found out and turned out to get those related data in a context is  with the professional objects.  And so in our case here with the data I presented to you, how would we do that?  Imagine we have a serializer, pretty simple model serializer.  I want to describe the deputy of his fields with the depth of two.  That means I go one, two levels of relations from that model.  So then I create a list view to list all my deputies.  What would we do normally on a prerecet?  I get a select related to get all the party to which this deputy belongs to, the state  where he was elected from with the select related.  So then this select related will create joins with on statement on the database.  And then the prefetch related will create another query that will get all the expenses  filtered by the deputies IDs.  Okay, that's what you need to show related models.  But then I would get all the deputies and all the expenses from my database.  And it would be a huge query.  I don't want all the data.  So how would I do to get only the data specific to a context?  So I'm going to refactor my view to, well, first I'm going to set a month and a year  as a default parameter.  So you're only allowed to get data from a year, from a month.  Okay.  Then I get first my deputies, prerecet.  So I get all my deputies and all the parties and states related to it.  I set the default month and year.  And then what I do is there's a last line missing there, but I think it's okay.  So I'm going to get all of the query parameters that the user passes to me and pass to that  method called prefetch expenses.  I'm going to, like, you should never do that in real life.  Don't get the query parameters from your user and just put them on your model.  That's just, for example, you would normally use Django filter or something like that.  I just created that method here to show how it would work.  So I get all those filters, pass them directly to my query set, and that's when I prefetch  the data.  So yeah, it's a little bit unformatted here, but it's okay.  Well, the prefetch object creates a prefetch for me.  So instead of prefetching by the related name, I prefetch with a prefetch object, and I pass  query for that prefetch object.  So I will filter all the expenses by the parameters I got.  And after this, the expenses are filtered, I pass them as a prefetch.  That means when my user types in that he wants all the deputies and he wants all the deputies  with their expenses on the third month of 2018, and they want the expenses that are bigger  than 100 instead of each deputy with all the expenses from four years ago, I only get the  expenses that match that exact parameter.  So it creates a single query.  I only fetch the data I have.  I never had to once make a loop over the data.  I just retrieve them directly from the database exactly the way I want them to be.  So that's the first useful thing I've discovered recently.  Then second thing, and it was very funny to do, we can filter aggregates.  I didn't know that before I was planning that talk.  So you know Django aggregate functions, we can sum, we can average, we have max variance,  standard deviation.  We have lots of aggregate functions.  But we can also aggregate on filtered things.  So I'm going to annotate all of my deputies.  And I want a field on my deputy object that says which was how much did he spend on a  month and a year?  So have you used f strings?  I love f strings.  I'm like using them everywhere.  Love them.  Just beautiful.  So I'm going to annotate my deputy with a field with the expenses of a particular month.  How am I going to do that?  I'm going to use the sum.  That's the aggregate expression.  Everybody knows.  And I can then filter that sum.  So instead of summing up all the values, whoa, ready?  No.  Instead of summing the values of all of the expenses, I'm going to just sum the ones that  match my filter.  So I get the sum of a specific field.  And when you look at the query they make, they make a filter on that sum expression  with a join on the expenses.  Okay?  Well, that does alter the filtered relations.  They can do the same thing.  Not that beautiful, but they do almost the same thing.  I can get a filtered relation.  It's the same thing.  I'm going to get my deputies.  I'm going to filter the expenses.  Use the same condition.  As you see, the condition is basically the same one.  Filter them by month and year.  I'm going to then create the first annotation of that that says all the expenses that were  made in that month and year.  And then I'm going to do a second annotate.  Just going to sum up all of them.  Okay?  They both do the same things.  If you look at the query they make, the conditions of month and year instead of appearing on  a filter, like here they appear inside that where statement, and here they appear inside  the on condition.  That's the different performance.  That's the first query.  I don't really understand all of the things they do around there.  But the main thing is the number up here.  It shows how efficient the query is.  That's the first one.  And that's the second one.  It's incredibly faster.  Like, they do basically the same thing.  End of filtering here and here.  But the fact that the filter is on an on condition makes a huge difference.  So the code is not that beautiful.  Like, when you look at it, it's like bigger and has a strange thing in the middle, but  makes a big difference in real life.  And you can also aggregate annotations.  That's also great.  So I did the same annotation as before, but for all the months from 2009 to 2018, going  to do the same annotation.  So I'm going to have a spent for month annotated for each deputy.  And then I can aggregate those annotations.  So each deputy will have a field that says how much he spent on a specific month.  And then I can do the average of that sum.  So that field that says gosh to year month will be aggregated.  So each deputy will have an attribute for each month in the last lots of months.  And I'm going to calculate the average for each one of those.  We end up with a huge dictionary, and we could also use those to build graphs.  Pipelots is beautiful.  We don't know what would I use that thing for, but if someone is interested in how they  vary their expenses, like with two, three Python methods, I got this beautiful plot.  And the nicest thing, subqueries.  I've always wondered how to do that in Django.  Now we can do two versions.  So I want to know which deputies have companies.  We cannot do no that for sure, because I don't have any unique identifier for people.  The only way to identify them is by the name, so that's not very accurate.  They can be people with the same names.  But anyway, I wanted to at least know which deputies could have companies in their names.  So I make a query on the companies.  And I want to know which companies have partnerships that belong to natural people.  And I want to know the name of the people that owns these companies.  And I want to compare that name to something else.  What's that something else?  A reference to the query that's outside of it.  That's how we use the alt ref.  So the documentation normally shows you how to use the alt ref with primary keys.  But what's the fun in using primary keys?  We already have the relationships.  We can use reference to any keys.  So I use the reference to a name.  So I have companies, the partnerships they have, the name of their partners.  And I want to compare them with the names that appear in the query that's outside of  it.  And I want to compare the states.  So it's probably, like, as I don't have any unique identifier, I want to know if they belong  to the same state.  So I do the same thing with the state.  I make a reference to the query that's outside of it.  So they match.  And we can annotate that.  And I discovered, I think I forgot to put here, I discovered that one tenth of our deputies  have probably companies in their names.  And that's the query they make.  So it's basically a select on the company's table with the joins to the partnerships.  And we can reference the columns from the inner query and the alpha query.  Then I think, yeah, I think that's it.  Help you get something from it.  Thank you.
DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_0_59.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_60_143.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_144_263.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_264_2291.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_2292_2351.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_2352_4643.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_4644_15539.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_15540_15599.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_15600_16715.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_16716_19031.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_19032_19079.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_19080_21299.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_21300_21527.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_21528_27851.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_27852_28967.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_28968_29423.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_29424_29495.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_29496_30875.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_30876_32759.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_32760_32879.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_32880_34655.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_34656_34859.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_34860_35051.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_35052_35567.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_35568_38255.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_38256_42887.png DjangoCon_2018_-_Making_smarter_queries_with_advanced_ORM_resources_42888_42911.png