im brand new to Django and attempting to make my first ECommerce site. I'm attempting to get a list of products from a .JSON file to display in my products.html template. The products are in place, as shown in the Django admin portal, productsis definied with in my views.py file and i've done loaddata but nothing comes through. I've added some screenshots and code below to further explain.
Views.py Code:
from django.shortcuts import render
from .models import Product
def all_products(request):
products = Product.objects.all()
context = {
'products': products,
}
return render(request, 'products/products.html', context)
products.html Template:
{% extends "base.html" %}
{% load static %}
{% block page_header %}
<div class="container header-container">
<div class="row">
<div class="col"></div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col">
{{ products }}
</div>
</div>
</div>
{% endblock %}
My folder structure is correct but I'll post it here just incase:
Any help would be great as i've been stuck with this for a while now. Thanks!
models.py:*
class Product(models.Model):
category = models.ForeignKey('category', null=True, blank=True, on_delete=models.SET_NULL)
sku = models.CharField(max_length=254, null=True, blank=True)
name = models.CharField(max_length=254)
description = models.TextField()
price = models.DecimalField(max_digits=6, decimal_places=2)
rating = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
image_url = models.URLField(null=True, blank=True)
image = models.ImageField(null=True, blank=True)
def __str__(self):
return self.name
You need to iterate through the object you are passing to your template.
For example,
{% for product in products %}
<img src={{ product.src }} />
<h3>{{ product.title }}</h3>
<p>{{ product.desc }}</p>
{% endfor %}
You have done most of the thing right so far. One thing that causes the issue is the template didn't iterate all the products.
In the views.py, Product.objects.all() returns a query set which you can imagine it is a list that contains many product instances.
So when the products, which is Product.objects.all(), is passed to template, you will need to use a loop to iterate the instances inside the products.
I assume your product model contains the following three fields(you can replace them with the actual field inside your product model later in the example I provide).
name
description
qty
Now, in Django template, you can replace the {{ products }} with the following code.
<table>
<thead>
<tr>
<th scope="col" class="text-nowrap"><span>Product</span></th>
<th scope="col" class="text-nowrap"><span>Description</span></th>
<th scope="col" class="text-nowrap"><span>Qty</span></th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td><span>{{ product.name }}</span></td><!-- Replace the name with your actual field -->
<td><span>{{ product.desciption }}</span></td><!-- Replace the desciption with your actual field -->
<td><span>{{ product.qty }}</span></td><!-- Replace the qty with your actual field -->
</tr>
{% endfor %}
</tbody>
</table>
With the above example, the most important part is
{% for product in products %}
<tr>
<td><span>{{ product.name }}</span></td>
<td><span>{{ product.desciption }}</span></td>
<td><span>{{ product.qty }}</span></td>
</tr>
{% endfor %}
It is similar to the following python code.
for product in products:
print(product.name)
print(product.desciption)
print(product.qty)
And as a result, now the template should show the product on a table.
Related
I have a model called Section and a model called SectionInfo. Section has one field and that's name. A user will create a section by giving it a name. SectionInfo has a foreign key section_name which links it to the Section model. I have a few other fields inside SectionInfo such as things like detail, date_created, etc. What I am trying to accomplish here is using a for loop to display section names that have already been created inside or above some cards that I have set up in an html template. Then I want to use a nested loop to gather the data that is tied to that section name which is what the user inputs in for the SectionInfo model. I am able to display the section names correctly, but the issue im having is in the loop inside that loop. The same detail link is being displayed in every section that was made. Information should only be displayed in the chosen section for which it was made.
Here is a bit of code to help you understand what I am trying to accomplish.
Template file:
{% extends "base.html" %}
{% load bootstrap5 %}
{% load crispy_forms_tags %}
{% load static %}
{% block content %}
{% for i in object %}
{{ i.name}}
<div class="row">
<div class="col-sm-4">
<div class="card">
<div class="card-body">
{% for ii in object_2 %}
Details
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock content %}
Here is what I am getting displayed
whats being displayed
Here is the view that's being used
def section_info_detail(request, pk):
object = get_object_or_404(Section, id=pk)
object_2 = get_object_or_404(SectionInfo, id=pk)
context = {
'object': object,
'object_2':object_2,
}
return render(request, 'manufacturing/section_info_detail.html', context)
Models.py:
class Section(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class SectionInfo(models.Model):
section_name = models.ForeignKey(Section, on_delete=models.CASCADE)
detail = models.CharField(max_length=50)
text = models.TextField(max_length=2000)
date_created = models.DateField('Date created', auto_now_add=True)
date_updated = models.DateField('Date updated', auto_now=True)
Forms.py:
class SectionNameForm(forms.ModelForm):
class Meta:
model = Section
fields = [
'name',
]
class SectionInfoForm(forms.ModelForm):
class Meta:
model = SectionInfo
fields = [
'section_name',
'detail',
'text',
]
If any other information is needed, do tell.
<div class="row">
{% for object in object_list %}
<div class="col-sm-4">
<div class="card">
<div class="card-header">
<h1><strong>{{ object.name }}</strong></h1>
<hr>
</div>
<div class="card-body">
<div class="row">
{% for info in object.section_infos.all %}
<ul id="list">
<li>{{ info.date_created }}</li> |
<li>{{ info.detail|truncatechars:20 }}</li>
<hr>
</ul>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
This did it for me. So basically what my problem was I was not drilling down far enough to get the data that I needed to be displayed from the template. Also I had changed some things up in my models and had created a manyTomany and through field
I write a twitter-like app in django,
I've got two models:
class Tweet(models.Model):
content = models.CharField(max_length=140)
creation_date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Replies(models.Model):
reply = models.CharField(max_length=140)
tweet = models.ForeignKey(Tweet, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.reply
And the fragment of template:
{% block content %}
{% for t in tweets %}
<table class="table1">
<br>
<tr>
<td>
#{{t.user}}
<br> <br>
{{t.content}}
<br> <br>
{{t.creation_date}}
</td>
#######
#######
</tr>
</table>
{% endfor %}
{% endblock %}
Between '####' i would like to have all the replies to the specific tweet, how can i do that?
Thanks in advance
something like this should do the trick:
{% block content %}
{% for t in tweets %}
<table class="table1">
<br>
<tr>
<td>
#{{t.user}}
<br> <br>
{{t.content}}
<br> <br>
{{t.creation_date}}
</td>
{% for reply in t.replies_set.all %}
{{reply.user}} {{reply.reply}}
{% endfor %}
</tr>
</table>
{% endfor %}
{% endblock %}
Here is a link to the documentation that explains many to one relationships in detail https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_one/
#Steffen - it works, however you need to remove '()' at the end of
for reply in t.replies_set.all()
I have two models shown below
class Services(models.Model):
name = models.CharField(max_length=200, null=True)
price = models.FloatField(null=True)
service_sku = models.CharField(max_length=200, null=True)
class Order(models.Model):
customer = models.ForeignKey(Customer, null=True, on_delete = models.SET_NULL)
service = models.ForeignKey(Service, null=True, on_delete = models.SET_NULL)
I have created a function-based view based on the service model where I want to render a template displaying a list of field information from each instance from the service model.
See below for my views and template
views.py
def service_list(request):
service_list = Service.objects.all().order_by('service_sku')
context = {'service_list':service_list}
return render(request, 'accounts/service_list.html',context)
template
<div class="card card-body">
<table class="table">
<tr>
<th>SKU</th>
<th>Service Name</th>
<th>Number of Ordered</th>
</tr>
{% for i in service_list %}
<tr>
<td>{{i.service_sku}}</td>
<td>{{i.name}} </td>
<td></td>
</tr>
{% endfor %}
I want to display the number of orders of each instance, considering that the order model has a foreign key from the service model.
The query set would have to be like the below shown.
Order.objects.all().filter(service__id =3).count()
Problem is that I must specify which id of that model would be for that queryset to work.
I’ve tried to put the queryset format below within the template
{{Order.objects.all().filter(service__id=i.id).count()}
In the template it would look like how it does below
template
<div class="card card-body">
<table class="table">
<tr>
<th>SKU</th>
<th>Service Name</th>
<th>Number of Ordered</th>
</tr>
{% for i in service_list %}
<tr>
<td>{{i.service_sku}}</td>
<td>{{i.name}} </td>
<td>{{Order.objects.all().filter(service__id=i.id).count()}}</td>
</tr>
{% endfor %}
The above implementation does not work.
Which method is ideal for this situation?
You can not call methods (with parameters) in a Django template. The templates are deliberately restricted to prevent people from writing business logic in the templates.
You can .annotate(…) [Django-doc] the queryset to fetch the number of related Orders:
from django.db.models import Count
def service_list(request):
service_list = Service.objects.annotate(
norders=Count('order')
).order_by('service_sku')
context = {'service_list':service_list}
return render(request, 'accounts/service_list.html',context)
In the template you can then render this with:
{% for i in service_list %}
<tr>
<td>{{ i.service_sku }}</td>
<td>{{ i.name }} </td>
<td>{{ i.norders }}</td>
</tr>
{% endfor %}
My problem is the nested loop of my template... I use it to check if my model sitio.id_sitio has an equivalent ID with another model comprobante.id_sitio with a Foreign Key and then print A if one result is found and B if none
The conditional if work fine but I don't want the nested loop print multiple times.
If one result is found I want to break the cycle and print the HTML only one time, like Checkouts
Else if the result doesn't exist in the records at the end of the for I want to print <p>No payments</p>
I don't know if I have to write a query in the views.py or if I have to do something else in the templates...
Is there a correct way to do this?
This is my code:
Models.py
class Comprobante(models.Model):
id_sitio = models.ForeignKey('Sitio', models.DO_NOTHING, db_column='id_sitio', blank=True, null=True)
class Sitio(models.Model):
id_sitio = models.IntegerField(primary_key=True)
sitio = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return self.sitio
Views.py
def topsitios(request):
sitio = Sitio.objects.all()[0:100]
comprobante = Comprobante.objects.all()[0:100]
context = {'sitio': sitio, 'comprobante': comprobante}
return render(request, "sitio_ptc/topsitios.html", context)
Template.html
{% block content %}
{% for s in sitio %}
<tr>
<th scope="row"> {{ forloop.counter }}</th>
<td> {{ s.sitio }} </td>
<td>
{% for c in comprobante %}
{% if s.id_sitio == c.id_sitio_id %}
Checkouts
{% else %}
<p>no payments</p>
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
{% endblock %}
The correct way of doing this is to use Django ORM to access a model's relationship. In the for-loop of each sitio, you can check if any related comprobante exists by {% if s.comprobante_set.all %}
For better performance, you would also need to prefetch related comprobante records by Sitio.objects.prefetch_related('comprobante_set').all(). This way you won't suffer from N+1 query problem.
Quick solution would be:
{% if c.id_sitio_id == s.id_sitio %}
Because Comprobante.id_sitio is a foreign key related to a model with a custom primary key, so c.id_sitio_id (_id is a suffix added by Django that keeps the value of primary key, that is usually id-field in most cases) stores the value of Sitio.id_sitio.
But I would refactor your code to make it clearer and more flexible like this:
# models.py
class Comprobante(models.Model):
sitio = models.ForeignKey('Sitio', on_delete=models.SET_NULL, blank=True, null=True)
class Sitio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
# in the template
...
{% if s.id == c.sitio_id %}
...
You can use the related name attribute in Comprobanteclass
class Comprobante(models.Model):
id_sitio = models.ForeignKey(
'Sitio',
models.DO_NOTHING,
related_name='comprobantes',
db_column='id_sitio', blank=True, null=True)
once a related_name is set, you can check with it in your template
{% block content %}
{% for s in sitio %}
<tr>
<th scope="row"> {{ forloop.counter }}</th>
<td> {{ s.sitio }} </td>
<td>
{% if s.comprobantes %}
{# Hay un comprobante para el sitio #}
Checkouts
{% else %}
<p>no payments</p>
{% endif %}
</td>
</tr>
{% endfor %}
{% endblock %}
Hope this helps
I am building a website in Django where messages have tags. Below you can see my models.py
class Message(models.Model):
message_id = models.BigIntegerField(primary_key=True)
user = models.ForeignKey(User)
title = models.CharField(max_length=50)
tags = models.ManyToManyField(Tag, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True)
def get_tags(self, tag_id):
return self.message.filter(tags__tag_id=tag_id)
class Tag(models.Model):
tag_id = models.BigIntegerField(primary_key=True)
tag_name = models.CharField(max_length=100)
date_created = models.DateTimeField(auto_now_add=True)
Here's my views.py as well.
popular_tags = Tag.objects.filter(messages__date_created__gt=now).annotate(num=Count('messages')).order_by('-num')[:25]
What I am trying to do here is that I want to show tags with the most number of messages. After I do that, I want the tags to show which messages have that tag. So I know I can't do query filtering in template, and that's why I added get_tags under the Message model. So with that I've tried writing the right template, but I am not sure what went wrong with it.
{% for q in popular_tags %}
<tr>
<td align="left"><h5>{{ q.tag_name }}</h5> </td>
{% for m in message.get_tags %}
<td align="left"><h5>{{ m.title }} </h5></td>
{% endfor %}
</tr>
{% endfor %}
I am not getting any errors, it's just that no messages are being shown while the tags are correctly displayed. Any ideas how and where I should fix?
You don't need anything special, Django's ORM takes care of it, cf https://docs.djangoproject.com/en/1.6/ref/models/relations/#related-objects-reference
The following should work:
{% for q in popular_tags %}
<tr>
<td align="left"><h5>{{ q.tag_name }}</h5> </td>
{% for m in q.message_set.all %}
<td align="left"><h5>{{ m.title }} </h5></td>
{% endfor %}
</tr>
{% endfor %}
You should add a function within Tag rather than Message:
class Tag(models.Model):
tag_id = models.BigIntegerField(primary_key=True)
tag_name = models.CharField(max_length=100)
date_created = models.DateTimeField(auto_now_add=True)
def get_msgs(self):
return Message.objects.filter(tags__tag_id=tag_id)
Hence, the template should be:
{% for q in popular_tags %}
<tr>
<td align="left"><h5>{{ q.tag_name }}</h5> </td>
{% for m in q.get_msgs %}
<td align="left"><h5>{{ m.title }} </h5></td>
{% endfor %}
</tr>
{% endfor %}