Django random object and use in template - django

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

Related

how to use a forloop such that the if the "IF" statement in the loop is satisfied the loop ends in Django Templates

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

Django: how to get the second last object from database?

I have a model for news. I add news to the database with Django admin. Model Post consists of title, body and image.
On the main.html page of my project i have a carousel with 3 slides.
I already have some news in my database, and I want to display the last, the second last and so on images in that carousel.
My question is: what code should I add in html to display the last, the second last and the third last images?
<img src="???"> {# the last image #}
<img src="???"> {# the second last image #}
<img src="???"> {# the third last image #}
You can try like this:
posts= Post.objects.all().order_by('-id')
last_post = posts[0]
second_last_post = posts[1]
third_last_post = posts[2]
last_three_posts = posts[0:3]
But make sure to have atleast three posts or else it will throw index error
# views.py
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'home.html'
posts = Post.objects.all().order_by('-id')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = {
'carousel_posts': self.posts[:3],
}
return context
Use this make a for loop over the carouser_posts keyword in your template and extract the information you need.
<div class="carousel-inner">
{% for post in carousel_posts %}
<div class="carousel-item active">
<img src="{{post.image}}">
<p>{{post.title}}"</p>
</div>
{% endfor %}
</div>
UPDATE
Answer to your update. Using context keyword carousel_posts provided by our HomePageView we can access post objects one by one using for loop.
In template, assuming that your Post model has an image field called image.
{% for post in carousel_posts %}
<img src="{{ post.image.url }}">
{% endfor %}

Django No Reverse Match Error

I've been working on this error for the better part of a week and this is the error I keep receiving:
>
NoReverseMatch at /practice/practice/2/saleinfoedit/
Reverse for 'car_detail' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['practice/practice/(?P<pk>\\d)$']
I've simplified the code to what are the relevant parts of the error, I think. The idea is to have a list page of cars and when you click on the car link you can edit the Sale History of the vehicle. Eventually I'll setup formsets for this part, but babysteps. Here's the relevant code:
models.py
class Car(models.Model):
car_name = models.CharField(max_length=200)
color = models.CharField(max_length=200)
age = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse('practice:car_detail',kwargs={'pk':self.pk})
def __str__(self):
return '%s' %(self.car_name)
class SaleInfo(models.Model):
car_name = models.ForeignKey(Car, on_delete=models.CASCADE,)
price = models.CharField(max_length=100)
date = models.CharField(max_length=100)
comments = models.CharField(max_length=200)
def __str__(self):
return '%s' %(self.car_name)
def get_absolute_url(self):
return reverse('practice:car_detail',kwargs={'pk':self.pk})
views.py
class IndexView(generic.ListView):
template_name = 'practice/carlist.html'
context_object_name = 'latest_car_list'
def get_queryset(self):
return Car.objects.all()
class DetailView(generic.DetailView):
model = Car
form_class = CarForm
template_name = 'practice/car_detail.html'
class UpdateView(generic.UpdateView):
model = Car
form_class = CarFormEdit
class SaleInfoUpdateView(generic.UpdateView):
model = SaleInfo
form_class = SaleInfoFormEdit
template_name = 'practice/saleinfo_form.html'
urls.py
app_name = 'practice'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'), # shows lists of Cars via Car Name
url(r'^practice/(?P<pk>\d)$', views.DetailView.as_view(), name='car_detail'),
url(r'^practice/(?P<pk>\d)/edit/$', views.UpdateView.as_view(), name='car_edit'),
url(r'^practice/(?P<pk>\d)/saleinfoedit/$', views.SaleInfoUpdateView.as_view(), name='saleinfo_edit'),
]
car_detail.html
{% extends 'practice/car_base.html' %}
{% block post_content %}
<div class="col-md-8">
<a class='btn btn-primary' href="{% url 'practice:car_edit' pk=car.pk %}">
<span class='glyphicon glyphicon-pencil'></span>
</a>
<p>{{ car.car_name|safe }}</p>
<p>{{ car.color|safe }} {{ car.age|safe }} </p>
<a class='btn btn-primary' href="{% url 'practice:saleinfo_edit' pk=car.pk %}">
<span class='glyphicon glyphicon-pencil'> SaleInfo</span>
</a>
</div>
{% endblock %}
saleinfo_form.html
{% extends 'practice/car_base.html' %}
{% block post_content %}
{% load bootstrap3 %}
<form class="" action="" method="post">
{% csrf_token %}
{% bootstrap_form form %}
<!-- {{form.as_p}} -->
<input type="submit" name="" value="Update">
</form>
{% endblock %}
I can post my templates too if necessary. I'm sure it's something simple I keep looking past, but after a few days I'm lost.
According to the error message, the parameter pk you have given to the {% url %} tag is empty:
...with keyword arguments '{'pk': ''}'
There are two common reasons for that:
You have a typo in your variable name in your template.
You are creating a new object, so it doesn't have an ID/PK yet.
Your templates look fine though, so here is how I would debug it:
Remove {% url %} tags one after the other until you find the actual culprit.
Print out the variable you pass as keyword argument pk.
One more thing: \d matches exactly one digit, so your URL patterns will stop working once you have more than 9 cars. You have to add a + to match one or more digits:
url(r'^practice/(?P<pk>\d+)/edit/$', views.UpdateView.as_view(), name='car_edit'),
Also, like #wencakisa remarked, URLs in Django usually end with a slash, but that isn't mandatory.
Edit: I just noticed two more things:
your SaleInfo.get_absolute_url method uses practice:car_detail. Shouldn't that be saleinfo_edit?
in the car_detail.html template, you use {% url 'practice:saleinfo_edit' pk=car.pk %}. That won't work. If there is only one SaleInfo per car, use a OneToOneField instead of a ForeignKey in your model. Then you can do something like:
{% if car.sale_info %}
{% url 'practice:saleinfo_edit' pk=car.sale_info.pk %}
{% endif %}

Models inherit fields from other models in Django

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

django url from another template than the one associated with the view-function

Heyy there,
i have for example a view function like this:
def profile_view(request, id):
u = UserProfile.objects.get(pk=id)
return render_to_response('profile/publicProfile.html', {
'object_list': u,
},
context_instance=RequestContext(request))
and the url:
url(r'^profile_view/(?P\d+)/$',
profile_view,
name='profile_view'),
my problem is that i want to use the function's url in another template too, for example
in the search in blog template, where people see the posts of some persons, and they want to navigate to their profile.
the search view may look like that:
def searchn(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(post__iexact=query)
)
results = New.objects.filter(qset).distinct()
else:
results = []
return render_to_response('news/searchn.html',{
'results': results,
'query': query},
context_instance=RequestContext(request))
and the template:
{% if query %}
Results for "{{ query|escape }}":
{% if results %}
<ul>
{% for object in results %}
<li>{{ object.post }} <a href='../../accounts/profile_view/{{object.id}}/'> {{ object.created_by }} </a> {{object.date}} </li>
{% endfor %}
</ul>
{% else %}
<p>No posts found</p>
{% endif %}
{% endif %}
there, in created_by, i'd like to put a link to my user profile, but the user profile view doesn't 'point' to this template.
What shoul i do?
Thanks!
You usually don't call model or view functions from a template, you link to URLs which in turn be dispatched to view functions.
Dana: I hope I understand your problem correctly, but if you want to use the data from two "functions", you could achieve the same method by creating a model that links the two data fields you are using together, using a foreign key.
So you'd have
class MyCustomModel(models.Model):
profile = models.ForeignKey(Profile, unique=True)
otherdata = models.CharField("Some other field", blank=True, null=True)
You could then call your view on this model.