I have the following models. I am trying to get the newlistitem model to inherit the same image from the above, if that makes sense. I see that I passed through user as a parameter when calling listitem.user and it works fine, but can't seem to grab the picture of the related object.
HTML Render
I am returning both objects to the form and call
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
<li><img src="/media/{{ item.list_picture }}"/></li>
<li>{{item|title}}</li>
</div>
{% endfor %}
#MODELS
from django.db import models
from django.contrib.auth.models import User
class newlist(models.Model):
user = models.ForeignKey(User)
list_name = models.CharField(max_length = 100)
picture = models.ImageField(upload_to='profiles/')
def __str__(self):
return self.list_name
class newlistitem(models.Model):
user = models.ForeignKey(User)
list_name = models.ForeignKey(newlist)
list_item = models.CharField(max_length = 200)
list_picture = models.ImageField(newlist.picture)
def __str__(self):
return self.list_item
First things first, list_picture = models.ImageField(newlist.picture)
is not going to work. However, it did provide some insight into what you're trying to do.
Since you already have a foreign key to a list in the newlistitem model (your list_name field), you can access the picture that it's linked to by traversing the foreign key, as such.
You'll note that I've also used the url property that all ImageFields contain, to automatically populate the URL of the picture:
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
<li><img src="{{ item.list_name.picture.url }}"/></li>
<li>{{item|title}}</li>
</div>
{% endfor %}
UPDATE
Some of the pictures that you are trying to access are blank, so you will need to validate that there is an image associated with each entry.
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
{% if item.list_name.picture %}
<li><img src="{{ item.list_name.picture.url }}"/></li>
{% endif %}
<li>{{item|title}}</li>
</div>
{% endfor %}
Related
I have 2 Django models Review and Item that I am working with. I want to see if the user has already reviewed the item. If yes he sees the review score. if no he sees the button to review the item
I have the below Review model
class Review (models.Model):
review_from = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='review_from')
review_for = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='review_for')
item = models.ForeignKey(OrderItem, related_name='items')
Defining the variables in the view context (pseudocode)
admin = User.objects.get(username="admin")
admins_reviews = Review.objects.filter(review_from__username = "admin")
Below is my template
{% for item in buyers_items %}
{% for review in buyers_review%}
{% if review.item.id == item.id %}
<button class="text-success">Your rating<br/><b>{{review.ratings}}/10</b></button>
{% else %}
<a href="{% url ... %}">
<button>Leave Review</button>
</a>
{% endif %}
{% endfor %}
{% endfor %}
If I do this I get a below error
How can I overcome this problem.
View
from django import template
register = template.Library()
class OrderHistory(LoginRequiredMixin, ListView):
model = Order
template_name = 'order/order_list.html'
def get_context_data(self, **kwargs):
context = super(OrderHistory, self).get_context_data()
context['order_details'] = Order.objects.filter(emailAddress=self.request.user.email)
context['order_items'] = OrderItem.objects.filter(order__emailAddress=self.request.user.email)
context['buyers_review'] = Review.objects.filter(review_from=self.request.user)
print(context['buyers_review'])
return context
Custom Tag
#register.filter()
def review_bought_items(order_items, buyers_review):
return buyers_review.filter(item__in=order_items).exists()
Based on what I see in your templates, you could do it simpler with a tag filter or in your view side. Let's go with a custom tag:
#register.filter
def review_bought_items(buyers_items,buyers_review):
return buyers_review.filter(item__in=buyers_items).exists()
Now in the templates you could do
<!-- load the tag -->
{% load file %}
{% if buyers_items|review_bought_items:buyers_review %}
<button class="text-success">Your rating<br/><b>{{review.ratings}}/10</b></button>
{% else %}
Leave Review
{% endif %}
The issue is that you are iterating over all buyers_reviews. In this particular case, you have 2 buyer reviews, one for the current item and one for a different one.
First iteration will evaluate to False the first condition and it will display all the Leave Review button and the 2nd iteration will evaluate it to True and display the "Your rating" block.
If you don't want to move all the logic on the backend, maybe make us of a template tag in order to filter the reviews based on item.id
First, I'm newbie in Django. My first project is a movie web app, with model as below:
class Movie(models.Model):
### movie database ###
def __str__(self):
return self.name
def get_random(self):
max_id = Movie.objects.all().aggregate(max_id=Max('id'))['max_id']
while True:
pk = random.randint(1, max_id)
movie = Movie.objects.filter(pk=pk).first()
if movie:
return movie
This "get_random" func only give me 1 return. May I get more than that, let say 10?
I used this model in my "movies_index" template. :
{% for movie in movies %}
<a href="{% url 'movies_detail' movie.get_random.pk %}">
<img src="{{ movie.get_random.poster }}" class="img-fluid">
{% endfor %}
Webpage can show a movie poster with hyperlink. But when I click to, it go to another movie. Yes, because of I did "random" two time, and get 2 different results.
My question is: how can I choose a set of random and use it consistency in my scenario?
BTW, I'm using CBV as below:
class MoviesIndex(ListView):
model = Movie
context_object_name = 'movies'
template_name = 'movies/movies_index.html'
If you just want to list your movies object in random order always, you can simply use .order_by('?') while retrieving query set.
Override get_queryset method of ListView
class MoviesIndex(ListView):
model = Movie
context_object_name = 'movies'
template_name = 'movies/movies_index.html'
def get_queryset(self):
return Movie.objects.order_by('?')
and in template remove get_random
{% for movie in movies %}
<a href="{% url 'movies_detail' movie.pk %}">
<img src="{{ movie.poster }}" class="img-fluid">
{% endfor %}
Or if you still want to get_random then, list all ids of the movie objects
movie_pks = list(Movie.objects.values_list('id', flat=True))
and use random.choice to select a single pk
import random
print(random.choice(movie_pks))
Note: don't use random.randint in your case, because if an object
of the movie is deleted then it will fail
Also, remove while True: from get_random method, you don't need it because you will always get a movie object
#property
def get_random(self):
movie_pks = list(Movie.objects.values_list('id', flat=True))
pk = random.choice(movie_pks)
movie = Movie.objects.get(pk=pk)
return movie
and don't call get_random 2 times in the template use with block
{% with rand_movie=movie.get_random %}
<a href="{% url 'movies_detail' rand_movie.pk %}">
<img src="{{ rand_movie.poster }}" class="img-fluid">
{% endwith %}
I have a lot of duplicated queries (in django debug toolbar) when i load my menu tabs, im sure i can optimize this but don't find the good way.
Models :
class Categorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
def __str__(self):
return self.nom
def getscateg(self):
return self.souscategorie_set.all().filter(visible = True)
class SousCategorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
categorie = models.ForeignKey('Categorie')
def __str__(self):
return self.name
def gettheme(self):
return self.theme_set.all().filter(visible = True)
class Theme(models.Model):
name = models.CharField(max_length=100)
visible = models.BooleanField(default = False)
souscategorie = models.ForeignKey('SousCategorie')
def __str__(self):
return self.name
Views :
def page(request):
categs = Categorie.objects.filter(visible = True)
return render(request, 'page.html', locals())
Templates :
{% for categ in categs %}
<li>
{{categ.name}}
<ul>
{% for scateg in categ.getscateg %}
<li>
{{scateg.name}}
<ul>
{% for theme in scateg.gettheme %}
<li>{{ theme.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
I have look at prefetch_related but only work if i want load Categorie from SousCategorie and SousCategorie from Theme, so if i understand i need the contrary of this...
Solved !
If it can help :
from .models import Categorie, SousCategorie, Theme, SousTheme
from django.db.models import Prefetch
pf_souscategorie = Prefetch('souscategorie_set', SousCategorie.objects.filter(visible=True))
pf_theme = Prefetch('souscategorie_set__theme_set', Theme.objects.filter(visible=True))
pf_soustheme = Prefetch('souscategorie_set__theme_set__soustheme_set', SousTheme.objects.filter(visible=True))
categs = Categorie.objects.filter(visible=True).prefetch_related(pf_souscategorie, pf_theme, pf_soustheme)
and call like this in the template :
{% with currscat=categ.souscategorie_set.all %}
{% with currth=souscateg.theme_set.all %}
{% with currsth=theme.soustheme_set.all %}
Bye
In Django every time you evaluate a new queryset a query is executed, so you need to reduce the number of queryset being used. Here is what is happening:
You create a queryset Categorie.objects.filter(visible=True) and it is passed into the view layer, there the first query is executed at the this tag {% for categ in categs %}
Inside the loop, for every category you're calling a method categ.getscateg which returns a new queryset return self.souscategorie_set.all().filter(visible = True), this queryset will be executed at the second loop in your template {% for scateg in categ.getscateg %}
The same thing happens with {% for theme in scateg.gettheme %}
Using prefetch_related was the right move, try something like (didn't test it):
Categorie.objects.filter(visible=True, souscategorie_set__visible=True, souscategorie_set__theme_set__visible=True).prefetch_related('souscategorie_set__theme_set')
prefetch_related works by running a first query to load the categories that satisfy your current filter, then it executed a second query to load all subcategories and so on.
In other cases you can use select_related, but that only works when a single query can be used, as an example, it would work if you needed the category and subcategory of a theme, like:
Theme.objects.filter(pk=1).select_related('souscategorie__categorie')
The difference here is that Theme is the one that has the ForeignKey, so it has only one subcategory, and it can be loaded with a single join, that means you can use select_related only when your queryset is from the Model that points and I think that it also works with OneToOneField.
I have reduce my querysets by 2 by using "with"
It's a good point but i always got a lot of duplicate (like 44 duplicate for 51 queries), for example i hit my database when i do that :
{% for categ in categs %}
{% with currscat=categ.getscateg %}
<li class = "{% if currscat %} has_menu {% endif %}"> {{categ.name}} # This line hit database
{% if currscat %} # This line hit database
<ul class = "menu-1"> ... </ul>
{% endif %}
</li>
{% endwith %}
{% endfor %}
I try to use prefetch_related like this :
categs = Categorie.objects.filter(visible=True).prefetch_related('souscategorie_set')
but thats just add query to database, don't reduce...
Some advice?
Thanks
I try to display a menu category ---> Subcategory ---> products.
I use a context_processor to display all the categories and submenus .
I need to get products based on category and subcategory
I can only display django.db.models.fields.related.ManyRelatedManager at object ....
class Categorias(models.Model):
nome_categoria = models.CharField(max_length=100)
class Subcategoria(models.Model):
nome_subcategoria = models.CharField(max_length=100)
class Product(models.Model):
categoria = models.ManyToManyField('Categorias')
subcategoria = models.ManyToManyField('Subcategoria')
context.py
def menu(request):
return {'menucategoria': Categorias.objects.all(),}
def submenu(request):
return {'submenu': Subcategoria.objects.all(),}
menu.html
{% for c in menucategoria %}
<ul>
<li class="block">
<input type="checkbox" name="item" id="{{c.id}}" />
<label for="{{c.id}}">{{c}}</label>
<ul class="options">
{% for p in produtos.subcategoria.all %}
<li>{{p}}</li>
{% endfor %}
</ul>
</li>
</ul>
{% endfor %}
{% for p in produtos.subcategoria.all %}
In Python you would get a TypeError: 'Manager' object is not iterable exception, but in templates if fails silently...
There are some more tweaks to be done... You seem to got it wrong with the related_name. Related name is used for reversing relationships, not following them. So probably this is what you're after:
class Categoria(models.Model): # singular!
nome_categoria = models.CharField(max_length=100)
class Subcategoria(models.Model):
nome_subcategoria = models.CharField(max_length=100)
class Product(models.Model):
# using ForeignKey instead of ManyToMany. Guessed so because "categoria" is singular, right?
categoria = models.ForeignKey('Categoria', related_name='produtos') # plural in related_name, and "products" not "category"
subcategoria = models.ForeignKey('Subcategoria', related_name='produtos') # plural in related_name, and "products" not "category"
Now you can do stuff like:
{% for p in categoria.produtos.all %}
somestuff...
{% for sc in p.subcategoria.all %}
somemorestuff...
P.S.
You can leave out the related_name altogether. A default related name will be used: product_set in this example.
I need to understand a bit better how do FK/m2m relationships work.
I've prepared Images model for uploading images and it has an additional feature - it can be categorized by adding to a specific gallery (m2m relation to gallery).
To access gallery name I just had to do a query set for example:
Images.objects.filter(gallery__gallery_name = '')
I'd like to reverse the query a little bit so from Gallery model I can access pictures which are in specific gallery (gallery_name).
How I can do that?
Models:
class Images(models.Model):
image = models.ImageField(upload_to=update_filename, blank=True, null=True, verbose_name="Obrazek")
gallery = models.ForeignKey('Gallery', blank=True, null=True)
class Gallery(models.Model):
gallery_name = models.CharField(max_length=128)
gallery_description = models.TextField(blank=True, null=True)
View:
def index(request):
p = Gallery.objects.filter(gallery_name="main").order_by('-id')
return TemplateResponse(request, 'gallery.html',
{'gallery': p,
},)
Template:
{% for n in gallery.all %}
<h2 class="center">{{n.gallery_name}}</h2>
<hr>
{% for n in gallery.images_set %}
<div class="grid_4">
{{ n.image }}
</div>
{% endfor%}
Try something along the lines of:
# models.py
class Gallery(models.Model):
name = models.CharField(max_length=30)
description = models.CharField(max_length=200, null=True)
images = models.ManyToMany(Image)
class Image(models.Model):
title = models.CharField(max_length=30)
caption = models.CharField(max_length=200, null=True)
image = models.ImageField(upload_to=SOMEPLACE_FOR_MEDIA)
From here you should be able to do things like:
image = Image.objects.get(title="Girl Holding Cheese")
related_galleries = image.gallery_set.all()
or something similar as needed to pull what you want. The same goes the other way. To pull all images in a gallery you would do
gallery = Gallery.objects.get(name="Cheesy Wimmin")
related_images = gallery.images.all()
Though the assignments at the end aren't necessary, I usually just pass gallery.images.all() or image.gallery_set.all() directly. Note the "_set" at the end of the reference from the object that does not contain the M2M definition.
On the subject of direct usage, you can do compound references like
Image.objects.get(title="Girl Holding Cheese").gallery_set.all()
as well, but you have to decide when this makes code more clear and concise and when it just makes it more confusing to read later.
I hope this put you in the right direction.
Update
In your comment below you noticed that you cannot do
images = Images.objects.filter(gallery_set="Cheesy Wimmins")
related_galleries = images.gallery_set.all()
This is because you would be trying to filter() or all() on a queryset, not an individual model. So to make this work you can use a for loop in your template. Something like
# views.py
galleries = Gallery.objects.all()
return render(request, 'some/template.html', {'galleries': galleries})
And then
<!-- templates/some/template.thml -->
{% for gallery in galleries %}
<div class="gallery">
<h2>{{ gallery.name }}</h2>
{% for item in gallery.images.all %}
<div class="image">
{{ item.image }}
</div>
{% endfor %}
</div>
{% endfor %}
or something like this. Of course, you need to do whatever formatting steps you want to make this look right, but that's a way to get at your data.
The problem is with the {% for n in gallery.images_set %} bit in your template. images_set is a related manager, not a queryset. To get a queryset, you need to call all or another of the DBAPI methods that return querysets. So, just change it to gallery.images_set.all, and you're good.
gallery is a QuerySet - it doesn't have a images_set.
This is where naming your variables more appropriately can easily start preventing these problems: for example, galleries would be more appropriate for a list of Gallery objects... then, galleries.images_set would immediately raise red flags.
Anyways, you need to call images_set on what you've called n
{% for n in gallery.all %}
<h2 class="center">{{n.gallery_name}}</h2>
<hr>
{% for n in n.images_set.all %}
<div class="grid_4">
{{ n.image }}
</div>
{% endfor%}