Django displaying many to many in template - ordering ignored? - django

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']

Related

How to get my image gallery into my product.html in Django with Class based view DetailView

I am using model Product and Images. my goal is to display in my singe item page a gallery of item related pictures coming from model "Images"
How can i change the following code to filter by item slug and only show gallery specific to the slug.
Item class
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
image = models.ImageField(, upload_to='catalog/images/', blank=True)
slug = models.SlugField()
Images class
class Images(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
title = models.CharField(blank=True, max_length=50)
image = models.ImageField(upload_to='catalog/images/', blank=True)
Product detail view
class ProductDetailView(DetailView):
model = Item
template_name = 'product.html'
context_object_name = 'item'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['image_gallery'] = Images.objects.all()
return context
product.html page
<div class="row wow fadeIn">
{% for img in image_gallery %}
<div class="col-lg-4 col-md-12 mb-4">
<img src="{{img.image.url}}" class="img-fluid" alt="">
</div>
You can access the instance of image with self.object, then you just need to filter the images you want to pass to the view with the foreign key relationship.
class ProductDetailView(DetailView):
model = Item
template_name = 'product.html'
context_object_name = 'item'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['image_gallery'] = Images.objects.filter(item=self.object)
return context
You need to use .filter() not .get()
image = Images.objects.all()
image.image will give you the image
This is Django 3 you need to convert it to whatever version you are using

How to display A only a users entered data related in a foreignkey on detailview in django

I am building this simple quiz app. This app allows all users to submit an answer to an assignment in Docx format. I what that any time a user views the question on the DetailView page, if the user has already submitted a solution for that assignment, that solution should be shown on the DetailView page as well. Current I get is all that answers submitted by all users. I only want a user's answer to that assignment on the detailpage
this is my model.
class Assignment(models.Model):
title = models.CharField(max_length=120)
slug = models.SlugField(max_length=500)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
class_or_level = models.ForeignKey(StudentClass, on_delete=models.CASCADE)
teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
Text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
date_expire = models.DateTimeField()
def __str__(self):
return self.title
class Answer(models.Model):
slug = models.SlugField(max_length=500)
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
student = models.ForeignKey(User, on_delete=models.CASCADE)
file = models.FileField(upload_to='assignment')
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{} - {} '.format(self.assignment, self.student)
Below is my view
class AssignmentSubmitView(DetailView):
model = Assignment
template_name = 'assignment_submit.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['student_answer'] = self.object.answer_set.all()
return context
Below is my filter on detailview template.
{% for answer in student_answer %}
{{ answer.file }}
{% endfor %}
You will need to first of all know the user that is accessing that page, so i presume you have a user model and an authentication system in place.
in the views
class AssignmentSubmitView(DetailView):
model = Assignment
template_name = 'assignment_submit.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['student_answer'] = self.object.answer_set.filter()#then filter and get the answer specific to that user here (depending on your user and Assignment models)
return context
and in your templates
{% if user.is_authenticated %}
{% if student_answer %}
{% for answer in student_answer %}
{{ answer.file }}
{% endfor %}
{% endif %}
{% endif %}

Get image.url as atributte in a related model in django template

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

Django - admin filter to "Add..." page (Model with more then one FK)

I am trying to filter the "Test" content according to "Plan" selection in Admin page as described in the attached picture.
.
I am looking for an easy way to filter the content of "Test" drop list according to the selection of "Plan" drop list.
Thanks for your help, Eran
While my models looks like:
class TestPlan(models.Model):
test_plan_name = models.CharField(max_length=200)
def __str__(self):
return self.test_plan_name
class Test(models.Model):
test_plan = models.ForeignKey(TestPlan, on_delete=models.CASCADE)
test_name = models.CharField(max_length=200)
test_type = models.CharField(max_length=200)
manual_ttc = models.IntegerField(default=0)
priority = models.IntegerField(default=0)
owner = models.CharField(max_length=200)
drop_name = models.CharField(max_length=200)
test_description = models.CharField(max_length=200)
note = models.CharField(max_length=200)
ac = models.CharField(max_length=200)
def __str__(self):
return self.test_name
class Result(models.Model):
plan = models.ForeignKey(TestPlan, on_delete=models.CASCADE)
test = models.ForeignKey(Test)
status = models.CharField(max_length=100)
version = models.CharField(max_length=100)
bug = models.CharField(max_length=100)
result_path = models.CharField(max_length=100)
def __str__(self):
return self.status
Create a new change_form_template for the admin by extending the admin_change_form
Write your own custom JS to handle the change event
your_change_form.html
{% extends 'admin/change_form.html' %}
{% load static %}
{% block admin_change_form_document_ready %}
{{ block.super }}
<script type="text/javascript" src="{% static 'path_to_js.js' %}"></script>
{% endblock %}
create the JS file and add in your custom logic.
in your admin.py, tell django to use your custom change_form html
class ResultAdmin(admin.ModelAdmin):
change_form_template = '<template_directory/your_change_form.html>'

Combining, filtering models/views in a ManyToMany Django

I'm working on a comic book database and there are main covers and variant covers. I have a page that shows all the Main covers, but I'd like to combine the variant covers too, in order of the publication date. This is what part of my models look like:
class Image(models.Model):
CATEGORY_CHOICES = (
('Cover', 'Cover'),
('Scan', 'Scan'),
('Other', 'Other'),
)
title = models.CharField(max_length=128)
number = models.CharField(max_length=20, help_text="Do not include the '#'.")
image = models.ImageField(upload_to="images/")
category = models.CharField(max_length=10, choices=CATEGORY_CHOICES)
### The variant cover is determined by the category_choice 'Cover'. ###
contributor = models.ManyToManyField(Contributor, blank=True, null=True)
date_added = models.DateField(auto_now_add=True, auto_now=True)
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
class Issue(models.Model):
CATEGORY_CHOICES = (
('Major', 'Major'),
('Minor', 'Minor'),
('Cameo', 'Cameo'),
('Other', 'Other'),
)
title = models.ForeignKey(Title)
number = models.CharField(max_length=20, help_text="Do not include the '#'.")
pub_date = models.DateField(blank=True, null=True)
cover_image = models.ImageField(upload_to="covers/", blank=True, null=True)
### This would be where the main image goes. ^^^ ###
images = models.ManyToManyField(Image, related_name="images_inc", blank=True, null=True)
### This is where the variant covers go.^^^ ###
has_emma = models.BooleanField(help_text="Check if Emma appears on the cover.")
My views.py for the main cover page looks like this:
def covers(request):
sort_by = request.GET.get('sort', 'pub_date')
if sort_by not in ['-date_added', 'date_added', '-pub_date', 'pub_date']:
sort_by = '-date_added'
issues = Issue.objects.filter(has_emma=True).order_by(sort_by).select_related(depth=1)
return render_to_response('comics/covers.html', {'issues': issues}, context_instance=RequestContext(request))
But I would like to display the variant covers too and not just the cover_image. Is there a way to do this? Maybe with something image and then filtering the category (of the Image model by cover)?
I, of course, can do this:
def variants(request):
Issue.objects.filter(has_emma=True).order_by(sort_by).select_related(depth=1)
images = Image.objects.filter(category='Cover').order_by('id')
return render_to_response('comics/variants.html', {'images': images}, context_instance=RequestContext(request))
But that does not give me enough flexibility as def covers does, and I want them combined and sorted by pub_date, like def covers.
Edit
models.py:
class Image(models.Model):
CATEGORY_CHOICES = (
('Cover', 'Cover'),
('Scan', 'Scan'),
('Other', 'Other'),
)
title = models.CharField(max_length=128)
image = models.ImageField(upload_to="images/")
category = models.CharField(max_length=10, choices=CATEGORY_CHOICES)
date_added = models.DateField(auto_now_add=True, auto_now=True)
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
class Issue(models.Model):
title = models.ForeignKey(Title)
number = models.CharField(max_length=20)
######
has_emma = models.BooleanField(help_text="Check if cover appearance.")
cover_image = models.ImageField(upload_to="covers/", blank=True, null=True)
images = models.ManyToManyField(Image, related_name="images_inc", blank=True, null=True)
######
def get_images(self):
''' Returns a list of all cover images combined,
"main" cover image first.
'''
images = [self.cover_image]
for image in self.images.filter(category='Cover'):
images.append(image.image)
return images
views.py:
def covers(request):
sort_by = request.GET.get('sort', '-pub_date')
if sort_by not in ['-date_added', 'date_added', '-pub_date', 'pub_date']:
sort_by = '-date_added'
issues = Issue.objects.filter(has_emma=True).order_by(sort_by)
return render_to_response('template.html', {'issues': issues,}, context_instance=RequestContext(request))
template.html:
{% for issue in issues %}{% for image in issue.get_images %}{{ image.image }}{% endfor %}{% endfor %} - displays nothing, however, {% for issue in issues %} {% for image in issue.get_images %} {{ issue.cover_image }} {% endfor %} {% endfor %} will repeatedly display the cover_image of the Issue model if there are variant covers, which are categorized in the Image model.
What can I do to fix this, so that it shows everything correctly? And for the record again, I want it to display the {{ cover_image }} (from the Issue model) and the {{ image.image }} as defined by the Image model combined.
If I understand your problem correctly, one way to solve it would be adding a method to Issue class like this:
class Issue(models.Model):
# fields...
def get_images(self):
''' Returns a list of all cover images combined,
"main" cover image first.
'''
images = [self.cover_image]
for image in self.images.filter(category='Cover'):
images.append(image.image)
return images
Then, in your template, you can do, for example, {% for image in issue.get_images %}....
(If it's not exactly what you need—then, I think, it would be better if you provide some template code as an example of what you're trying to achieve.)