Rendering models in template which are related themselves - python

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()

Related

Prompt Asking For Password When Pressing A Button

I would like to add a prompt everytime someone tries to press the button to 'Delete' or 'Download' a file (in my project, you can upload files with passwords, so that if someone wants to delete/download the file, he needs to enter a password).
The password itself is saved in the models.py using django - how would i be able to to so in my code (listing my viwes.py to the html code, urls.py, models.py, forms.py, HTML CODE Itself).
#login_required(login_url='login')
def My_Files(request):
files = File.objects.all()
return render(request, 'home/My Files.html', {"files" : files})
path("MyFiles", views.My_Files, name="my_files"),
path("MyFiles/<int:pk>/", views.delete_file, name="delete_file"),
{% extends 'layouts/base.html' %}
{% load static %}
{% block title %} {% endblock title %}
<!-- Specific CSS goes HERE -->
{% block stylesheets %}{% endblock stylesheets %}
{% block content %}
{% include "includes/footer.html" %}
<h2>My Files</h2>
<table class="table">
<thead>
<tr>
<th>File Name</th>
<th>File Description</th>
<th>Uploaded By</th>
<th>Download</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for file in files %}
<tr>
<td> {{ file.File_Name }}</td>
<td> {{ file.File_Type }}</td>
<td> {{ file.Uploaded_By }}</td>
<td>
<a href="{{ file.File.url }}" class="btn btn-primary btn-sm" target="_blank">
Download File
</a>
</td>
<td>
<form method="post" action="{% url 'delete_file' file.pk %}">
{% csrf_token %}
<button type="submit" class="btn btn-dange btn-sm">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}
<!-- Specific JS goes HERE -->
{% block javascripts %}
<script>
}
},
},
},
});
</script>
{% endblock javascripts %}
class File(models.Model):
File_Name = models.CharField(max_length=200, null=True)
File_Type = models.CharField(max_length=30, null=True)
Uploaded_By = models.CharField(max_length=90,default=None,blank=True, null=True)
Password = models.CharField(max_length=100, null=True)
File = models.FileField(upload_to='Files')
def __str__(self):
return self.File_Name
def delete(self, *args, **kwargs):
self.File.delete()
super().delete(*args, **kwargs)
def UploadedBy(self,username):
self.File.Uploaded_By = username
# Create your forms here.
class FileForm(forms.ModelForm):
class Meta:
model= File
fields= ["File_Name", "File_Type","Uploaded_By","Password","File"]
You should define a form with a field for entering password which validates against the password of the specific file, then add your delete or download logic after form.is_valid() is called

Django can't upload file

I'm doing a django app with a form using image file upload. I have a default image in case user doesnt choose an image. But when I save the form, my image isn't saved in my files. Any idea ?
Here's my code :
First my views.py
class AnimalCreateView(generic.CreateView):
model = Animal
form_class = AnimalForm
template_name = 'myApp/animal_create.html'
success_url = reverse_lazy('animal:index')
Then my models.py
class Animal(models.Model):
name= models.CharField()
animal_photo= models.ImageField(upload_to="images/",default="images/noPhoto.svg", null=False, blank=True)
def __str__(self):
return f'{self.name}'
And my animal_create html :
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-lg-8 offset-lg-2">
<h4>My anmials</h4>
<table class="table">
<thead>
<tr>
<th> </th>
<th>Name</th>
<th>Photo</th>
</tr>
</thead>
<tbody>
{% for animal in animal_list%}
<tr>
<td>{{animal.name}}</td>
<p> {{ animal.photo.url }} </p> #check url file
<td class="picture-thumb">
{% if animal.photo.url %}
<img src="{{ animal.photo.url}}"" />
{% else %}
<img src="{% static 'images/noPhoto.svg' %}" />
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock content %}
When I save my file and then check my html or django admin page, all the rows of animal_photo are using the default file...
Where are you using form from context? And also, to upload an images(files) you need to set
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
<form method="post" action="/foo/">
{% endif %}
https://docs.djangoproject.com/en/3.1/ref/forms/api/#testing-for-multipart-forms

Django app not displaying data in template

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.

How to check two models ID whitout nested loop?

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

trying to get many-to-many related data in Django

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

Categories