I have a ListView where a I want to list products. The problem is that I can't get the related image of these products as they are in a different model.
The model for products is:
class Product(models.Model):
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, verbose_name='marca')
name = models.CharField('nombre', max_length=40)
description = models.TextField('descripción', blank=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
slug = models.SlugField(max_length=50)
active = models.BooleanField('activo',default=True)
in_stock = models.BooleanField('en stock', default=True)
tags = models.ManyToManyField(ProductTag, blank=True)
date_updated = models.DateTimeField('última actualización', auto_now=True)
The model of images is:
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name='producto')
image = models.ImageField('imagen', upload_to="product-images")
thumbnail = models.ImageField('miniatura', upload_to="product-thumbnails", null=True)
To get both models in the template I used context.update in the view.
class ProductListView(ListView):
template_name = 'product_list.html'
context_object_name = 'products_list'
model = models.Product
paginate_by = 4
def get_context_data(self, **kwargs):
context = super(ProductListView, self).get_context_data(**kwargs)
context.update({'product_images_list': models.ProductImage.objects.all()})
return context
def get_queryset(self):
tag = self.kwargs['tag']
self.tag = None
if tag != 'all':
self.tag = get_object_or_404(models.ProductTag, slug=tag)
if self.tag:
products = models.Product.objects.active().filter(tags=self.tag)
else:
products = models.Product.objects.active()
return products.order_by('name')
Additionally, I created a filter to iterate both models in a forloop but I think is useless as I don't want to iterate both models, I just want to get the first image that matches the product's FK to show it in the template:
from django import template
register = template.Library()
#register.filter(name='zip')
def zip_lists(a, b):
return zip(a, b)
The template I'm using is:
{% extends 'base.html' %}
{% load humanize %}
{% load product_extras %}
{% block content %}
<div class="destacados">
{% for product, image in products_list|zip:product_images_list %}
<div class="collections coll-watches">
<img class="foto" src="{{ image.thumbnail.url }}">
<p class="prod-description">{{ product.name }}</p>
<p class="prod-description prices"><strong>$ {{ product.price|intcomma }}</strong></p>
<a class="boton-tr boton-tr-watches" href="#">Agregar al carrito</a>
</div>
{% endfor %}
</div>
{% endblock content %}
As you can see the problem is in <img class="foto" src="{{ image.thumbnail.url }}">. I know this is incorrect, but I don't know how to get the image related to the product through its FK.
I'm new i django (only two months) and I'm sure this should be easier, but I can figure it out...
Any help would be appreciated!!
Best regards
you can define a get image url method in your product model as below
def get_image_url(self):
img = self.productimage_set.first()
if img:
return img.thumbnail.url
return img #None
and call it in your templates get_image_url
Thank you very much #bmons. I had to modify the method a little bit, but your answer gave me the clue I needed.
This is the final code:
def get_image_url(self):
img = self.productimage_set.first().thumbnail.url
if img:
return img
return img #None
When Using img = self.productimage_set.thumbnail.first() I get a RelatedManager error.
Anyway, I really appreciate your help!! Cheers
Related
I have this model.py file:
class Category(models.Model):
title = models.CharField(max_length=255, verbose_name="Title")
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.title
class Video(models.Model):
title = models.CharField(max_length=128)
description = models.TextField(default='')
thumbnail = models.ImageField(default='default_thumbnail.png', upload_to='thumbnails')
date_posted = models.DateTimeField(default=timezone.now)
category = models.ForeignKey(
Category,
on_delete=models.SET_DEFAULT,
#null=True,
default=1,
verbose_name="Category"
)
def __str__(self):
return self.title
Each video has its own category and I want to display on one page a given number of films in each category.
Currently, I'm only showing the last 5 videos on the page:
def home(request):
latest_videos = Video.objects.all().order_by('-date_posted')[:5]
categories = Category.objects.all()
context = {
'latest_videos': latest_videos,
#'categories': categories,
}
return render(request, 'videos/home.html', context)
I have no idea how to display a given number of movies in each category. I tried to send all the videos and query them on the template, but this is probably impossible.
Any ideas? I'm still learning Django, but this time I found a problem I can't solve all day.
Thanks
I don't know how many categories do you need to show in your template but one way to do this is like:
def home(request):
ready_data = []
all_categories = Category.objects.prefetch_related('video_set').all() # Note that video_set is your default related query name
for _c in all_categories:
related_videos = _c.video_set.all().order_by('-date_posted')[:5]
ready_data.append({"category_id": _c.id, "category_title": _c.title, "related_videos": related_videos})
context = {
'data': ready_data
}
return render(request, 'videos/home.html', context)
And you should also change this part of your template to something like:
{% for d in data %}
<p> category: {{ d.category_id }}, {{ d.category_title }} </p>
{% for v in d.related_videos %}
<h3> Videos </h3>
<p> {{ v.id }}- {{v.title}} </p>
{% endfor %}
{% endfor %}
This way you can show each category and 5 last of it's related videos.
I am working on this project that I can add two or more forms in a template. I am able to get the two forms in the template but when I submit the form, I get the objects for the rentalproperty model and not the contract model. I have created two different solution but the two doesn't solve the problem.
The first solution below display both objects multiple times in the detailview but what I want is to display the two model objects just once. The second solution display the rentalproperty object once but the contract objects multiple times. Could someone point me in the right direction? Thanks.
First solution:
views.py
class DetailView(generic.DetailView):
model = RentalProperty
template_name = 'rental/detail.html'
context_object_name = 'property'
def new_rental(request, pk):
if request.method == 'POST':
rental_form = NewRentalPropertyForm(request.POST, request.FILES, prefix = "rentals")
contract_form = NewContractForm(request.POST, prefix = "contracts")
if rental_form.is_valid() and contract_form.is_valid():
print ("all validation passed")
rentalproperty = rental_form.save()
contract_form.cleaned_data["rentalproperty"] = rentalproperty
print(contract_form)
contract = contract_form.save(commit=False)
contract.rentalproperty = rentalproperty
contract = contract_form.save()
return HttpResponseRedirect(reverse("home"))
else:
messages.error(request, "Error")
contract = Contract.objects.get(pk=pk)
else:
rental_form = NewRentalPropertyForm(prefix = "rentals")
contract_form = NewContractForm(prefix = "contracts")
contract = Contract.objects.get(pk=pk)
return render(request, 'rental/new_rental.html', {
#'rentalproperty': rentalproperty,
'rental_form': rental_form,
'contract_form': contract_form,
'contract': contract,
})
detail.html
<h1>This is the detail view</h1>
<h3>From landlord</h3>
<p>Landlord: {{property.created_by}}</p>
<p>address: {{property.landlord.address}}</p>
<h3>From Rental property</h3>
<ul>
{% for rental in property.landlord.rentalpropertys.all %}
<br>
<li>Title: {{property.title}}</li>
<img src="{{property.image.url}}" height="200" alt=""/>
<li>created at: {{property.created_at}}</li>
<li>Type of property: {{property.type_of_property_listing}}</li>
<li>Street: {{property.street}}</li>
<li>Borough: {{property.borough}}</li>
<ul>
{% for contract in rental.contracts.all %}
<li> Insurance required: {{contract.insurance_required}}</li>
<li> other terms: {{contract.other_terms}}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
Second solution:
views.py
class DetailView(generic.DetailView):
model = RentalProperty
template_name = 'rental/detail.html'
context_object_name = 'property'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['landlord']= Landlord.objects.all()
context['contract']= Contract.objects.filter(rentalproperty__title=title).order_by('created_at')
return context
The post function is the same.
detail.html
<h1>This is the detail view</h1>
<h3>From landlord</h3>
<p>Landlord: {{property.created_by}}</p>
<p>address: {{property.landlord.address}}</p>
<h3>From Rental property</h3>
<ul>
<li>Title: {{property.title}}</li>
<img src="{{property.image.url}}" height="200" alt=""/>
<li>created at: {{property.created_at}}</li>
<li>Type of property: {{property.type_of_property_listing}}</li>
<li>Street: {{property.street}}</li>
<li>Borough: {{property.borough}}</li>
</ul>
<ul>
{% for data in contract %}
<li> insurance : {{data.insurance_required}}</li>
<li> other terms: {{data.other_terms}}</li>
{% endfor %}
</ul>
My model:
class Landlord(models.Model):
user = models.OneToOneField(UserModel, on_delete=models.CASCADE)
address = models.CharField(max_length=255)
def __str__(self):
return str(self.address)
class RentalProperty(models.Model):
landlord = models.ForeignKey("Landlord", related_name='rentalpropertys', on_delete=models.CASCADE)
created_by = models.ForeignKey(UserModel, related_name='rentalpropertys', on_delete=models.CASCADE)
title = models.TextField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
PROPERTY_LISTING_CHOICES = Choices(
('APARTMENT', _('Apartment')),
('HOLIDAY_HOME', _('Holiday home')),
('SINGLE_FAMILY_HOME', _('Single family home')),
('COMMERCIAL', _('Commercial')),
)
type_of_property_listing = models.CharField(
max_length = 50,
choices = PROPERTY_LISTING_CHOICES,
default = PROPERTY_LISTING_CHOICES.APARTMENT,)
street = models.CharField(max_length=255)
borough = models.CharField(max_length=255)
image = models.ImageField(upload_to='images/', null=True, blank=True,)
def __str__(self):
return str(self.title)
class Contract(models.Model):
rentalproperty = models.ForeignKey("RentalProperty", related_name='contracts', on_delete=models.CASCADE)
insurance_required = models.BooleanField(default=True)
other_terms = models.TextField(blank=True)
def __str__(self):
return str(self.insurance_required)
first solution output:
address: Helsinki
title: build apps
insurance: Yes
- This repeats itself multiple times.
Second solution output:
address: Helsinki
title: build apps
insurance: Yes
- insurance repeats itself multiple times
What I expect:
address: Helsinki
title: build apps
insurance: Yes
Thanks for your time :)
With the help of #Timmy O'Mahony answer, I did {% for contract in property.contracts.all %} in my template using the first solutions and it works. The contracts is the related name to the rental property. Thanks all.
I have a list of users and want to display their tasks only if the selected user belongs to the same department. My Models have a department field that I want to compare.
This is my template code.
{% extends 'view_users.html' %}
{% block view_user_tasks %}
Back
<p> todo lists for {{ user }}</p>
{% for todo in view_user_tasks %}
<a id="{{todo.id}}" class="todo_remove"></a>
{% endfor %}
{% endblock view_user_tasks %}
What i want to do is evaluate this condition:
if request.user.Department == user.Department:
show user tasks
This are my respective views.
class ViewUsers(ListView):
model = CustomUser
template_name = 'view_users.html'
class ViewUserTasks(ListView):
model = Todo
template_name = 'view_user_tasks.html'
context_object_name = 'view_user_tasks'
My models.py
class Todo(models.Model):
title = models.CharField(max_length=30)
body = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
checked = models.BooleanField(default=False)
owner = models.ManyToManyField(CustomUser)
id = HashidAutoField(primary_key=True)
def __str__(self):
return "%s: %s" % (self.title, self.body)
class CustomUser(AbstractUser):
Department = models.CharField(max_length=30, blank=True)
How can I be able to accomplish this?
Do your filtering logic in the view. You can override the default get_queryset method and return only the Todos that you want.
class ViewUserTasks(ListView):
template_name = 'view_user_tasks.html'
context_object_name = 'view_user_tasks'
def get_queryset(self):
return Todo.objects.filter(user__Department=self.request.user.Department)
And then just loop through the returned data like you are already doing.
If I clearly understand your question, you can compare it like this:
{% if todo.user.id == user.id %}
This is my template file:
{% for tag, count in tags.items %}
{{ tag }} ({{count}}),
{% endfor %}
urls.py
url(r'^tag/(?P<slug>[\w-]+)/$', views.tags, name='tags'),
I am trying to take {{ tag }} as slug parameter. Is it possible? Next I would try to create page where I will list all pages that cointain tag in keywords field in Sites model.
I don't have tag model by the way. I am trying to take my tag variable and put it into my url (tag/tagvariable).
I get tags that way:
index.view
tags = Tags().all_tags()
context['tags'] = tags
calculations.py
class Tags():
def all_tags(self):
sites = Site.objects.values('keywords')
keywords = []
tags = []
for site in sites:
keywords.append(site['keywords'].split(','))
for keyword in keywords:
for tag in keyword:
tags.append(tag.strip())
return(dict(Counter(tags)))
models.py
class Site(models.Model):
category = models.ForeignKey('Category')
subcategory = ChainedForeignKey(
'Subcategory',
chained_field='category',
chained_model_field='category',
show_all=False,
auto_choose=True)
name = models.CharField(max_length=70)
description = models.TextField()
keywords = MyTextField()
date = models.DateTimeField(default=datetime.now, editable=False)
url = models.URLField()
is_active = models.BooleanField(default=False)
def get_absolute_url(self):
return "%s/%i" % (self.subcategory.slug, self.id)
class Meta:
verbose_name_plural = "Strony"
def __str__(self):
return self.name
A tag model is going to make your life easier
I'm assuming that you've make the correct view tag, use the slugify tag to convert your tag to a slug
{% for tag, count in tags.items %}
{{ tag }} ({{count}}),
{% endfor %}
and in the urls.py
url(r'^tag/(?P<slug>[-\w]+)/$', views.tags, name='tags'),
I am trying to display a list of objects (Images)based on a many-to-many relationship (Image / Gallery with an intermediary Galleryimage model).
Galleryimage has an additional field called position which I want to use to manage the order that the images are listed in a template.
I also have a page model which can optionally have a gallery attached to it.
My models look like this:
class Page(models.Model):
gallery = models.ForeignKey(Gallery, blank=True, null=True)
slug = models.SlugField()
title = models.CharField(max_length=200)
content = models.TextField()
def __unicode__(self):
return self.title
class Image(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(upload_to='images')
def __unicode__(self):
return self.title
class Gallery(models.Model):
title = models.CharField(max_length=50)
images = models.ManyToManyField(Image, through='Galleryimage')
def __unicode__(self):
return self.title
class Galleryimage(models.Model):
image = models.ForeignKey(Image)
gallery = models.ForeignKey(Gallery)
position = models.IntegerField()
class Meta:
ordering = ['position']
I am retrieving a page model in my view like this:
def detail(request, page_id):
p = get_object_or_404(Page, pk=page_id)
return render_to_response('detail.html', {'page': p},
context_instance=RequestContext(request))
And finally, I am displaying the images in the template like so:
{% block images %}
{% if page.gallery %}
{% for image in page.gallery.images.all %}
<a rel="gallery" href="{{ STATIC_URL }}{{ image.image }}"></a>
{% endfor %}
{% endif %}
{% endblock %}
The images all display as expected however, the order always seems to be the same, regardless of what I do.
Can anyone give me a nudge in the right direction?
Any advice appreciated.
Thanks.
Have you tried to set the ordering option in the Image model? I know you setted the position in the through relation table, but by moving it to the Image model (if possible ?) and setting the order on that model, it shall work.
class Image(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(upload_to='images')
position = models.IntegerField()
def __unicode__(self):
return self.title
class Meta:
ordering = ['position']