Django display data from two different models - python

I have two seperated models. One with two text fields and one for multiple images. Now I want to display the entire data in one html div. What do I have to change in the projects view and in projects.html?
models.py
class Project(models.Model):
title = models.CharField(max_length=200)
describtion = models.TextField(null=True, blank=True)
class ProjectImage(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
image = models.FileField(upload_to="products/")
forms.py
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['title', 'describtion']
class ProjectImageForm(forms.ModelForm):
class Meta:
model = ProjectImage
fields = ['image']
widgets = {
'image': ClearableFileInput(attrs={'multiple': True}),
}
views.py
def createProject(request):
form = ProjectForm()
form2 = ProjectImageForm()
if request.method == 'POST':
form = ProjectForm(request.POST)
form2 = ProjectImageForm(request.POST, request.FILES)
images = request.FILES.getlist('image')
if form.is_valid() and form2.is_valid():
title = form.cleaned_data['title']
describ = form.cleaned_data['describtion']
project_instance = Project.objects.create(
title=title, describtion=describ)
for i in images:
ProjectImage.objects.create(project=project_instance, image=i)
context = {'form': form, 'form2': form2}
return render(request, 'projects/project_form.html', context)
def projects(request):
projects = Project.objects.all()
context = {"projects":projects}
return render(request, 'projects/projects.html', context)
projects.html
{% for project in projects %}
<div class="column">
<div class="card project">
<img class="project__thumbnail" src="{{project.image.url}}" alt="project thumbnail" />
<div class="card__body">
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
</div>
</a>
</div>
</div>
{% endfor %}

You don't need to change anything.
You should be able to access the reverse with project.project_image_set attribute in the template:
<div class="card__body"
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
{% for image in project.project_image_set.all %}
{{ image.image }}
{% endfor %}
</div>
Docs: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_one/

I don't really understand the question here but i see a problem in your template considering you are using foreign key in ProjectImage. And update the question
{% for project in projects %}
<div class="column">
<div class="card project">
{% for j in project.projectimage_set.all %}
<img class="project__thumbnail" src="{{j.image.url}}" alt="project thumbnail" />
{% endfor %}
<div class="card__body">
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
</div>
</a>
</div>
</div>
{% endfor %}

I would change FileField to ImageField and add function:
#property
def get_photo_url(self):
if self.image and hasattr(self.image, 'url'):
return self.image.url
else:
return ''
If createProject function works( I would rename it to create_project) then in projects.html:
{% for project in projects %}
<div class="column">
<div class="card project">
<div class="card__body">
<h3>{{project.title}}</h3>
<h2>{{project.describtion}}</h2>
{% for img in project.projectimage_set.all %}
<img class="project__thumbnail" src="{{img.get_photo_url}}" alt="project thumbnail" />
{% endfor %}
</div>
</a>
</div>
</div>
{% endfor %}

Related

ValueError: while trying to use the crispy forms

I am trying to make use of the crispy forms to display the form for inserting the data. I have a model as:
class Athlete(models.Model):
athlete_name=models.CharField(max_length=50)
GENDER_CHOICES=(
('M','Male'),
('F','Female'),
('O','Others')
)
gender=models.CharField(choices=GENDER_CHOICES,max_length=100)
age=models.IntegerField()
athlete_category=models.ForeignKey(Category,on_delete=models.CASCADE)
image=models.FileField(upload_to='static/athlete_img', null=True)
COUNTRY_CHOICES=(
('np','nepal'),
('in','india'),
('uk','united kingdom'),
('sp','spain'),
('ch','china')
)
medals=models.IntegerField
country=models.CharField(choices=COUNTRY_CHOICES,max_length=100)
def __str__(self):
return self.athlete_name
In the forms.py...I have modelform as:
class AthleteForm(ModelForm):
class Meta:
model:Athlete
fields='__all__'
In my views.py I have the following function:
def add_athlete(request):
if request.method == 'POST':
form = AthleteForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.add_message(request, messages.SUCCESS,
'Athlete added sucessfully')
return redirect('/admin/athletes')
else:
messages.add_message(request, messages.ERROR,
'Enter the appropriate values')
return render(request, 'forgame/addathletes.html', {
'form': form
})
context = {
'form': AthleteForm
}
return render(request, 'forgame/addathletes.html', context)
Inside my templates/forgame I have created addathletes.html
{% extends 'layouts.html' %}
{% load crispy_forms_tags %}
{% block title %}
<title>Game Category</title>
{%endblock%}
{% block main_content %}
<div class="container-fluid mt-4">
<div class="d-flex justify-content-center">
<div class="col-md-6">
<h2>Add Categories Here!</h2>
{% for msg in messages %}
{% if msg.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %}
<div class="alert alert-success">
{{msg}}
</div>
{%endif%}
{% if msg.level == DEFAULT_MESSAGE_LEVELS.ERROR %}
<div class="alert alert-danger">
{{msg}}
</div>
{%endif%}
{%endfor%}
<form action="" method="post" class="shadow-lg p-3">
{%csrf_token%}
{{form | crispy}}
<div class="mt-3">
<input type="submit" value="Add Category" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
{% endblock %}
My urls looks fine but I have been getting this error:
Along with this:
It should be = not : so:
class AthleteForm(ModelForm):
class Meta:
model = Athlete
fields='__all__'
I'd also recommend you to maintain gaps between template tags like it should be {% endblock %} not {%endblock%} same goes for every tag.

Book object is not iterable: Trying to display similar objects of an instance based on a field

I am working on a library project that displays books and each both is in a category(history, math, adventure, etc). On the detailed view of a book, I want a link (with "See similar books in this category") that will redirect the user to other books of the same category of the current book. So if the detail page of a book has a category of "history", the link will direct to other books in the "history" category. The redirection is to another template (Addon: In helping me out, I would love to display the "similar books" in the same page, not a different page)
error details
TypeError at /search/similar/bootsrtap/
'Book' object is not iterable
models.py
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('index')
choices = Category.objects.all().values_list('name', 'name')
choice_list = []
for item in choices:
choice_list.append(item)
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
category = models.CharField(max_length=255, choices=choice_list)
slug = models.SlugField(max_length=100, unique=True)
def __str__(self):
return self.author + ": " + self.title
def get_absolute_url(self):
return reverse('detail', kwargs={'slug': self.slug})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
return super().save(*args, **kwargs)
def similar_book(self):
return Book.objects.filter(category=self.category)
urls.py
urlpatterns = [
path('detail/<slug:slug>/', BookDetail.as_view(), name='detail'),
path('search/similar/<slug:slug>/', views.search_similar_results, name='search_similar_results'),
views.py
def search_similar_results(request, slug):
books = get_object_or_404(Book, slug=slug)
books.similar_book()
return render(request, 'book/similar_results.html', {'books': books})
class BookDetail(generic.DetailView):
model = Book
context_object_name = 'book'
template_name = 'book/detail.html'
clickable link to lead to the template
Click to see Similar books in this category</h3>
<div class="row">
template
<div class="row">
{% if books %}
{% for book in books %}
<h2>Similar Books in {{ book.category } Category}</h2>
<figure class="col-lg-3 col-md-4 col-sm-6 col-12 tm-gallery-item animated bounce infinite">
<a href="{{ book.get_absolute_url }}">
<div class="tm-gallery-item-overlay">
{% if book.cover %}
<img src="{{ book.cover.url }}" alt="Image" class="img-fluid tm-img-center">
{% endif %}
<p class="small font-italic">Author: {{ book.author }}</p>
<p class="small font-italic text-left">Title: {{ book.title }}</p>
<p class="small font-italic">Category: {{ book.category }}</p>
</div>
</a>
</figure>
{% endfor %}
{% else %}
<p>There are No Available Books</p>
{% endif %}
</div>
details.html
<div class="tm-main-content no-pad-b">
<!-- Book Detail Page Display -->
<section class="row tm-item-preview">
{% if book.cover %}
<div class="col-md-6 col-sm-12 mb-md-0 mb-5">
<img src="{{ book.cover.url }}" alt="Image" class="img-fluid tm-img-center-sm card-img-top">
<h2>Title {{ book.title }}</h2>
</div>
{% else %}
<p>No book cover</p>
{% endif %}
</section>
<!-- Similar Books Display by Category -->
<div class="tm-gallery no-pad-b">
<h3 style="color: white; text-align: center;">Similar books you might be interested in</h3>
<div class="row">
{% for item in similar_book%}
<figure class="col-lg-3 col-md-4 col-sm-6 col-12 tm-gallery-item">
<a href="{{ book.get_absolute_url }}">
<div class="tm-gallery-item-overlay">
<img src="{{ book.cover.url }}" alt="Image" class="img-fluid tm-img-center">
</div>
<p class="tm-figcaption">Title: {{ book.title}}</p>
<p class="tm-figcaption">Category: {{ book.category }}</p>
</a>
</figure>
{% endfor %}
</div>
</div>
</div>
You need to pass the result of the similar books to the template, so:
def search_similar_results(request, slug):
book = get_object_or_404(Book, slug=slug)
books = book.similar_book()
return render(request, 'book/similar_results.html', {'books': books})
In the DetailView, you can pass the `similar books with:
class BookDetail(generic.DetailView):
model = Book
context_object_name = 'book'
template_name = 'book/detail.html'
def similar_books(self):
return self.object.similar_book()
Then in the template we work with:
{% for item in view.similar_books %}
<!-- … -->
{% endfor %}

i want to display the list of pruducts based on the choice of the category chosing -django

so i want to khow what i have to add in the urls.py and in the views.py to add this functionnality: if i click in one of this categories here my categories display some products based on the category chosen.
and this the models.py
class Product(models.Model):
name=models.CharField(max_length=200,null=True)
price=models.DecimalField(max_digits=7,decimal_places=2)
digital=models.BooleanField(default=False,null=True,blank=True)
image=models.ImageField(blank=True,null=True,upload_to ='images/',default="images/default.jpg")
categories = models.ForeignKey(Category,on_delete=models.CASCADE,blank=True, null=True)
def __str__(self):
return self.name
#property
def imageURL(self):
if self.image and hasattr(self.image, 'url'):
return self.image.url
else:
return '/static/images/default.png'
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True,
help_text='Unique value for product page URL, created from name.')
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'categories'
ordering = ['-created_at']
verbose_name_plural = 'Categories'
def __unicode__(self):
return self.name
and this is the template :
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<form method="get" action="">
{% for c in active_categories %}
<a class="dropdown-item" href='#'>{{ c.name }}</a>
{% endfor %}
<a class="dropdown-item" href="#">something else</a>
</form>
</div>
This is simplest way. You can change code as per requirement.
urls.py
from . import views # import views.py file
urlpatterns = [
path('product_list/<id>', views.product_list, name='product_list'),
]
views.py
def product_list(request, id):
products = Product.objects.filter(categories__pk=id)
context = {
'products': products,
}
return render(request, 'product_list.html', context)
link template (Check the change in link)
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<form method="get" action="">
{% for c in active_categories %}
<a class="dropdown-item" href="{% url 'product_list' id=c.pk %}">{{ c.name }}</a>
{% endfor %}
<a class="dropdown-item" href="#">something else</a>
</form>
</div>
product_list.html
Your regular html things +
{% for product in products %}
<p>{{ product.name }}</p>
<p>{{ product.price }}</p>
{% empty %} # in case there is no product in this category
<p>No product available for this category</p>
{% endfor %}
I hope this will help. Please comment if get any error.
If you products to load without refreshing the page, you can use ajax. Reply if need that.
You can try this:
views.py
def my_view(request):
category_id = request.GET.get('category_id')
context = {}
if category_id:
products = Product.objects.filter(categories__id=category__id)
context["products"] = products
return render(request, 'template', context)
template
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<form method="get" action="">
{% for c in active_categories %}
<a class="dropdown-item" href='?category_id={{ c.id }}'>{{ c.name }}</a>
{% endfor %}
<a class="dropdown-item" href="#">something else</a>
</form>
</div>

How would I print this field in Django?

So I am trying to print out a field in my models.py file to a HTML file. (I couldn't think of any other word besides print). I am able to do it for the username, but I am struggling for the description/bio in this case.
I have tried things like self.user.bio, self.userprofileinfo.bio among other things
models.py relevant class
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50,blank=True,null=True)
last_name = models.CharField(max_length=50,blank=True,null=True)
bio = models.CharField(max_length=150)
image = models.ImageField(upload_to='profile_pics',default='default.png')
joined_date = models.DateTimeField(blank=True,null=True,default=timezone.now)
verified = models.BooleanField(default=False)
def __str__(self):
return self.user.username
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
forms.py
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta():
model = User
fields = ('username','email','password')
class UserProfileInfoForms(forms.ModelForm):
class Meta():
model = UserProfileInfo
fields = ('first_name','last_name','bio','image')
user_posts.html
{% extends 'mainapp/base.html' %}
{% block content %}
<div class="sidebar">
<p class="active" href="#">{{ view.kwargs.username }}</p>
<p>{{ view.kwargs.bio }}</p>
<p>Lorem</p>
<p>Lorem</p>
</div>
{% for post in posts %}
<div class="content">
<div class="post">
<h1 class='posttitle'>{{ post.title }}</h1>
{% if post.published_date %}
<!-- <div class="postdate">
<i class="fas fa-calendar-day"></i> <p>Posted {{ post.published_date|timesince }} ago</p>
</div> -->
<div class="posted-by">
<p>Posted by <strong>{{ post.author }}</strong> {{ post.published_date|timesince }} ago</p>
</div>
{% else %}
<a class="pub-post" href="{% url 'mainapp:post_publish' pk=post.pk %}">Publish</a>
{% endif %}
<p class='postcontent' >{{ post.text|safe|linebreaksbr }}</p>
</div>
</div>
{% endfor %}
{% endblock %}
I tried this code right here:
return '%s %s' % (self.user.username, self.UserProfileInfo.bio)
However this did not work, and it didn't print anything off in the HTML. Thanks for any help :)
This is my views.py UserPostListView. I just realised I didn't have bio there. How would I be able to add multiple strings? Comma separated?
class UserPostListView(ListView):
model = Post
template_name = 'mainapp/user_posts.html'
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User,username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-published_date')

Access Django's class model and display on template

I am new to django and is working on my pet project. I am having a bit of problem accessing the data from one of my classes in models.py
models.py
class Team_Region(models.Model):
name = models.CharField(max_length=50)
# String representation
def __str__(self):
return self.name
class Team_Name(models.Model):
t_name = models.CharField(max_length=100)
logo = models.ImageField(upload_to='team_logos', blank=True)
region_member = models.ForeignKey(Team_Region, related_name='regions')
def __str__(self):
return self.t_name + ' - ' + str(self.region_member)
class Team_Member(models.Model):
mem_name = models.CharField(max_length=100)
position = models.CharField(max_length=50)
member_of_team = models.ForeignKey(Team_Name, related_name='teams')
def __str__(self):
return self.mem_name + ' - ' + self.position + ' - ' + str(self.member_of_team)
views.py
# Display regions in list
class TeamRegionListView(ListView):
context_object_name = 'regions_list'
model = Team_Region
template_name = 'dota_teams/team_regions_list.html'
# Display all teams associated to the region
class TeamRegionDetailView(DetailView):
context_object_name = 'region_teams'
model = Team_Region
template_name = 'dota_teams/team_regions_detail.html'
class MemberDetailView(DetailView):
context_object_name = 'team_members'
model = Team_Name
template_name = 'dota_teams/member_detail.html'
urls.py
url(r'^$', views.TeamRegionListView.as_view(), name='all_regions'),
url(r'^(?P<pk>\d+)/$', views.TeamRegionDetailView.as_view(), name='region_teams'),
url(r'^(?P<pk>\d+)/(\d+)/$', views.MemberDetailView.as_view(), name='member_details'),
UPDATE
team_regions_list.html
{% block body_block %}
<div class="row">
{% for region in regions_list %}
<div class="col-xs-12 col-lg-4">
<a href="{{ region.id }}" class="thumbnail" style="width: 350px; height:350px">
<h4 style="text-align: center; margin-top: 150px"> {{ region.name }} </h4>
</a>
</div>
{% endfor %}
</div>
{% endblock %}
team_regions_detail.html
{% block body_block %}
<div class="row">
{% for team in region_teams.regions.all %}
<div class="col-xs-12 col-lg-4">
<a href="{{ team.id }}" class="thumbnail">
<img style="width: 100px; height:90px" src="{{ team.logo.url }}" alt="Image not found.">
<div class="caption">
<h4 style="text-align:center">{{ team.t_name }}</h4>
</div>
</a>
</div>
{% endfor %}
</div>
{% endblock %}
I'm not sure how to access the mem_name and position variables under the Team_Member class. In my views.py, if I use the model Team_Name, the ID is not properly assigned to the region and team. I have tried accessing the Team_Member by using a For loop from Team_Region and use the 'related_name' of Team_Name then access the 'related_name' of Team_Member but it won't work either (e.g. {% for member in regions_list.regions.teams.all %} ). I'm a bit loss on this.
Any suggestions please?
TIA
Based on your models, it has the relationship:
Team_Region --> has many Team_Name
Team_Name --> has many Team_Member
you have already defined the foreign keys with related_name, if you access the Team_Member from Team_Region, you have to for loop the regions_list first, then use the related_name regions to get the team names, after that for loop all the teams, use the related_name teams to get the team members. In your template team_regions_list.html, like this:
{%for rl in regions_list%}
{%for tn in rl.regions.all %}
{{tn.teams.all}}
{% endfor %}
{% endfor %}
Update:
in your views.py, the modal is not correct for class TeamRegionDetailView and MemberDetailView, change to:
# Display regions in list
class TeamRegionListView(ListView):
context_object_name = 'regions_list'
model = Team_Region
template_name = 'dota_teams/team_regions_list.html'
# Display all teams associated to the region
class TeamRegionDetailView(DetailView):
context_object_name = 'region_teams'
model = Team_Name
template_name = 'dota_teams/team_regions_detail.html'
class MemberDetailView(DetailView):
context_object_name = 'team_members'
model = Team_Member
template_name = 'dota_teams/member_detail.html'
Use region_teams.teams.all instead of region_teams.regions.all, change your team_regions_detail.html to :
<div class="row">
{% for team in region_teams.teams.all %}
<div class="col-xs-12 col-lg-4">
<a href="{{ team.id }}" class="thumbnail">
<img style="width: 100px; height:90px" src="{{ region_teams.logo.url }}" alt="Image not found.">
<div class="caption">
<h4 style="text-align:center">{{ team.t_name }}</h4>
</div>
</a>
</div>
{% endfor %}
</div>
Update 2:
I believe that you manage your upload files incorrectly, refer to official doc:
By default, Django stores files locally, using the MEDIA_ROOT and
MEDIA_URL settings.
in your project settings.py file, add them:
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
and in your project urls.py file, add:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
After that the template can access your uploaded images with (/media/team_logos/test.png), eg:
http://127.0.0.1:8000/media/team_logos/test.png
I'd like to thank Tiny.D for the idea on the solution.
I updated my views.py
from django.views.generic import ListView, DetailView
from .models import Team_Region, Team_Name, Team_Member
#Display regions in list
class TeamRegionListView(ListView):
context_object_name = 'regionlist'
model = Team_Region
template_name = 'dota_teams/team_region_list.html'
#Display teams associated to the region
class TeamRegionDetailView(DetailView):
context_object_name = 'regiondetail'
model = Team_Region
template_name = 'dota_teams/team_region_detail.html'
#Display members of each team
class TeamDetailView(DetailView):
context_object_name = 'teamdetail'
model = Team_Region
template_name = 'dota_teams/team_detail.html'
urls.py
url(r'^$', views.TeamRegionListView.as_view(), name='region_list'),
url(r'^(?P<pk>\d+)/$', views.TeamRegionDetailView.as_view(), name='region_detail'),
url(r'^(?P<pk>\d+)/(\d+)/$', views.TeamDetailView.as_view(), name='member'),
The logic of the team_region_list.html and team_region_detail.html are the same to my post above.
team_detail.html
{% block body_block %}
<div class="row">
{% for member in teamdetail.regions.all %}
{% for member_detail in member.teams.all %}
<h4>{{ member_detail.mem_name }}</h4>
{% endfor %}
{% endfor %}
</div>
{% endblock %}
For the team_detail.html, I just need to perform a nested loop just what Tiny.D mentioned. Everything is working now. Thanks.

Categories