I'm trying to create a query in Django that calls unique rows (using distinct) that meet some condition of a filter (using filter)
here are the used files :
views.py
def cat_details(request, pk):
current_user = request.user
selected_cat = get_object_or_404(Category, pk=pk)
selected_items = ItemIn.objects.all().filter(item_category=selected_cat).values_list('item_name', flat=True).distinct()
all_cats = Category.objects.all()
cat_count = all_cats.count()
item_count = ItemIn.objects.values_list('item_name', flat=True).distinct().count() # returns a list of tuples..
#all_units = Item.objects.aggregate(Sum('item_quantity'))['item_quantity__sum']
context = {
#'all_units': all_units,
'item_count': item_count,
'cat_count': cat_count,
'selected_items': selected_items,
'selected_cat': selected_cat,
'current_user': current_user,
}
return render(request, 'townoftech_warehouse/cat_details.html', context)
The variable called selected_items is where the problem!
and here is how I used this view function
HTML
{% extends 'townoftech_warehouse/base.html' %}
{% block content %}
{% load static %}
<div class="card">
<div class="card-header">
الاصناف فى المخزن
</div>
<div class="card-body">
<h3 align="right"> {{ selected_cat }}</h3>
<footer><button type="button" class="btn btn-dark">تعديل إسم الفئة</button>
<button type="button" class="btn btn-dark">مسح الفئة</button>
</footer>
</div>
</div>
<div style="overflow: auto;height: 280px">
<table class="table table-bordered">
<tbody>
{% for item in selected_items %}
<tr align="right">
<th scope="row">{{ item.item_name }}</th>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<br>
<br>
<div align="right"><button type="button" class="btn btn-danger">رجوع</button></div>
{% endblock %}
whenever I try to open this page the error I get is :
error
NoReverseMatch at /category/15/
Reverse for 'items' with arguments '('',)' not found. 1 pattern(s) tried: ['item\\/(?P<pk>[0-9]+)\\/$']
update
I've removed the link from the HTML files
changed
{% for item in selected_items %}
{{ item.item_name }}
{% endfor %}
to be like
{% for item in selected_items %}
<tr align="right">
<th scope="row">{{ item.item_name }}</th>
</tr>
{% endfor %}
the error disappeared but it gives me a list of empty values now!
selected_items variable contains list of string objects(item names). String dont have pk attribute, so item.pk in template returns nothing.
Istead of values_list you neet pass to template list of objects. If you are using postgres you can use distinct() with argument.
selected_items = ItemIn.objects.all().filter(item_category=selected_cat).distinct('item_name')
Otherwise you can try to use values with 'item_name', 'pk':
selected_items = ItemIn.objects.all().filter(item_category=selected_cat).values('item_name', 'pk').distinct()
Related
I am working with the omdb api (https://www.omdbapi.com/) and I was able to get the search results to display, but now I am trying to get the movie details when I click on each search result.
Right now I am getting a response of 'Not Found' which I think has something to do with retrieving the movie id, but I haven't been able to figure it out. I appreciate any tips/advice you may have.
app.py:
#app.route('/movies/search_results', methods=['GET'])
def show_search_results():
"""Logic for sending an API request and displaying the JSON response as an HTML. Expects a GET request."""
movies = []
s = request.args.get("s")
results = requests.get(f"{API_BASE_URL}search/movies/",params={"s": s}).json()
results = results['Search']
for movie in results:
movies.append(movie)
return render_template('movies/search_results.html', movies=movies, s=s, id=movie['imdbID'])
#app.route('/movies/<int:id>', methods=['GET'])
def show_movie_details(id):
"""For displaying information about the individual movie. Not a list. GET request only."""
movie = requests.get(f'{API_BASE_URL}movie/{id}')
print(movie)
return render_template('movies/detail.html', movie=movie, id=id)
search_results.html:
{% extends 'base.html' %}
{% block title %} {% endblock %}
{% block nav1 %} active {% endblock %}
{% block content %}
<div class="container-fluid" id="movie-list">
<div class="row row-cols-2 justify-content-center mt-2" style="--bs-gutter-x:0; ">
<p> Search Results for "{{s}}"</p>
<table class="movie-table">
<thead>
<tr>
<th>IMDB ID</th>
<th>Movie Title</th>
<th>Year Released</th>
</tr>
</thead>
<tbody>
{% for movie in movies %}
<tr>
<td>
<a href="/movies/{{id}}" id="id">
{{ id }}
</a>
</td>
<td>
{{ movie['Title'] }}
</td>
<td>
{{ movie['Year'] }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
{% block footer %} {% endblock %}
movies/details.html:
{% extends 'movies/search_results.html' %}
{% block content %}
<div class="card text-center" style="width: 18rem;">
<div class="card-body">
<!-- <div class="d-flex justify-content-between align-items-center mb-3 "> -->
<h5 class="card-title text-center"> Title: {{ movie['Title'] }} </h5>
<form method="POST" action="/movie/add_like/{{ movie['imdbID'] }}" id="messages-form">
<button class="btn btn-sm float-left">
<i class="fa fa-thumbs-up"></i>
</button>
</form>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
</div>
</div>
{% endblock %}
I tried not using int:id in the route and just redirecting to movies/detail.html, but I was still not receiving any results and just viewing the template without any API information on it.
I have an issue with flask paginate. Upon advice here at Stackoverflow I solved an initial paginate typeerror by changed my code so the paginate takes keywords. This gave me a new issue, since my code now returns an empty list of tickers.
The issue seems to be related to the implementation of paginate with keywords and the filter_by function, since my code returns a list of stocks from all exchanges when i remove the filter for CPH. So below code is working, but gives my tickers for all exchanges and obviously not only the ones with exchange='CPH':
#app.route("/stocks/cphstocks")
#login_required
def cphstocks():
page = request.args.get('page', 1, type=int)
**tickers = Ticker.query.paginate(page=page, per_page=app.config['TICKERS_PER_PAGE'], error_out=False)**
next_url = url_for('cphstocks', page=tickers.next_num) \
if tickers.has_next else None
prev_url = url_for('cphstocks', page=tickers.prev_num) \
if tickers.has_prev else None
return render_template('cphstocks.html', title='Copenhagen Stocks', tickers=tickers.items, next_url=next_url,
prev_url=prev_url)
My code with keywords in paginate and the filtering is shown below. Does anyone know what I'm missing here, since it's not returning the tickers for exchange='CPH'? Advice is much appreciated!
#app.route("/stocks/cphstocks")
#login_required
def cphstocks():
page = request.args.get('page', 1, type=int)
tickers = Ticker.query.filter_by(index="CPH").paginate(
page=page, per_page=app.config['TICKERS_PER_PAGE'], error_out=False)
next_url = url_for('cphstocks', page=tickers.next_num) \
if tickers.has_next else None
prev_url = url_for('cphstocks', page=tickers.prev_num) \
if tickers.has_prev else None
return render_template('cphstocks.html', title='Copenhagen Stocks', tickers=tickers.items, next_url=next_url,
prev_url=prev_url)
And my jinja template:
{% extends "layout.html" %}
{% block content %}
<article class="media content-section">
<div class="media-body">
<h3>Copenhagen listed stocks</h3>
</br>
<p></p>
</div>
</article>
<div class="table-responsive">
<table class="table table-bordered table-hover table-sm w-auto small">
<caption>Stocks listed at Copenhagen stock exchange</caption>
<thead>
<tr>
<th class="text-left"scope="col">Ticker</th>
<th class="text-right"scope="col">Company</th>
</tr>
</thead>
<tbody>
{% for items in tickers %}
<tr>
<td scope="row"> <a href="{{ url_for('stockpage', id=items.id) }}">{{ items.yahoo_ticker }}</td>
<td class="text-right">{{ items.company_name }}</td>
</tr>
{% endfor %}
</tbody>
</table> {% if prev_url %}
Previous page
{% endif %}
{% if next_url %}
Next page
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="content-section">
<h3>New menu </h3>
<p class='text-muted'>You can put any information here you'd like.
<ul class="list-group">
<li class="list-group-item list-group-item-light">Visit Yahoo Finance</li>
<li class="list-group-item list-group-item-light">Update1</li>
<li class="list-group-item list-group-item-light">Calendars</a></li>
<li class="list-group-item list-group-item-light">etc</a></li>
</ul>
</div>
</div>
{% endblock content %}
Solved it by using filter instead of filter_by, so code that works is as below:
tickers = Ticker.query.filter(Ticker.exchange.contains('CPH')).paginate(page=page, per_page=app.config['TICKERS_PER_PAGE'], error_out=False)
Problem:
I am developing an application in django that uses paginator for the list page of a model, it is set to display 25 instances of the model per page, when i view the first page everything works fine but when i move to the second page it shows some values from the first again. i have checked the database and there is no data duplication either.
Cause/attempted solutions:
while not 100% i believe the problem originates from the order_by in the queryset but even after changing and removing the problem persists
Details:
snips of list for the first and second page
First: https://imgur.com/tyY5xu0
Second: https://imgur.com/UcGWEFN (note CO01, 4 and 5 appear again)
Views.py:
def view_jobs(request):
query_results = Job.objects.select_related('contract').order_by('-active', '-order_date')
paginate = Paginator(query_results, 25)
page_no = request.GET.get('page')
page_obj = paginate.get_page(page_no)
context = {"query_results": page_obj}
return render(request, 'view_jobs.html', context)
view_jobs.html:
<table id="PageTable" class="table table-striped">
<thead>
<tr>
<th class="dropdown">
<a class="dropdown-toggle" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<h6>Number</h6>
</a>
</th>
</tr>
</thead>
<tbody>
{% for item in query_results %}
<td>
<small>{{ item|filter_prep:"order" }}</small>
{% if item.help_text %}
<small style="color: grey">{{ item.help_text }}</small>
{% endif %}
{% for error in item.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pagination">
<span class="step-links">
{% if query_results.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ query_results.number }} of {{ query_results.paginator.num_pages }}.
</span>
{% if query_results.has_next %}
next
last »
{% endif %}
</span>
</div>
feel free to ask for any extra details.
I am working on a Supplier Management System. I have a page where users could view all suppliers on the application. on the same page, users can upvote and downvote suppliers, is there a way that i could make the supplier not able to upvote or downvote his self.
I am thinking if there is a way i can pass the supplier.user into the context which identifies the supplier, perhaps doing something like this but it doesn't seem to work. I could then pass this into the template {% if supplier.user == request.user %}content...{% endif %}
Presently, supplier.user return nothing.
views.py
def Viewsupplier(request):
title = "All Suppliers"
suppliers = User.objects.filter(user_type__is_supplier=True)
# Get the updated count:
suppliers_votes_count = {}
for supplier in suppliers:
upvote_count = supplier.upvotes
downvote_count = supplier.downvotes
supplier_count = {supplier: {'upvote': upvote_count, 'downvote': downvote_count } }
suppliers_votes_count.update(supplier_count)
context = {"suppliers":suppliers, "title":title, "suppliers_votes_count": suppliers_votes_count}
return render(request, 'core/view-suppliers.html', context)
view-supplier.html
<thead>
<tr>
<th>No</th>
<th>Email</th>
<th>Votes</th>
</tr>
</thead>
<tbody>
{% for supplier in suppliers %}
<tr>
<td>{{forloop.counter}}</td>
<td>{{supplier.email}}</td>
<td>
<div class="table-data-feature">
{% if supplier.user == request.user %}<a href="{% url 'upvote' supplier.id %}">{% endif %}
<button class="item" data-toggle="tooltip" data-placement="top" title="Like">
<i class="zmdi zmdi-thumb-up"></i></button>
{% if supplier.user == request.user %}</a>{% endif %}
<button>{{supplier.upvotes}}</button>
{% if supplier.user == request.user %}<a href="{% url 'downvote' supplier.id %}">{% endif %}
<button class="item" data-toggle="tooltip" data-placement="top" title="Dislike">
<i class="zmdi zmdi-thumb-down"></i></button>
{% if supplier.user == request.user %}</a>{% endif %}
<button>{{supplier.downvotes}}</button>
</div>
</td>
</tr>
{% empty %}
<tr><td class="text-center p-5" colspan="7"><h4>No supplier available</h4></td></tr>
{% endfor %}
</tbody>
I was able to solve the issue by using this templatetag in my templates:
{% if supplier.email != request.user.email %}content...{% endif %}
This worked easily because i already had the supplier info from the views already.
I want to override the django admin change_list page but i am not able to find how to get the actual object so that i can access the proerties like object.name
In the template they have this code
<tr >{% for item in result %}{{ item }}{% endfor %}</tr>
where is the actual object which i can use
EDIT:
It look like result is the row and item is the column. i want something like result.pk
This yield the list if results
https://github.com/django/django/blob/master/django/contrib/admin/templatetags/admin_list.py#L175
The context fed to change_list.html includes a cl entry corresponding to a contrib.admin.views.main.ChangeList object, which is the object containing the result list.
You can access the result list directly like this:
{% for object in cl.result_list %}
{{ object.field }}
{% endfor %}
The change list results are rendered as part of the change_list_results.html template through use of the result_list template tag. The change_list_results.html template also has the cl context variable present when rendering the template.
When the Django template is iterating over result in your example, result is a ResultList object which contains pre-rendered html, the underlying object which is being rendered is not made available.
To override the template at this level, it's likely you'll need to implement your own result_list type template tag which can return a list of results with the underlying objects attached as an attribute to each result.
In short it is likely that you will need to:
Create your own result_list template tag, based on the Django's implementation. Rather than have it return a results context as a list of ResultList prerendered html, have it return results containing objects which are both capable of being rendered to html, as well as each item having the original underlying object attached for later use in your template.
Override the change_list.html template to use your new tag rather than Django's result_list template tag.
Override the change_list_results.html template to make use of the extra information available from your template tag such as the presence of each underlying object.
As you've probably gathered, the admin app is quite tightly integrated through the layers. Changing its operation is non-trivial and requires changes and overrides in multiple parts of the source.
You can also override template you use for admin with "change_list_template" and then extend change_list template.
Like
class NoiseFilter200Admin(admin.ModelAdmin):
change_list_template = 'currencies/nf200_change_list.html'
list_filter = ['sectors',]
search_fields = ['name']
#list_filter = ( MarketCapFilter )
def market_cap_value(self, obj):
return obj.market_cap_value > 1000000000
def changelist_view(self, request, extra_context=None):
response = super().changelist_view(
request,
extra_context=extra_context,
)
try:
qs = response.context_data['cl'].queryset
except (AttributeError, KeyError):
return response
metrics = {
'total_volume': Sum('volume'),
'total_mcap': Sum('market_cap'),
}
response.context_data['nf_200_list'] = list(
qs
.values('rank','ticker','name','priceUSD','market_cap','volume','change_24h')
# .annotate(**metrics)
.order_by('-market_cap')[:200]
)
response.context_data['nf_200_totals'] = dict(
qs.aggregate(**metrics)
)
return response
Then in your template you go like this and iterate over results and you will have a look you want
{% extends "admin/change_list.html" %}
{% load humanize %}
{% load math_tags %}
{% block content_title %}
<h1> Noise Filter 200 </h1>
{% endblock %}
{% block result_list %}
<style>
.results table {
counter-reset: rowNumber;
}
.results tbody tr{
counter-increment: rowNumber;
}
.results tbody td.rank::before {
content: counter(rowNumber);;
}
</style>
<div style="margin: 10px 0;">Total MCap today: {{ nf_200_totals.total_mcap | intword }}, Total volume today: {{ nf_200_totals.total_volume | intword }} </div>
<div class="results">
<table>
<thead>
<tr>
<th>
<div class="text">
Rank
</div>
</th>
<th>
<div class="text">
Name
</div>
</th>
<th>
<div class="text">
Ticker
</div>
</th>
<th>
<div class="text">
PriceUSD
</div>
</th>
<th>
<div class="text">
Market cap
</div>
</th>
<th>
<div class="text">
Volume
</div>
</th>
<th>
<div class="text">
Change 24
</div>
</th>
</tr>
</thead>
<tbody>
{% for row in nf_200_list %}
<tr class="{% cycle 'row1' 'row2' %}">
<td class="rank"> </td>
<td> {{ row.name }} </td>
<td> {{ row.ticker }} </td>
<td> $ {{ row.priceUSD|to_string }} </td>
<td> {{ row.market_cap | intword }} </td>
<td> {{ row.volume | intword }} </td>
<td> {{ row.change_24h | percent}} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
{% block pagination %}{% endblock %}
Downside is you may use some functionality here that you get (like sorting).
My solution was to override result_list templatetag to add object in results
add custom result_list tag, I named it result_list_ext
from django.contrib.admin.templatetags.admin_list import result_headers, result_hidden_fields, ResultList, \
items_for_result
from django.contrib.admin.templatetags.base import InclusionAdminNode
from django.template import Library
register = Library()
def results(cl):
if cl.formset:
for res, form in zip(cl.result_list, cl.formset.forms):
yield (res, ResultList(form, items_for_result(cl, res, form)))
else:
for res in cl.result_list:
yield (res, ResultList(None, items_for_result(cl, res, None)))
def result_list(cl):
"""
Display the headers and data list together.
"""
headers = list(result_headers(cl))
num_sorted_fields = 0
for h in headers:
if h['sortable'] and h['sorted']:
num_sorted_fields += 1
return {
'cl': cl,
'result_hidden_fields': list(result_hidden_fields(cl)),
'result_headers': headers,
'num_sorted_fields': num_sorted_fields,
'results': list(results(cl)),
}
#register.tag(name='result_list_ext')
def result_list_tag(parser, token):
return InclusionAdminNode(
parser, token,
func=result_list,
template_name='change_list_results.html',
takes_context=False,
)
Actually all that we changed here is add res in yield
yield (res, ResultList(None, items_for_result(cl, res, None)))
In change_list_results.html in for loop we receive result and object. That's it, now we have object in our for loop and can use it
{% for object, result in results %}
{% if result.form.non_field_errors %}
<tr id="id_{{ object.pk }}" class="grp-errors">
<td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td>
</tr>
{% endif %}
<tr id="id_{{ object.pk }}" class="grp-row {% cycle 'grp-row-even' 'grp-row-odd' %}">
{% for item in result %}{{ item }}{% endfor %}
</tr>
{% endfor %}
Use custom result_list_ext instead of django result_list and don't forget to load this new template tag in the beginning of template with load tag
<!-- CHANGELIST-RESULTS -->
{% block result_list %}
{% result_list_ext cl %}
{% endblock %}