How to use one view on two different pages in Django? - django

I'm building a site with Django 1.5.1. I have Album and Category models defined:
###models.py
class Category(models.Model):
title = models.CharField(max_length=200, unique=True)
class Album(models.Model):
category = models.ForeignKey(Category, related_name='albums')
I've generated a menu to list automatically Categories and their related Albums with this view and template:
###views.py
def index(request):
categories = Category.objects.all()[:5]
context = {'categories': categories}
return render(request, 'gallery/index.html', context)
def detail(request, album_id):
album = get_object_or_404(Album, pk=album_id)
return render(request, 'gallery/detail.html', {'album': album})
###index.html
{% for category in categories %}
{% with category.albums.all as albums %}
{{ category.title }}
{% if albums %}
{% for album in albums %}
{{ album.title }}<br>
{% endfor %}
{% endif %}
{% endwith %}
{% endfor %}
Biography
I also have a view to show each album as a gallery indicates to detail.html. I want to show menu list beside each gallery so I used {% include "gallery/index.html" %} tag at the begining of detail.html. But menu list doesn't show up when detail.html loads, I just see Biography fixed link.
Here is my question: How should I import menu list generated in index.html in detail.html too?

index.html expects to receive categories variable in order to create the menu. If you want to include it in some other template you have to pass the categories variable to the other template for the included one. If you have some conflicts with names you can also pass variables to the include tag like this:
{% include 'include_template.html' with foo=bar%}
So the included template can use the variable foo which has the value bar.
For example, you need to pass the categories variable to the context generating for the detail.html like this:
def detail(request, album_id):
categories = Category.objects.all()[:5]
album = get_object_or_404(Album, pk=album_id)
return render(request,
'gallery/detail.html',
{'album': album,
'categories':categories}
)
And the line that includes the index.html template inside the detail.html template should remain like it is in the question:
{% include "gallery/index.html" %}
What I just did was pass the categories variable needed by index.html for rendering the menu to the detail.html template, which in turn, will pass it to all included template (index.html).
That should get the menu working from within detail.html template.
Hope it helps.

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 multi-level extends not appearing

With Django 1.10 I am building a blog/portfolio hybrid website. Besides Mysite, I have two apps, Blog and Portfolio. Within blog/templates/ I have an index.html that displays content from both Blog and Portfolio (the 15 most recent blog posts and 5 most recent portfolio projects).
So the index.html page looks like this:
http://imgur.com/y2UqBSS
As you can see data is not appearing. However, it does when I navigate to the Blog page and Portfolio page. For example, the Blog page:
http://imgur.com/7P922Ga
I am figuring that this issue is related to the mult-level extends that I have going on. Because the Blog and Portfolio pages both display content from the database, it makes me think that the models are O.K but something is up with the views. The index.html extends the base_generic.html template, and my recent_blog_posts.html and recent_portfolio_pieces.html extends the index.html.
I am not sure how to remedy this issue. Any suggestions what I'm doing wrong?
Project structure
mysite/
---blog/
------static/
---------css/
---------images/
------------blogpostimages/
------------favicon.ico
------templates/
---------blog/
------------blog_post.html
------------blog_list.html
------------recent_blog_posts.html
---------base_generic.html
---------index.html
---------bio.html
---------resume.html
------admin.py
------apps.py
------models.py
------tests.py
------urls.py
------views.py
---portfolio/
------static/
---------css/
---------images/
------------portfoliopieceimages/
------templates/
---------portfolio/
------------portfolio_piece.html
------------portfolio_list.html
------------recent_portfolio_pieces.html
------admin.py
------apps.py
------models.py
------tests.py
------urls.py
------views.py
---mysite/
------settings.py
------urls.py
------wsgi.py
manage.py
db.sqlite3
requirements.txt
blog/views.py
from django.shortcuts import render
from django.views import generic
# Create your views here.
from .models import Blog
def index(request):
"""
View function for home page of site.
"""
# Generate most recent blog post
title = Blog.objects.all()
post_date = Blog.objects.all()
get_absolute_url = Blog.objects.all()
# Render the HTML template index.html with the data in the context variable.
return render(
request,
'index.html',
context={'title': title,
'post_date': post_date,
'get_absolute_url': get_absolute_url,
}
)
def recent_blog_posts.html(request):
blog = Blog.objects.order_by('-post_date')[0:11]
return render(request, 'index.html', {'blog': blog})
class BlogListView(generic.ListView):
"""
Generic class-based view for a list of blog posts.
"""
model = Blog
paginate_by = 20
def get_queryset(self):
return Blog.objects.order_by('-post_date')
class BlogDetailView(generic.DetailView):
"""
Generic class-based detail view for a blog post.
"""
model = Blog
def bio(request):
return render(
request, 'bio.html'
)
def resume(request):
return render(
request, 'resume.html'
)
index.html
<div>
<h2>Recent Blog Posts</h2>
<div>
{% block blogs %}
{% if blog_list %}
{% for blog in blog_list %}
<article>
<header>
<h4><small>{{blog.post_date}} ยป </small>{{ blog.title }}</h4>
</header>
</article>
{% endfor %}
{% else %}
<p>Unfortunately, there are no blog posts yet.</p>
{% endif %}
</div>
{% endblock %}
</div>
<div>
<h2>Portfolio</h2>
{% if portfolio %}
{% for portfolio in portfolio %}
<div class="col-xs-12 col-sm-6 thumb">
<a class="thumbnail" href="{{ portfolio.get_absolute_url }}">
{% load static %}
<img class="img-responsive" src="{{ portfolio.cover_image }}" alt="">
<p>{{ portfolio.title }}</p>
<p>{{ portfolio.client_name }}</p>
</a>
</div>
{% endfor %}
{% else %}
<div>
<p>Unfortunately, there are no portfolio pieces yet.</p>
</div>
{% endif %}
You must use the variable pass to your context, in your template.
So you have to pass the variable blog_list to your context to iterate on it in your template.
Example below: (only for blog_list)
def index(request):
"""
View function for home page of site.
"""
blog_list = Blog.objects.all()
# Render the HTML template index.html with the data in the context variable.
return render(request, 'index.html', context={'blog_list': blog_list})
You need to pass the data to the template in the context. I don't really understand what you're trying to do in the index by setting and passing the variables in the context:
# Generate most recent blog post
title = Blog.objects.all()
post_date = Blog.objects.all()
get_absolute_url = Blog.objects.all()
Blog.objects.all() returns a queryset with all blog instances, not a title/post_date/get_absolute_url of a single blog instance.
In the template you refer to two context variables: blog_list and portfolio. You don't set the variables in the index. I would also avoid a statement like that:
{% for portfolio in portfolio %}
Don't use the same variable name as the variable you iterate over, change the latter to portfolio or portfolio_list.
To make it work you might try this:
index.py
def index(request):
blog_list = Blog.objects.all()
portfolio_list = Portfolio.objects.all()
return render(request, 'index.html',
context={'blog_list': blog_list, 'portfolio_list': portfolio_list}
)
In the index.html file change:
{% for portfolio in portfolio %}
to
{% for portfolio in portfolio_list %}

Combining model data to form url pattern

I am generating a generic list right now, with the following code:
views.py
class ServiceReportIndex(LoginRequiredMixin, ListView):
model = TblServiceRecords
context_object_name = 'all_servicereports'
login_url = 'login'
template_name = 'servicereport/servicereport_index.html'
def get_context_data(self, **kwargs):
context = super(ServiceReportIndex, self).get_context_data(**kwargs)
context['companies'] = TblCompanies.objects.all()
return context
In my template, I want to generate a URL using both of the models. The TblServiceRecords model contains a column that references the company_id, which is the primary key of the appropriate company in the TblCompanies model. I want to use the company_name from the Companies model in my list view. How would I go about doing that? I'm sure it's simple but I can't seem to get my url tags done correctly.
<div class="col-sm-4">
<p>Company Name</p>
{% for servicereport in all_servicereports %}
<p>{% for servicereport.company_id in companies.company_id %} {{ companies.company_name }} {% endfor %}</p>
{% endfor %}
</div>
Also, how can I be sure my views.py is set up correctly for multiple model functionality? I ask because if I put
{% for company_name in companies %}
{{companies.company_name}}
{% endfor %}
In my template, nothing comes up, but there are no errors either.
Probably you cannot see companies bacause of this:
{{companies.company_name}}
companies is queryset and it does not have company_name property.
Try this:
{% for company_name in companies %}
{{company_name.company_name}}
{% endfor %}

How do I access unrelated models within a Django template?

I want to use an app to create a menu that is easy to edit with the admin interface. Something like this:
class Menu_item
name = models.CharField()
item_url = models.URLField()
My template looks something like this:
{% extends base.html %}
div ID="nav"
{{ foo.navbar.? }}
/div
div ID="Content"
{% block content %}{% endblock %}
/div
I want div#nav to contain a ul based upon the above model but just can't figure out how to accomplish this. It seems like an object_list generic view would be great but, the URL accesses the view for the model that populates div#content. Does anyone have any suggestions? Is there a way to access a generic view without a URL?
Thank you.
I have discovered a solution. Inclusion Tags.
What I did was create an inclusion tag, which amounts to a simple custom template tag (django provides a shortcut for you!).
Here is what I did:
Ignore views.py - It will not be used in this case
Create a sub-directory in the app's dir called "templatetags" containing init.py and get_navbar.py (or whatever you want your tag to be):
mysite/
navbar/
templatetags/
__ init__.py (without the space befire init)
get_navbar.py
I changed my navbar.models to look like this:
from django.db import models
class Menu_choice(models.Model):
name = models.CharField(max_length=30)
def __unicode__(self):
return self.name
class Menu_item(models.Model):
name = models.CharField(max_length=20)
navbar = models.ForeignKey(Menu_choice)
item_url = models.CharField(max_length=200)
item_desc = models.CharField(max_length=30)
def __unicode__(self):
return self.name
This allows me to create a Menu_choice object in the admin interface for each layer of navigation (primary, secondary, etc)
get_navbar.py looks like this:
from navbar.models import Menu_choice, Menu_item
from django import template
register = template.Library()
#register.inclusion_tag('navbar/navbar.html')
def get_navbar(navBar):
navbar = Menu_item.objects.filter(navbar__name=navBar)
return { "navbar": navbar }
(Note navBar as opposed to navbar)
Create the navbar.html template:
<ul>
{% for menu_item in navbar %}
<li><a href="{{ menu_item.item_url }}">{{ menu_item.name }}&lt/a>&lt/li>
{% endfor %}
</ul>
Finally, I inserted the following into my base.html:
{% load get_navbar %}
And where I want primary navigation:
{% get_navbar "primary" %}
Note: the quotes around the string you are sending to your inclusion tag are important. I spent a ridiculously lengthy bit of time trying to make it work before I figured that out.
Also a big thank you to dikamilo for the help.
First, in you view, get data from db:
def index(request):
navbar = Menu_item.objects.all()
return render_to_response( 'you_template.html',
{ 'navbar': navbar }, context_instance = RequestContext (request ) )
And in template:
<div id="nav">
<ul>
{% for i in navbar %}
<li>{{ i.name }}</li>
{% endfor %}
</ul>
</div>

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.