PROJECTS NOTES HOME

Through attribute in django-orm

This is the basic m2m relationship:

from django.db import models

class Vessel(models.Model):
    name = models.CharField(max_length=255)
    stock = models.IntegerField(default=0)

    def __str__(self) -> str:
        return self.name


class Deal(models.Model):
    collateral = models.ManyToManyField(
        Vessel,
        blank=True,
    )
    register_date = models.DateTimeField(
        null=True,
        blank=True
    )
    has_new = models.BooleanField(
        default=True,
        null=True
    )

    def __str__(self) -> str:
        return f"#{self.pk}"

Query the above created table in Django shell like so:

In [9]: deal = Deal.objects.last()
In [10]: collateral_vessels = deal.collateral.all()
In [11]: print(collateral_vessels)
<QuerySet [<Vessel: Two>, <Vessel: Three>]>

In the case above we CAN NOT control the app_deal_collateral table. Which is sad… let's say we want to control it, we then use the through attribute.

We will inform Django not to use the default table like above, but in this case we want to use OUR OWN table.

We also infom that we WILL NOT want to control the collaterals over the DEAL model/admin page, but over the Collaterals page/admin page:

Will do that with through attribute:

from django.db import models


class Vessel(models.Model):
    name = models.CharField(max_length=255)
    stock = models.IntegerField(default=0)

    def __str__(self) -> str:
        return self.name


class Deal(models.Model):
    collaterals = models.ManyToManyField(
        Vessel,
        blank=True,
        through='Collateral',
    )
    register_date = models.DateTimeField(
        null=True,
        blank=True
    )
    has_new = models.BooleanField(
        default=True,
        null=True
    )

    def __str__(self) -> str:
        return f"#{self.pk}"


class Collateral(models.Model):
    vessel = models.ForeignKey(
        Vessel,
        on_delete=models.DO_NOTHING,
    )
    deal = models.ForeignKey(
        Deal,
        on_delete=models.DO_NOTHING,
        null=True
    )
    quant = models.PositiveIntegerField(
        null=True,
    )
    charfield = models.CharField(max_length=255)
    is_new = models.BooleanField(
        default=True,
        blank=True
    )

    def __str__(self) -> str:
        return str(self.part)

Db structure then:

App_collateral then becomes like a control center from which you can control all the relationships, etc.

Deal admin page, we can not see the collaterals(vessels) here.

What we can see is another model in the admin called collaterals.

This is good if you actually want to separately create Deal and Vessel objects and then connect them in one in Collateral model.

Query like such(using shell_plus with print sql):

But what if you want to modify the collaterals from the Deal model?

Answer - inline

from django.contrib import admin
from .models import *

class CollateralInline(admin.TabularInline):
    model = Collateral
    extra = 1

class DealAdmin(admin.ModelAdmin):
    inlines = [CollateralInline]

admin.site.register(Deal, DealAdmin)
admin.site.register(Vessel)
admin.site.register(Collateral)