Django many-to-many field in template and through - django

In my models there are some manytomany fields. I've been struggling to make them appear in the template. One is a regular ManyToMany field, the other one uses through. The problem is that the amount is not shown. I understand that currently the iteration is only defined for component in pcbuilds.components.all. How to manage the amount in there as well?
models.py:
class Component(models.Model):
name = models.CharField(max_length=100, unique=True,help_text='Component name')
manufacturer = models.IntegerField(blank=True,null=True,choices=MANUFACTURERS)
model = models.CharField(max_length=100,unique=True,blank=True,null=True, help_text='model')
class Tag(models.Model):
title = models.CharField(max_length=100,unique=True,blank=True,null=True, help_text='tagname')
class PCBuilds(models.Model):
title = models.CharField(max_length=50, help_text='PC build title')
components = models.ManyToManyField(Component,help_text='Pick your components from the list or create and add them.',through='Componentlist')
subtitle = models.CharField(blank=True,max_length=80, help_text='Subtitle')
def __str__(self):
return self.title
class Componentlist(models.Model):
component = models.ForeignKey(Component, on_delete=models.CASCADE,related_name='components')
pcbuild = models.ForeignKey(PCBuilds, on_delete=models.CASCADE,related_name='pcbuilds')
amount = models.FloatField(null=True,help_text='amount')
template:
<div class="card-deck">
{% for pcbuilds in object_list|slice:":3" %}
<div class="card" style="width: 18rem;">
<div class="card-header">
<a href="{% url 'pcbuilds_detail' pcbuilds.pk %}">
<span class="font-weight-bold">{{ pcbuilds.title }}</span></a> ยท
<span class="text-muted">{{ pcbuilds.subtitle }}</span>
</div>
<div class="card-body">
<ul>
{% for component in pcbuilds.components.all %}
<li>{{ component.name}}{{ component.manufacturer}}{{ component.model }}{{ componentlist.amount }}</li>
{% endfor %}
</ul>
</div>
<div class="card-footer text-center text-muted">
{% for tag in recipe.tags.all %}
Tags: {{ tag.title }}
{% endfor %}
</div>
</div>
<br />
{% endfor %}

You don't define componentlist there, so the template will ignore it. Normally, you need to follow a relationship, as you did to get component in the first place. But this way there is no access to the through table, as you've already effectively gone past it to get the target table.
Instead you need to follow the relationship from pcbuild to the through table, and from there to the component:
{% for componentlist in pcbuilds.pcbuilds.all %}
<li>{{ componentlist.component.name}}{{ componentlist.component.manufacturer}}{{ componentlist.component.model }}{{ componentlist.amount }}</li>
{% endfor %}
Note, your related names in that through table are strange, which is why I had to use that confusing pcbuilds.pcbuilds.all. The reverse relationship from pcbuild to componentlist should be componentlist_set, which is the default; there shouldn't be any reason to change that.

Related

How to access ManyToMany field without loop inside template

I have the following models defined for my portfolio projects:
class Technology(models.Model):
title = models.CharField(max_length=10)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = 'Technologies'
class Project(models.Model):
title = models.CharField(max_length=100)
description = HTMLField()
technology = models.ManyToManyField(Technology)
image = models.ImageField(upload_to='projects/')
def __str__(self):
return self.title
And I have this in my views.py:
def index(request):
projects = Project.objects.all()
context = {'projects': projects}
return render(request, 'homepage/index.html', context)
And the following piece of HTML to display the projects:
{% for project in projects %}
<div class="col-lg-4 col-md-6 portfolio-item filter-django">
<div class="portfolio-wrap">
<img src="{% static 'homepage/img/portfolio/portfolio-1.jpg' %}" class="img-fluid">
<div class="portfolio-info">
<h4>{{ project.title }}</h4>
<p>FIRST TECHNOLOGY GOES HERE</p>
</div>
</div>
</div>
{% endfor %}
My challenge is the <p>...</p> tag because each project has multiple technologies, but I need to print the first one only so I can't have a for loop here.
I have tried {{ project.technology.[0] }} but that gives me Could not parse the remainder: '[0]' from 'project.technology.[0]'
Please assist.
Django template language (DTL) does not support the various complex syntax that python does. This is done intentionally because one of the aims of DTL is to separate business logic from presentation logic. Hence {{ project.technology.[0] }} does not work. But instead all sorts of lookups can be performed using the . operator itself, meaning you can write {{ project.technology.all.0 }} (Here since all is callable it will get called by the template engine) to achieve the same effect as indexing.
But instead you should use the first [Django docs] method of the queryset instead which will give you either the first object or None (The [0] indexing can raise an exception if the resulting query does not return anything):
{% for project in projects %}
<div class="col-lg-4 col-md-6 portfolio-item filter-django">
<div class="portfolio-wrap">
<img src="{% static 'homepage/img/portfolio/portfolio-1.jpg' %}" class="img-fluid">
<div class="portfolio-info">
<h4>{{ project.title }}</h4>
{% with first_technology=project.technology.first %}
{% if first_technology is not None %}
<p>{{ first_technology }}</p>
{% else %}
<p>No technology</p>
{% endif %}
{% endwith %}
</div>
</div>
</div>
{% endfor %}

why i am not getting a followed_by(followers) entry showing up on my page

i am making a twitter like clone(just to learn how things works in django)
so i am basically trying to set up a many_to_many relationship.
i want to add the functionality of showing 'FOLLOWED_BY' and 'FOLLOWING' to a user profile but list of 'FOLLOWED_BY' is not showing on the page please someone help me!
in the models.py i have define two relationship
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL, related_name='profile', null=True,
blank=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='followed_by', blank=True)
and in the user_detail.html i have the code for how a profile should look like
this is the models.py module:
from django.conf import settings
from django.db import models
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL, related_name='profile',
null=True,
blank=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='followed_by', blank=True)
def __str__(self):
return str(self.following.all().count())
below is the code for user_detail.html file:
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-3 col-xs-12" style="background-color: yellow">
<h1>{{ object.username }}</h1>
<p>Followers: {{ object.followed_by.count }}</p>
</div>
<div class="col-sm-9 col-xs-12">
<h1>Tweets</h1>
{% for tweet in object.tweet_set.all %}
{{ tweet }}<br/>
{% endfor %}
<hr/>
<h1>Following</h1>
{% for user in object.profile.following.all %}
<a href='/{{ user.username }}'>{{ user.username }}</a><br/>
{% empty %}
<h4>Not following any users</h4>
{% endfor %}
<hr/>
<h1>Followed By</h1>
{% for profile in object.profile.followed_by.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not followed by any user</h4>
{% endfor %}
</div>
{% endblock content %}
for user profile i am getting the FOLLOWING field as i want but FOLLOWED_BY field is not showing how can i do that (what changes should i do in my code)??
You defined a following field that points to the user model, not to a Profile. As a result a Profile has no followed_by relation, a User object has.
I think it probably is better to let following point to Profile, like:
class UserProfile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='profile',
null=True,
blank=True
)
following = models.ManyToManyField(
'self',
related_name='followed_by',
symmetrical=False,
blank=True
)
def __str__(self):
return str(self.following.all().count())
Then you can render this like:
<div class="col-sm-3 col-xs-12" style="background-color: yellow">
<h1>{{ object.username }}</h1>
<p>Followers: {{ object.followed_by.count }}</p>
</div>
<div class="col-sm-9 col-xs-12">
<h1>Tweets</h1>
{% for tweet in object.tweet_set.all %}
{{ tweet }}<br/>
{% endfor %}
<hr/>
<h1>Following</h1>
{% for profile in object.profile.following.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not following any users</h4>
{% endfor %}
<hr/>
<h1>Followed By</h1>
{% for profile in object.profile.followed_by.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not followed by any user</h4>
{% endfor %}
</div>
Your code has however some (serious) anti-patterns. The most important one is that you should not write business logic in the template. You should use the view for that. For example you can specify in the view a context like:
context = {
'tweets': object.tweet_set.all()
'followers': object.profile.following.select_related('user').all()
'followed_by': object.profile.followed_by.select_related('user').all()
}
We here can also use a .select_related() [Django-doc] that will boost performance significantly, since now all the users are fetched in the same query.
You also better use the {% url ... %} template tag [Django-doc] to construct queries. So instead of writing:
<a href="/{{ profile.user.username }}">
it is better to construct the query using a reverse lookup like:
<a href="/{% url 'profile_view' username=profile.user.username %}">

Django filter child by parent

I'm trying to filter children by its parent in my templates. Example being I have houses that are displayed and want to display their amenities(children) along with them. When I try and do so each house list all amenities for every house. How would I make is so that I list a house and only its amenities?
Here are my models:
class Home(models.Model):
name = models.CharField(max_length=255)
photo = models.ImageField()
def __str__(self):
return self.name
class Amenities(models.Model):
home = models.ForeignKey(Home)
amenities = models.CharField(max_length=255)
In my views I am trying to filter the child by its parent:
def index(request):
home = Home.objects.filter()
amenities = Amenities.objects.filter(home=home)
return render(request, 'home/home.html', {'home': home, 'amenities': amenities})
In my template I am try and loop through each home and their amenities like so:
{% for house in home %}
<div class="row">
<div class="col-md-6 portfolio-item">
<a href="house1.html">
<img class="img-responsive" src=" media/{{ house.photo }}" alt="">
</a>
<h3>
House
</h3>
<ul>
{% for i in amenities %}
<li>{{ i.amenities }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
thank you
I think you are looking for this
{% for house in home %}
<div class="row">
<div class="col-md-6 portfolio-item">
<a href="house1.html">
<img class="img-responsive" src=" media/{{ house.photo }}" alt="">
</a>
<h3>
House
</h3>
<ul>
{% for i in house.amenities_set.all %}
<li>{{ i.amenities }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}

User entered links display as text in Django

I just finished creating a user commenting system on a social networking app I am building with Django (python version 2.7.8, Django verion 1.6).
Everything is working well with the commenting system, but I have encountered an issue. If a user submits a link to an external site in one of their comments, that link appears as plain text. I would like the user submitted link to automatically be viewed as a link to that other users can click on.
Does anyone know a potential solution to this problem?
models.py
class Comment(models.Model):
#Model that defines the Commenting system
created = models.DateTimeField(editable =False)
author = models.CharField(max_length = 200, editable = False)
body = models.TextField()
item = models.ForeignKey(BucketListItem)
def __unicode__(self):
return self.body
comment-template.html
<h2>Comments:</h2>
<br>
{% if comments %}
{% for comment in comments %}
<div class = "comment-div">
<h5>{% avatar comment.author 40 %}</h5>
<h5> {{comment.author}}</h5>
<h5 class ="timesince">{{ comment.created|timesince}} ago.</h3>
<br>
<br>
<p>{{comment.body}}</p>
{% if comment.author == current_user %}
<span class = "fa fa-close"></span>
{% endif %}
</div>
{% endfor %}
<br>
<hr>
<br>
{% else %}
<p>There are no comments yet. Be the first to add one!</p>
{% endif %}
<h5 class = "leave-comment">Leave a Comment Here: </h5>
<br>
<form action="/bucketlist/item/{{id}}/" method = "post" role = "form">
<div class = "form-group">
{% csrf_token %}
{% for field in form %}
{{ field.errors }}
{{ field }}
<br>
{% endfor %}
<br>
<input type = "submit" value = "Submit" class="btn btn-warning">
</div>
<br>
You should be able to do this using the urlize template tag that Django provides.
<p>{{ comment.body | urlize }}</p>
This should convert any links within the body of the comment to an actual <a> tag.

Access ForeignKey set directly in template in Django

I have this simplified model:
class Item(models.Model):
name = models.CharField(max_length=120)
class ItemImage(models.Model):
image = models.ImageField(upload_to='upload_dir')
item = models.ForeignKey(Item)
An Item can have many ItemImages. I also have a template rendering the following data set from the view:
items = Item.objects.all()
So now I would want to do something like this in the template:
{% for item in items %}
<div>
{{ item.name }}<br>
<img src="{{ item.itemimage_set.all()[0] }}">
</div>
{% endfor %}
But obviously that's not possible. Not from the template directly, at least.
What is the proper way to get the equivalent of the first image inside the template?
{% with item.itemimage_set.all|first as image %}
<img src="{{ image.url }}" />
{% endwith %}
Or you could add a method to your Item model:
def get_first_image(self):
return self.itemimage_set.all()[0]
and then call this method in your template:
{{ item.get_first_image }}
Or you could use:
{{ item.itemimage_set.all.0 }}
and to get the first image's url:
<img src="{{ item.itemimage_set.all.0.url }}">
Though if you need more flexibility (more than one picture in certain cases, etc.) it's probably best to write a little templatetag.
One possible way would be to iterate over all the ItemImages like so:
{% for item in items %}
<div>
{{ item.name }}<br>
{% for image in item.itemimage_set.all %}
<img src="{{ image.image.url }}">
{% endfor %}
</div>
{% endfor %}
This worked for me, use the related_name in your models.
models.py
class Building(models.Model):
address = models.CharField(max_length=200, blank=True, null=True)
city = models.CharField(max_length=200, blank=True, null=True)
class Space(models.Model):
title = models.CharField(max_length=200, blank=True, null=True)
building = models.ForeignKey(Building, related_name="spaces_of_this_building")
buildings.html
{% for space in building.spaces_of_this_building.all %}
{{ space.title }}
{% endfor %}
If you want the first picture from set you can do:
{% for item in item.image_set.all %}
{{if forloop.first }}
<img src="{{ item.url }}">
{% endif %}
{% endfor %}
But i also love Andray solution with 'with'