0

I have this code from Kohana (it's easily understandable) and I want to convert this to Django ORM given the ff. models:

class Item(models.Model):
    glasses = models.ManyToManyField(Glass, through="ItemGlass")
    collection = models.ForeignKey(Collection)

class ItemGlass(models.Model):
    glass = models.ForeignKey(Glass)
    item = models.ForeignKey(Item)

class Collection(models.Model):
    name = models.CharField(max_length=100)

class Code(models.Model):
    code = models.CharField(max_length=30)
    description = models.TextField()

class Glass(models.Model):
    collection = models.ForeignKey(Collection)
    code = models.ForeignKey(Code)

and the php query (uses Kohana's Database Lib)

$this->select(
                array('cd.id', 'id'), 
                array('cd.description','description'),
                array('COUNT(DISTINCT("items.id"))', 'count')
            )
                    ->from('items')
            ->join(array('collections', 'c'))
            ->on('c.id', '=', 'items.collection_id')
            ->join(array('glasses', 'g'))
            ->on('g.collection_id', '=', 'c.id')
            ->join(array('code', 'cd'))
            ->on('cd.id', '=', 'g.code_id')
            ->where('items.discontinued', '=', FALSE)
            ->group_by('cd.id');

NOTE: the "array" clause you see is translated as

"SELECT cd.id AS id, cd.description AS description, COUNT(DISTINCT(items.id) AS count"

The thing is how do I do it? I can't successfully use select_related to join multiple tables in this case, and I can't find a good "filter trick" for the query. Any ideas?

EDIT: I am considering doing it in plain SQL, but I would prefer to avoid it if a Django ORM query can be done :)

2
  • Why the ItemGlass model? Are the more attributes on it that you left out for simplification? Commented Jul 5, 2012 at 9:27
  • ItemGLass is a "through" table, storing additional data. some attributes are left out for simplification. Commented Jul 6, 2012 at 6:13

2 Answers 2

2

I have come up with this after an hour of "head banging":

glasses = Code.objects.filter(glass__collection__item__discontinued=False)\
        .values('id', 'description')\
        .annotate(count=Count('glass__collection__item__id', distinct=True))
Sign up to request clarification or add additional context in comments.

4 Comments

You are aware that this makes glasses a QuerySet containing Code objects.
this was copied from a PHP code, the variable naming must be wrong but I'll keep it as is to maintain that this PHP query translates to this django query
This just illustrates the power of the django queryset API does it not?
NB you might consider gradually moving away from using \ for line continuation -- it's not officially recommended due to the possibility of introducing subtle problems, there are better alternatives (here, you could rely on the implicit line continuation from parentheses) and in fact this feature was almost removed from Python entirely!
0

Definitely don't use SQL, it's a simple query which should be no problem with the ORM. Use something like:

Code.objects.filter(glass__item__discontinued=False) \
    .annotate(count=models.Count('glass__item__id'))

You can add a .values(...) to the end of it if you only want to retrieve specific columns, as in your php example.

2 Comments

aggregate() is used to aggregate annotations.. annotate() should be used here.
Your Count() call should have distinct=True as well

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.