How to access ManyToMany field without loop inside template - django

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 %}

Related

Django BooleanField if statement doesnt return content

For some reason when checking to see if BooleanField post.featured is true I get no output. If I remove that it works fine but not as I intend.
<div class="carousel-inner">
{% for post in object_list%}
{% if post.featured is True %}<!-- This post.featured is BooleanField -->
{% if forloop.first %}
<div class="carousel-item active">
{% else %}
<div class="carousel-item">
{% endif %}
<div class="col-md-6 px-0">
<h1 class="display-4 font-italic">{{ post.title }}</h1>
<p class="lead my-3">{{ post.hook }}</p>
<p class="lead mb-0">
<a href="{% url 'article-details' post.pk %}" class="text-white fw-bold">
Continue reading...
</a>
</p>
</div>
</div>
{% endif %}<!-- and this -->
{% endfor %}
</div>
Heres how it looks like when not checking if post.featured == true:
However, content doesnt render with {% if post.featured is True %} or {% if post.featured %}
Can someone please explain what im doing wrong
EDIT:
Submiting my view:
class Home(ListView):
model = Post
template_name = 'home.html'
You should not filter in the template. This is not only inefficient, but a template is not meant to implement business logic, only rendering logic: a template should not be concerned with what it renders, it should only be concerned with rendering the data in a pleasant way.
You should filter in the ListView:
class Home(ListView):
model = Post
template_name = 'home.html'
queryset = Post.objects.filter(featured=True)
This will filter at the database side.
If you need both the featured items, and the ones that are not featured, you can make two queries:
class Home(ListView):
model = Post
template_name = 'home.html'
queryset = Post.objects.filter(featured=True)
def non_featured(self):
return Post.objects.filter(featured=False)
then you can render the non-featured items with:
{% for post in view.non_featured %}
…
{% endfor %}

How can I print all the products attributes in product.html

This is my models.py
class Product(models.Model):
title = models.CharField(max_length=50)
description = models.TextField(blank=True, null=True)
price = models.FloatField()
summary = models.TextField(blank=False, null=False)
image = models.ImageField(blank=True, null=True)
This is my Django views.py
def products_view(request,*args, **kwargs):
print(Product.objects.values())
myDict = list()
for product in Product.objects.all():
my_product = {
"product_id": product.id,
"product_title":product.title,
"product_description":product.description,
"product_price":product.price,
"product_summary":product.summary,
"product_image":product.image
}
print(my_product)
return render(request, "products.html", my_product)
This my products.html
{% extends 'base.html' %}
{% block content %}
<h1>Products Page</h1>
<ul>
{% for product in my_product %}
<div class="card" style="width: 18rem;">
<img src="{{ product_image }}" class="card-img-top" alt="{{ product_id }}">
<div class="card-body">
<h5 class="card-title">{{ product_title }}</h5>
<p class="card-text">{{ product_description }}</p>
Add to Basket
</div>
</div>
{% endfor %}
</ul>
{% endblock %}
My Product.html page only show the "h1" tag, do not show the Products attributes. How can I achieve this problem?
Products.objects.all() gives me QuerySet and I tried to change into dict. but I cannot print the dict elements' attributes .
Your view should send products through context
def products_view(request,*args, **kwargs):
products = Product.objects.all()
context = {'products': products }
return render(request, "products.html", context)
also in template then you can use
{% for product in products %}
<div class="card" style="width: 18rem;">
<img src="{{ product.image.url}}" class="card-img-top" alt="{{ product.id}}">
<div class="card-body">
<h5 class="card-title">{{ product.title}}</h5>
<p class="card-text">{{ product.description}}</p>
Add to Basket
</div>
</div>
{% endfor %}
Drop the loop in your view function and try something like this below. There is no need to loop through each object in the view - your query has retrieved everything, and it's available to your template when you pass it through as context. Use the template language to access each object's attribute.
def products_view(request,*args, **kwargs):
products = Product.objects.all()
return render(request, "products.html", products)
And in your HTML, do something like:
{% for product in products %}
{{product.title}}
{{product.description}}
{{product.price}}
{{product.summary}}
{{product.image}}
{% endfor %}
This HTML will render weird in my example, but you can see what's going on.
The reason the for loop doesn't print out correctly is because my_product is not an iterable so the template cannot loop over it.
You will need to change this to be an iterable - like a list or queryset.
def products_view(request,*args, **kwargs):
print(Product.objects.values())
my_product = Product.objects.all()
print(my_product)
return render(request, "products.html", {'my_product': my_product})
Now, in the template, you will be able to access the product attributes using dot notation.
{% for product in my_product %}
<div class="card" style="width: 18rem;">
<img src="{{ product.image }}" class="card-img-top" alt="{{ product.id }}">
<div class="card-body">
<h5 class="card-title">{{ product.title }}</h5>
<p class="card-text">{{ product.description }}</p>
Add to Basket
</div>
</div>
{% endfor %}

Django many-to-many field in template and through

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.

Django: the information is not processed for a given condition if

Template tag {% if %} {% endif %} does't work correctly. I need to make the search results on the page appear only after the search query. But for some reason, when the page loads, all existing content appears at once. But after get request filter works correctly.
views.py
def search(request):
place_list = Places.objects.all()
place_filter = PlaceFilter(request.GET, queryset=place_list)
return render(request, 'search/user_list.html', {'filter': place_filter})
html
{% if filter.qs %}
<div class="row">
{% for obj in filter.qs %}
<div class="col-md-3 admin__block">
<div class="cover__wrapper">
<img src="{{ obj.main_photo.url }}" alt="">
<span>#</span>{{ obj.name }}
<p>{{ obj.description }}</p>
</div>
</div>
{% endfor %}
</div>
{% endif %}
filters.py
class PlaceFilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='icontains', widget=forms.TextInput(attrs={
'placeholder': 'Search place', 'class': 'input__search'}))
class Meta:
model = Places
fields = ['name']
FilterSet's qs property returns filterset's queryset. So it's always True.
You can use request.GET in template to check if GET contains any request data and render only filtered data:
{% if request.GET %}
<div class="row">
{% for obj in filter.qs %}
<div class="col-md-3 admin__block">
<div class="cover__wrapper">
<img src="{{ obj.main_photo.url }}" alt="">
<span>#</span>{{ obj.name }}
<p>{{ obj.description }}</p>
</div>
</div>
{% endfor %}
</div>
{% endif %}

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.