views:

105

answers:

3

If I have two tables, Company and Sales, and I want to display both sets of data in a single list, how would I do this on Google App Engine using GQL?

The models are:


class Company(db.Model):

   companyname = db.StringProperty()          
   companyid = db.StringProperty()
   salesperson = db.StringProperty()

class Sales(db.Model):

   companyid = db.StringProperty()

   weeklysales = db.StringProperty()

   monthlysales = db.StringProperty()

The views are:


def company(request): 
  companys = db.GqlQuery("SELECT * FROM Company")   
  sales = db.GqlQuery("SELECT * FROM Sales") 
  template_values = {
    'companys' : companys,
    'sales' : sales
  } 
  return respond(request, 'list', template_values)

List html includes:


{%for company in companys%}  
  {% for sale in sales %}    
    {% ifequal company.companyid sales.companyid %} 

    {{sales.weeklysales}}
    {{sales.monthlysales}}

    {% endifequal %}
     {% endfor %}

          {{company.companyname}}
          {{company.companyid}}
          {{company.salesperson}}

{%endfor%}

Any help would be greatly appreciated.

A: 

You should use a ReferenceProperty in your Sales model:

company = db.ReferenceProperty(Company)

An example on how to iterate through the sales for a given company:

company = db.GqlQuery("SELECT * FROM Company").fetch(1)[0]
for sale in company.sales_set:
    #Do something ...
Nathan
Be warned that this creates a lot more queries (1 plus one per company, instead of 2 total). Chances are it will therefore do a lot more datastore requests. If the number of "sales" objects happens to be about the same as the natural request size when iterating on a query object, then it will make little difference, but I doubt that can be counted on.
Steve Jessop
Not more queries, but rather more datastore operations. Resolving a referenceproperty requires a datastore get, which is much cheaper than a query.
Nick Johnson
Thanks for the clarification - the documentation for reference properties says, "each referenced entity gets a property whose value is a Query that returns all of the entities that refer to it". So I defend my claim that it creates more queries (i.e. Query objects), and I'm saying "datastore requests" when I mean "datastore operations". What kind of datastore operation that Query object issues when used, I leave to the expert :-)
Steve Jessop
Sorry, you're correct. I skimmed and thought the referenceproperty was being resolved in the other direction, which only requires a simple get.
Nick Johnson
A: 
{%for company in companys%}  
  {% for sale in sales %}    
    {% ifequal company.companyid sales.companyid %}

that code is problematic. If you have 200 companies and 1000 sales, you will be running that ifequal statement 200000 times!

In general, if you have more than 1000 sales or companies, this won't work at all, because you can only get 1000 items at a time from the datastore. (and if you're not planning on having more than 1000 items, app engine is probably overkill for your project)

I think your first goal should be to figure out how you want to break your list up into pages. Do you want to display 50 sales per page? or maybe 10 companies per page, along with all their respective sales? Once you decide on that, you can query for just the information you need.

Peter Recore
I'm trying to pull back over 800 companies with 800 sales there is a 1:1 relationship between a sale and a company. I guess this is where the problem is :-) any additional help would be great.
gpjones
Do you really want to display 1600 items to your user at once? Will they be able to make sense of them?
Nick Johnson
A: 

You've said in a comment that there's a 1-1 relationship between sales and companies. So you could get the data in the same order:

def company(request): 
  companys = db.GqlQuery("SELECT * FROM Company ORDER BY companyid").fetch(1000)
  sales = db.GqlQuery("SELECT * FROM Sales ORDER BY companyid").fetch(1000)
  template_values = {
    'companys' : companys,
    'sales' : sales
  } 
  return respond(request, 'list', template_values)

{%for company in companys%}  
    {{sales[forloop.counter0].weeklysales}}
    {{sales[forloop.counter0].monthlysales}}

    {{company.companyname}}
    {{company.companyid}}
    {{company.salesperson}}
{%endfor%}

That's still not a great solution, though. If you're confident that the 1-1 relationship is correct, then I would just have a single entity containing all the information. If nothing else, it saves you worrying about database inconsistency where you create a company, but your attempt to create the corresponding sales data entity fails for some reason.

Steve Jessop