Home > front end >  Avoid duplication in Django for loop with a many to many relationship
Avoid duplication in Django for loop with a many to many relationship

Time:06-16

My goal is to list the tag_titles for each note created.

I am having issues because the tag_title and note are linked through a Many to Many relationship.

How I would normally do this is:

{% for note_tag in note_tags %}
    {% if note_tag.note == note %}
        {{note_tag.tag_title}}
    {% endif %}
{% endfor %}

However, because tag_title has a many to many relationship with NoteModel, note_tag.note gives out notes.NoteModel.None

My models.py are as follows:


class NoteModel(models.Model):
    note = models.CharField(
        max_length = 5000,
    )

    note_title = models.CharField(
        max_length = 500,
        blank = False,
        null = True,
    )
    
    project = models.ForeignKey(
        IndividualProject,
        on_delete=models.CASCADE,
        related_name = "note_projects",
        blank = False,
        null = True,
    )

    tag = models.ManyToManyField(
        'NoteTagModel',
        related_name="tags",
        blank= False,
        through = "TaggedModel"
    )

    def __str__(self):
        return f"{self.note_title}"

class NoteTagModel(models.Model):
    note = models.ManyToManyField(
        'NoteModel',
        related_name="tag_notes",
        blank= False,
        through = "TaggedModel"
    )

    tag_title = models.CharField(
        max_length = 200,
        default = "General",
    )

    def __str__(self):
        return f"{self.tag_title}"

class TaggedModel(models.Model):
    note = models.ForeignKey(NoteModel, on_delete = models.CASCADE)
    tag = models.ForeignKey(NoteTagModel, on_delete = models.CASCADE)

    def __str__(self):
        return f"{self.note} | {self.tag}"

class TagCommentaryModel(models.Model):
    tag_title = models.ForeignKey(
        NoteTagModel,
        on_delete=models.CASCADE,
        related_name="tag",
        blank = False,
        null = True,
    )

    tag_commentary = models.CharField(
        max_length = 5000
    )

    def __str__(self):
        return f"Tag: {self.tag_title} | Tag Commentary: {self.tag_commentary}"

The context in my views.py is as follows:



    context = {
        'note_form' : note_form,
        'notes' : NoteModel.objects.filter(project = obj),
        'notes_tag' : NoteTagModel.objects.all(),
        'commentary' : TagCommentaryModel.objects.all(),
        'projects' : IndividualProject.objects.all(),
        'specific_project' :  IndividualProject.objects.get(id=id),
        'obj' : IndividualProject.objects.get(id=id),
        'tagged' : TaggedModel.objects.all(),
        
    }


    return render(request, "notes/specific-note.html", context)

My django template is as follows:

<div class = note_overview_container>
    {% for note in notes %} 
        <div class = individual_note_container> 
                <div class = note_title>
                {{ note.note_title }}
                </div>
                {% for note_tag in notes_tag %}
                    <div class = tag_title>
                    {{ note_tag.tag_title }}
                    </div>
                <ul class = tag_commentary>
                {% for commentary in commentary %}
                        {% if note_tag == commentary.tag_title %}
                            {% if note == commentary.note %}
                            <li>{{ commentary.tag_commentary }}</li>
                            {% endif %}
                        {% endif %}
                {% endfor %}
                </ul>
                {% endfor %}
        </div>
    {% endfor %}    
</div>

Any help would be greatly appreciated

CodePudding user response:

You can simplify your models a lot in my opinion. You point from tag in NoteModel to NoteTagModel and vice versa & I don’t think it is needed. Also TaggedModel seems superfluous. At least if there isn't a specific reason to link it like you did.

class NoteModel(models.Model):
    note = models.CharField(max_length = 5000)
    note_title = models.CharField(max_length=500, blank=False, null=True)
    project = models.ForeignKey(IndividualProject, on_delete=models.CASCADE, related_name="note_projects", blank=False, null=True)
    # plural
    tags = models.ManyToManyField(NoteTagModel, related_name="tags", blank=False)
    
    def __str__(self):
        return self.note_title
    
class NoteTagModel(models.Model):
    # usually there is no need to point here again to the same model thats already pointing to this one
    tag_title = models.CharField(max_length=200, default="General")
    
    def __str__(self):
        return self.tag_title

If you want to access reverse from NoteTagModel to NoteModel, you can do the following eg:

for tag in NoteTagModel.objects.all():
    for note in tag.nodemodel_set.all():
        print(note.note_title)
        …

(I put the html classes in double quotes)

<div >
    {% for note in notes %}
        <div > 
            <div >
                {{ note.note_title }}
            </div>

            <!-- loop over all the tags related to the note -->
            {% for tag in note.tags.all %}
                <div class=”tag_title”>
                    {{ tag.tag_title }}
                </div>

                <ul >
                    <!-- commentary in commentary shouldnt be named the same -->
                    {% for comment in commentary %}
                        {% if note_tag == comment.tag_title %}
                            {% if note == comment.note %}
                                <li>{{ comment.tag_commentary }}</li>
                            {% endif %}
                        {% endif %}
                    {% endfor %}
                </ul>
            {% endfor %}
        </div>
    {% endfor %}    
</div>
  • Related