Tell what model a queryset is from in the template - python

I am creating a list containing items from two different models and passing it to my template. Here is my view function:
def newsfeed(request):
Text = Post.objects.all().order_by('-Timestamp')
Images = ImagePost.objects.all().order_by('-Timestamp')
Posts = []
while True:
if Text[0].Timestamp >= Images[0].Timestamp:
Posts.append(Post.objects.get(id=Text[0].id))
Text = Text.exclude(id=Text[0].id)
else:
Posts.append(ImagePost.objects.get(id=Images[0].id))
Images = Images.exclude(id=Images[0].id)
if len(Text) == 0:
for i in Images:
Posts.append(i)
break
elif len(Images) == 0:
for i in Text:
Posts.append(i)
break
print(Posts[:6])
return render(request, 'campaign/newsfeed.html', {
"posts": Posts,
})
I need a way to find out which model each item in the list was from in the template so that I know how to render the item. Is there a way to tell without sending further data to the template?

You can give both models (or their common super class) a method:
def model_name(self):
return self.__class__.__name__
And in the template, you can check:
{% for p in posts %}
{% if p.model_name == 'ImagePost'%}
# ...
{% endif%}
{% endfor %}
If these are models from third-party packages, you can always just set attributes in the view:
for i in Images:
i.model_name = 'ImagePost'
Posts.append(i)

Related

Struggles unpacking a two-dimensional list in template

I'm passing a bunch of data into my template but am having a hard time breaking apart a zipped list of items. No matter what I try, I always get the following error.
Need 2 values to unpack in for loop; got 0.
Heres my code:
views.py
import requests
from django.shortcuts import render
from django.http import HttpResponse
dictionary, words = [[], []], []
def home(request, username='johnny'):
template_name = 'main/index.html'
url = "https://www.duolingo.com/users/{}".format(username)
getUserData(url)
context = {
'username': username,
'dictionary': dictionary,
'words': words,
}
# print(context)
return render(request, template_name, context)
def getUserData(url):
response = requests.get(url)
userdata = response.json()
wordlists, explanations = [], []
for language in userdata['language_data']:
for index in userdata['language_data'][language]['skills']:
if index.get('levels_finished') > 0:
wordList = index.get("words")
wordlists.append(wordList)
explanations.append(index.get("explanation"))
for wordItem in wordList:
words.append(wordItem)
dictionary = list(zip(wordlists, explanations))
relevant template
{% block content %}
{% for words, exp in dictionary %}
{{ words }}
{{ exp|safe }}
{% endfor %}
{% endblock %}
I've tested this code, it works.
Once I refactored in Django to put wordLists in an array with explanations, things go to hell. If I print(dictionary) at the end of the method, the data shows in the console. Not sure what else I'm missing.
Your problem is with scope. the dictionary(variable) which you are returning from home function(as context) and dictionary in getUserData function are not in same scope. So whenever you are updating getUserData method's dictionary, its not being updated in home. I would not recommend your approach for dictionary as its using global variable. I would recommend something like this:
def getUserData(url):
response = requests.get(url)
userdata = response.json()
wordlists, explanations, words = [], [], []
for language in userdata['language_data']:
for index in userdata['language_data'][language]['skills']:
if index.get('levels_finished') > 0:
wordList = index.get("words")
wordlists.append(wordList)
explanations.append(index.get("explanation"))
for wordItem in wordList:
words.append(wordItem)
return list(zip(wordlists, explanations)), words # return the value of dictionary from here
def home(request, username='johnny'):
template_name = 'main/index.html'
url = "https://www.duolingo.com/users/{}".format(username)
dictionary, words = getUserData(url) # catch value of dictionary
context = {
'username': username,
'dictionary': dictionary,
'words': words,
}
# print(context)
return render(request, template_name, context)

how to get nearby places using google places with python and flask

i am trying to get nearby places using googleplaces with python and flask
i am getting this error: (UnboundLocalError: local variable 'place_name' referenced before assignment)
here is my code:
#app.route('/Search', methods=['POST', 'GET'])
#login_required
def Search():
if request.method == 'POST':
query_result = google_places.nearby_search(
lat_lng={'lat':31.7917, 'lng' : 7.0926},
radius=500,
types=[types.TYPE_SHOPPING_MALL] or [types.TYPE_STORE])`
if query_result.has_attributions:
print(query_result.html_attributions)
for place in query_result.places:
place.get_details()
place_name = place.name
print(place.name)
place_rating = place.rating
print(place.rating)
place_location = place.get_location
print(place.get_location)
for photo in place.photos:
photo.get(maxheight=500, maxwidth=500)
photo.mimetype
photo.url
photo.filename
photo.data
return render_template('Search.html', place_name, place_rating, place_location)
else:
return render_template('Search.html')```
#Note: i am new to python in general
return render_template('Search.html', place_name, place_rating, place_location)
The above isn't valid syntax. When you pass the details to the template, you need to do it as:
return render_template('Search.html', name = place_name,
rating = place_rating, location = place_location)
The variables name, rating and location will then be accessible in the template as {{name}}, {{rating}} and {{location}}.
However, the way you have the for loops laid out means the first time the return statement is reached, it will stop the loop and return the template with these variables.
Perhaps this is what you want, but you may wish to pass query_result to the template, and implement a Jinja2 for loop in the template to print out the various place details. You would remove the for loops and replace that whole block with:
return render_template('Search.html', all_places = query_result)
Then in the template something like:
{% if all_places %}
{% for place in all_places %}
<p><b>{{place.name}}</b> has a rating of <u>{{place.rating}}</u></p>
{% endfor %}
{% else %}
<p>No places found.</p>
{% endif %}

Dynamically populate a choice field and get the non-chosen fields in the post data

I have a form where I'm trying to compare two objects (Cards) that are randomly selected from the database. In my form, I tried to dynamically populate a choicefield with the two cards, and in the view I'm trying to create an object (CardComparison(winning_card,losing_card)) with the choice of the user.
I've overwritten the __init__ of the form to dynamically populate the card choices, and it's working fine. The problem is that when the user selects a card, it only passes the selected card, and I'm not able to figure out which card is NOT chosen in the view.
I'm new to Django, and I've realized the more I struggle with this, a dynamic choice field may not be what I actually want to use, so a suggestion for a better method would also be greatly appreciated.
forms.py:
def get_new_comparison():
left_card = #get a random card (code hidden for cleanliness)
right_card = #get a second random card
left_card_choice = (left_card, left_card.name)
right_card_choice = (right_card, right_card.name)
return [left_card_choice, right_card_choice]
class CompareCardsForm(forms.Form):
def __init__(self, *args, **kwargs):
post_flag = False
if kwargs.get('post_flag'):
post_flag = kwargs.pop('post_flag')
super(CompareCardsForm, self).__init__(*args, **kwargs)
if post_flag and len(args) > 0:
card_name = args[0].get('cards_to_compare')
card_obj = Card.objects.get(name=card_name)
card_choice = [(card_obj,card_name)]
self.fields['cards_to_compare'] =
forms.ChoiceField(choices=card_choice, widget=forms.RadioSelect())
#Because everytime the __init__ is called the choices are randomized, I need to set the choices to the POST data's choices otherwise the selected card may not be in the form when it's trying form.is_valid()
else:
self.fields['cards_to_compare'] = forms.ChoiceField(choices=get_new_comparison(), widget=forms.RadioSelect())
views.py:
def CompareCards(request):
if request.method == 'POST':
form = CompareCardsForm(request.POST, post_flag=True)
if form.is_valid():
print(form.cleaned_data['cards_to_compare'])
# Here is where I want to create a new comparison using the two cards
return HttpResponseRedirect(reverse('comparecards'))
else:
print(form.errors.as_text())
else:
form = CompareCardsForm()
return render(
request,
'mtg_compare/comparecard.html',
{'form': form}
)
The solution to this is actually by using the template file. By adding hidden fields in the template file and by setting the choices to an accessible variable in the form, you can pull the fields and add them to the POST message, so that I'm able to access them in the view.
comparecard.html:
{% extends "base_generic.html" %}
{% block content %}
<h1>Compare Cards</h1>
<h2>Choose a card to win the matchup</h2>
<p>
<p>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.cards_to_compare }}
</table>
<input type="hidden" value="{{ form.card_list }}" name="var_name_1" />
<input type="submit" value="Submit" />
</form>
{% endblock %}
forms.py:
class CompareCardsForm(forms.Form):
def get_fresh_comparison():
return get_new_comparison()
def __init__(self, *args, **kwargs):
post_flag = False
if kwargs.get('post_flag'):
post_flag = kwargs.pop('post_flag')
super(CompareCardsForm, self).__init__(*args, **kwargs)
if post_flag and len(args) > 0:
card_name = args[0].get('cards_to_compare')
card_obj = Card.objects.get(name=card_name)
card_choice = [(card_obj,card_name)]
self.card_list = card_choice
self.fields['cards_to_compare'] = forms.ChoiceField(choices=card_choice, widget=forms.RadioSelect())
else:
self.card_list = get_new_comparison()
self.fields['cards_to_compare'] = forms.ChoiceField(choices=self.card_list, widget=forms.RadioSelect())
views.py:
def CompareCards(request):
if request.method == 'POST':
form = CompareCardsForm(request.POST, post_flag=True)
if form.is_valid():
print(request.POST.get('var_name_1'))
return HttpResponseRedirect(reverse('comparecards'))
else:
print(form.errors.as_text())

Django form, view, and forms.py not working together

My first foray into Django, allows the user to input a day of the week, and searches my database for restaurants open on that given day. Right now, the restaurant objects (Resto) have a days_open attribute, with each day separated by a comma (Monday, Tuesday, etc...).
When I input a day, the resulting page only displays the title and '[]' and I can't seem to get it to return the list of Resto objects. What is displaying the square brackets, and how do I go about fixing it to display the results of the search?
The code is below- my apologies if I neglected to include any relevant bits.
my forms.py:
from django import forms
from .models import Resto
class RestoSearch(forms.ModelForm):
class Meta:
model = Resto
fields = ('title', 'description', 'opening_hour', 'closing_hour')
models.py:
from django.db import models
class Resto(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
opening_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
closing_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
days_open = models.TextField(blank=True)
views.py:
from django.shortcuts import render
from django.http import Http404
from django.shortcuts import HttpResponse
from belize.models import Resto
from django.core.exceptions import *
from .forms import RestoSearch
def index(request):
return render(request, 'form.html')
def search(request):
form = RestoSearch()
if request.method == 'POST':
search_id=request.POST.get('textfield', None)
try:
#I think this is where my problem is
available = Resto.objects.filter(days_open = search_id)
html = ("<H1>Hello</H1>", available)
return HttpResponse(html)
except Resto.DoesNotExist:
return HttpResponse("Please try another day")
else:
return render(request, 'belize/form.html')
def restaurant_detail(request, id):
try:
restaurant = Resto.objects.get(id=id)
except Resto.DoesNotExist:
raise Http404('This restaurant does not exist')
return render(request, 'belize/restaurant_detail.html', {
'restaurant': restaurant,
})
template form.html:
<form method="POST" action="/search/">
{% csrf_token %}
<input type="text" name="textfield">
<button type="submit">Enter a day of the week</button>
</form>
I presume what you are trying to show is the RestoForm in that case the index method is not correct. It should be
def index(request):
form = RestoForm()
return render(request, 'form.html', {'form': form })
And then your template should change as
<form method="POST" action="/search/">
{% csrf_token %}
{{ form }}
<button type="submit">Enter a day of the week</button>
</form>
For additional details please see the examples at https://docs.djangoproject.com/en/1.9/topics/forms/#the-template
The [] means that your .filter() returned no results, its not surprising as you have a few issues with your code, lets start from the top:
You are declaring a form that you never use.
You are trying to catch an exception that is never raised by .filter()
You filter condition will only work for exact matches.
I've annotated your code as well:
def search(request):
form = RestoSearch() # You aren't using this form anywhere in your code?
if request.method == 'POST':
# Where is 'textfield' coming from?
search_id = request.POST.get('textfield', None)
try:
# If search id is "Tuesday", and a restaurant is
# open on monday and tuesday, so it has "Monday,Tuesday"
# in the days_open field, then this search will not
# return any results, because its looking for an exact
# match
available = Resto.objects.filter(days_open=search_id)
html = ('<H1>Hello World</H1>', available)
return HttpResponse(html)
except Resto.DoesNotExist:
# This exception is not raised by .filter(),
# .filter() will return an empty list, [] if no results are found
# so this entire try/except is not doing anything
return HttpResponse("Please try another day")
else: # in your code, this else is not indented correctly
return render(request, 'belize/form.html')
So there is a lot going on here, lets try something simple, starting with the template:
{% if results %}
{% for restaurant in results %}
{{ restaurant }}
{% endfor %}
{% else %}
Sorry, no results for your search. Try again.
{% endif %}
<form>
{{ form }}
<input type="submit" name="Search" />
</form>
Next, the search form:
class SearchForm(forms.Form):
search_field = forms.CharField('Search', strip=True)
Finally the view:
from django.db.models import Q
def search(request):
form = SearchForm(request.GET)
results = [] # Set results to an empty list
if form.is_valid():
needle = form.cleaned_data['search_field'].capitalize()
results = Resto.objects.filter(Q(days_open__startswith='{},'.format(needle)) |
Q(days_open__endswith=',{}'.format(needle)) |
Q(days_open__contains=',{},'.format(needle)) |
Q(days_open='{},'.format(needle)) |
Q(days_open='{}'.format(needle)))
return render(request, 'search.html', {'results': results, 'form': form})
Lets assume the user entered 'Thursday' as a search field. In this view you are searching for all restaurants whose days_open field:
Either starts with Thursday, or
Ends with ,Thursday or
Contains ,Thursday, in the middle or
Has the value Thursday, or
Has the value Thursday
Your template will then only show the results if there are any values to display; since an empty list [] is false, then the {% if results %} condition will fail, so on empty lists the template will display the error notice instead.
In your view, you only do the database check if someone enters something in the search field, that's what if form.is_valid(): does. In django, by default all form fields are required - so a blank form will return an error. Using this trick, we make sure we only search if someone enters a value in the search box.
The main action happens with all the Q() calls. A Q object is a way to do multiple queries and chain them together. It is used whenever you want to do an "or" or "and" type query. Our search is an "or" type query, because we want to return any results if the value in days_open matches any number of conditions.

pagination is applied not working in django

in my custom templates pagination is applied on it. the list that show on page is much bigger than i used pagination on it. the limit to display the list is wroking properly but when i click on the next button than it go on the else condition.
views.py :-
#csrf_exempt
def search(request):
if request.method == 'POST':
getchoice = request.POST['userchoice']
getfirstdate = request.POST['firstdate']
getseconddate = request.POST['seconddate']
if getchoice == '0':
getdata = applicationform.objects.filter(date__gte=getfirstdate , date__lte=getseconddate)
##### PAGINATION
searchpagination = Paginator(getdata ,5)
page = request.GET.get('searchpage')
try:
searchcontacts = searchpagination.page(page)
except PageNotAnInteger:
searchcontacts = searchpagination.page(1)
except EmptyPage:
searchcontacts = searchpagination.page(searchpagination.num_pages)
if getdata:
return render_to_response('registration/search_page.html', {'getdata':getdata ,'getchoice':getchoice ,'searchcontacts': searchcontacts})
else:
return HttpResponse('NO ITEMS FOUND ON THIS DATE')
else :
return render_to_response('registration/search_page.html')
custom template:-
{% if searchcontacts.has_previous %}
PREVIOUS
{% endif %}
{% if searchcontacts.has_next %}
NEXT
{% endif %}
Click on NEXT will be GET request and hence it is going in else section.
To continue search for next page, either you need to store search parameters in the session or pass it through url. And then use that in filter() query.

Categories

Resources