Search code examples
djangotemplatesmodels

many to many relationship in django and templates


I have three models: Zone, Stage and ZoneStage with ManyToMany relationship.

the whole point behind is that I have multiple Zone and each Zone has multiple Stages. For each Zone for each Stage I want to display certain VALUE and this is why I create ZoneStage model.

I do not know how to display values as per pictures below. I thought I could do it via:

e.g. for objectz in zone: for objects in stage: for objectzs in zone_stage:

and here some conditions

or

e.g. to try with filters ?

Although, I need some guidance and direction what to read about, how to do it etc and I would appreciate any help with any feedback that can make my life easier to solve this problem.

This is what I get:

enter image description here

My Models:

class Zone(models.Model):
    ZONE_N = (
        ('Z2','Z2'),
        ('Z4','Z4'),
        ('Z6.1','Z6.1'),
        ('Z9LH','Z9LH'),
        )
    zone_name = models.CharField(max_length=5, choices=ZONE_N, primary_key=True)
    zone_number = models.CharField(max_length=5,blank=True)


    class Meta:
        ordering = ('zone_name',)      

    def __unicode__(self):
        return self.zone_name    

class Stage(models.Model):
    STAGE_N = (
        ('S1','S1'),
        ('S2','S2'),
        ('S3','S3'),
        ('S4','S4'),
        )    
    stage_number = models.CharField(max_length=3, choices=STAGE_N, primary_key=True)
    stage_name = models.CharField(max_length=50)
    zones = models.ManyToManyField('Zone', through='ZoneStage', related_name='status')

    class Meta:
        ordering = ('stage_number',)      

    def __unicode__(self):
        return self.stage_number       

class ZoneStage(models.Model):
    zone = models.ForeignKey(Zone)
    stage = models.ForeignKey(Stage)
    value = models.CharField(max_length=10)

    class Meta:
        ordering = ('zone',)          

    def __unicode__(self):
        return '%s %s' % (self.zone, self.stage)

My Views:

def index(request):
    zs = ZoneStage.objects.all()
    z = Zone.objects.all()
    s = Stage.objects.all()
    context = {'zone_stage': zs, 'zone': z, 'stage': s}

    return render(request, 'index.html', context)

My Template:

<table border="1">
<tr>
    <td></td>
    <td></td>

    {% for object in zone %}
    <td>{{ object.zone_name }}</td>
    {% endfor %}
</tr>

{% for stages in stage %}
<tr>
<td>{{ stages.stage_number }}</td>
<td>{{ stages.stage_name }}</td>
        {% for values in stages.zonestage_set.all|dictsort:"zone.zone_name" %}
        <td>{{ values.value}}</td>
        {% endfor %}
</tr>
{% endfor %}
</table>

Solution

  • Since your ZoneStage is an intermediate model, the zoneid and stageid should be ForeignKey, not ManyToMany. See example here:

    https://docs.djangoproject.com/en/1.8/topics/db/models/#intermediary-manytomany

    If you look at your structure, you'll see that it makes sense. Since your ZoneStage are intersection points, they can't be many to many. Each ZoneStage will have one unique Zone and one unique Stage.

    Once this is done, you could access the values this way for example:

    <table border="1">
    <tr>
        <td></td>
        <td>{{ object.zone }}</td>
        {% for object in zone|dicsort:"zone_name" %}
            <td>{{ object.zone_name }}</td>
        {% endfor %}
        </tr>
    {% for objectS in stage %}
        <tr>
            <td>{{ objectS.stage_no }}</td>
            <td>{{ objectS.stage_name }}</td>
                {% for zonestages in objectS.zonestage_set.all|dictsort:"zoneid.zone_name" %}
                    <td>{{ zonestages.zonestage}}</td>
                {% endfor %}
        </tr>
    {% endfor %}
    </table>
    

    EDIT:

    Previous solution works if every intersection has a value. If not one other way to do it would be like this:

    <table border="1">
        <tr>
            <td></td>
            <td>{{ object.zone }}</td>
            {% for object in zone|dicsort:"zone_name" %}
                <td>{{ object.zone_name }}</td>
            {% endfor %}
            </tr>
        {% for objectS in stage %}
            <tr>
                <td>{{ objectS.stage_no }}</td>
                <td>{{ objectS.stage_name }}</td>
                    {% for zones in zone|dicsort:"zone_name" %}
                        <td>
                        {% for zonestages in objectS.zonestage_set.all %}
                        {% if zonestages.zone == zones %} {{ zonestages.zonestage}}{% endif %}
                        {% endfor %}
                        </td>
                    {% endfor %}
            </tr>
        {% endfor %}
        </table>