Search code examples
djangomany-to-manydjango-queryset

Django join through table even if don't match


I'm starting to use Django, but I'm just a beginner. I have a problem with Django Queries, and although I've done a lot of research online, I haven't found any answers that can help me.

Models.py:

class Articles(models.Model):
     ID = models.AutoField(primary_key=True)
     description = models.TextField()
     price = MoneyField(blank=True, null=True, decimal_places=2, max_digits=8, default_currency='EUR')
     class Meta:
        ordering = ('description',)
     def __str__(self):
         return self.description

class Interventions(models.Model):
     ID = models.AutoField(primary_key=True)
     start_date = models.DateField()
     description = models.TextField()
     Articles_ID = models.ManyToManyField(Articles, through="Detail", blank=True)

class Detail(models.Model):
     ID = models.AutoField(primary_key=True)
     article_id = models.ForeignKey(articles, on_delete = models.CASCADE)
     Intervention_ID = models.ForeignKey(Interventions, on_delete = models.CASCADE)
     quantity = models.IntegerField(null=True, blank=True, default='1')

I would like to be able to create a Query that takes all the records of the 'Interventions' model and all the records of the 'Details' table.

The problem is that if an intervention is made, but without having used any article, the intervention itself is not displayed.

How can I do? I tried with prefetch_related but it doesn't seem to work. Heartfelt thanks to everyone.

Example output Table: enter image description here


Solution

  • You are essentially looking for a left join, you can uses .values() to specify all the fields.

    Interventions.objects.values(
        "ID", "start_date", "description", "Articles_ID__description", "detail__quantity"
    )
    

    The query is equivalent to this sql query

    SELECT "interventions"."id",
           "interventions"."start_date",
           "interventions"."description",
           "articles"."description",
           "detail"."quantity"
    FROM   "interventions"
           LEFT OUTER JOIN "detail"
                        ON ( "interventions"."id" =
                           "detail"."intervention_id_id" )
           LEFT OUTER JOIN "articles"
                        ON ( "detail"."article_id_id" = "articles"."id" ) 
    

    Output :

    <QuerySet [
    {'ID': 4, 'start_date': datetime.date(2023, 6, 8), 'description': 'Try number one', 'Articles_ID__description': 'Article1', 'detail__quantity': 3},
    {'ID': 4, 'start_date': datetime.date(2023, 6, 8), 'description': 'Try number one', 'Articles_ID__description': 'Article2', 'detail__quantity': 8},
    {'ID': 5, 'start_date': datetime.date(2023, 6, 8), 'description': 'try number two', 'Articles_ID__description': None, 'detail__quantity': None},
    {'ID': 6, 'start_date': datetime.date(2023, 6, 8), 'description': 'try number three', 'Articles_ID__description': 'Article2', 'detail__quantity': 10},
    {'ID': 6, 'start_date': datetime.date(2023, 6, 8), 'description': 'try number three', 'Articles_ID__description': 'Article4', 'detail__quantity': 27},
    {'ID': 6, 'start_date': datetime.date(2023, 6, 8), 'description': 'try number three', 'Articles_ID__description': 'Article32', 'detail__quantity': 1}
    ]>  
    

    A downside of this approach is that the Joins are not lazy.
    Also use F model if you want to rename the fields