Display time stored in database in HTML (django) - html

I'm building a bus booking website using Django. Users fill From, To, Bus type(ordinary or Volvo) and date. Results display the buses available on that route on that particular date. I made three tables - Bus, Route, and Frequency. You can check my models.py and views.py here - https://dpaste.de/Oi6a I convert the date to the corresponding day of the week. I have filtered out the buses, However, I want to display time on the template. This is part of my views.py code:
def select(request):
bus_type = request.GET.get('bus_type')
bus_from = request.GET.get('bus_from')
bus_to = request.GET.get('bus_to')
date_string = request.GET.get('date')
date = datetime.strptime(date_string, '%Y-%m-%d')
day = calendar.day_name[date.weekday()]
kwargs = { '{0}__range'.format(day): ["00:00:00", "23:59:59"],}
qs = Frequency.objects.filter(bus__type_of_bus=bus_type, bus__route__location_from=bus_from, bus__route__location_to=bus_to, **kwargs)
context = {'qs': qs, 'date':date_string,}
template = 'select.html'
return render(request, template, context)
As you can see qs filters the buses available and is then passed to the template. This is a part of the template:
<div id="pricing-table" class="clear">
{% for info in qs %}
<div class="price_block">
<h3>{{ info.bus.type_of_bus }}<span>{% if info.bus.type_of_bus == 'Volvo' %} Rs 550 {% else %}Rs 330 {% endif %}</span></h3>
Book
<ul>
<li><b>Bus Number -</b> {{ info.bus.bus_number }}</li>
<li><b>Route -</b> {{ info.bus.route }}</li>
<li><b>Date -</b> {{ date }}</li>
<li><b>Time -</b> {{ info.day }}</li>
</ul>
</div>
In the last line of this HTML file, I have added info.day. What I want is that this displays the time that bus operates on the given day. For example, If a person searches a bus on 29th April. 29th April will be converted to corresponding day i.e. Friday. The frequency table has 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' and 'Sunday' as attributes and these all are TimeField(). qs filters the buses and passes to the template. I want {{ info.day }} to show the time when the bus operates. Instead, it shows nothing. But when I change {{ info.day }} to {{ info.Friday}} it start showing the time. How can I display the time without having to manually go and change the day in {{ info.(here) }} every time I search for a bus. I didn't make it very clear, but I hope you understand. Let me know if you don't.

You can create a custom template tag do this for you and a method on the model.
I haven't tested this code, but this should work with or without small adjustments
Add this method to the Frequency model:
def get_day_info(self, date):
# Where date_string would be a datestring
date = datetime.strptime(date_string, '%Y-%m-%d')
day = calendar.day_name[date.weekday()]
info_time = getattr(self, '{}'.format(day))
return info_time
And register this as a template tag.
#register.simple_tag
def get_day_info(info, date):
return info.get_day_info(date)
You would call it in the template like this:
<li><b>Time -</b> {% get_day_info info {{ date }} %}</li>

Related

Django How to make the condition correct on an HTML page - search bar

I try to do a search engine if the word in my DB thah I created then display the word on the HTML page and if not then nothing.. I did it right in VIEW but I can not apply it on the HTML page I searched the internet and did not find an answer I'm sure I fall for something stupid.
This is the view
def Search_word(request):
search = request.POST.get("search") #Grab the search item
return render(request,"search_page.html", {"search":search})
this is the html:
{%for i in Word.English_word%}
{%if search in Word.English_word%}
{{search}}
{%endif%}
{%endfor%}
and the urls:
path("Search_page",views.Search_word ,name="Search-page"),
models:
class Words(models.Model):
English_word = models.CharField(max_length=30)
Hebrew_word = models.CharField(max_length=30)
How_To_Remember = models.CharField(max_length=40)
Name = models.CharField(max_length=20)
pub_date = models.DateTimeField()
The problem is that even if the word is valid it does not show me anything ..
You should implement the filtering logic in the view, not in the template. Templates are for rendering logic, not business logic. Furthermore one should filter with the database, since databases are designed to do this.
The view thus looks like:
def Search_word(request):
search = request.POST.get('search')
items = Word.objects.filter(English_word__contains=search)
return render(
request,
'search_page.html',
{'search': search, 'items': items}
)
and then in the template we render this with:
{% for item in items %}
{{ item.English_word }}: {{ item.Hebrew_word }} <br>
{% endfor %}
You can use as lookup __contains to check if the English_word has a substring that is equal to search, with __icontains you check case-insensitive, with __iexact you look for Words that match search case-insensitive, and finally you can filter with Engish_word=search for an exact match.

Django Search Page with Query Pagination

Hello i have implemented a simple search form and search view to show search result. Now i want to paginate them. But there is a problem with the page in the url. My search url looks like ../search?q=Bla
Now if i try to add pagination like: ../search?q=Bla?page=2 (at least thats how i understand it would work) it takes the whole string after q= to my database filter. I took a look at how stackoverflow handles searching and pagination and found out using '&' here is the view code:
def search(request):
# get query search parameters
page = request.GET.get('page', 1)
query = request.GET.get('q', '')
# query none or empty
if query is None or query == '':
return redirect('home')
# query valid
else:
# icontains make sure to ignore character sensitivity
post_list = Post.objects.filter(title__icontains=query)
paginator = Paginator(post_list, 5)
try:
posts_l = paginator.page(page)
except PageNotAnInteger:
posts_l = paginator.page(1)
except EmptyPage:
posts_l = paginator.page(paginator.num_pages)
return render(request, 'search.html', {'post_list': posts_l, 'query': query})
and here the HTML Snippet:
{% if post_list.paginator.num_pages > 1 %}
<div class="pagination">
<span class="step-links mb-5 mx-auto">
{% if post_list.has_previous %}
<a class="mr-3" href="?page={{ post_list.previous_page_number }}&q={{ query }}">zurück</a>
{% endif %}
<span>Seite {{ post_list.number }} von {{ post_list.paginator.num_pages }}</span>
{% if post_list.has_next %}
<a class="ml-3" href="?page={{ post_list.next_page_number }}&q={{ query }}">nächste</a>
{% endif %}
</span>
</div>
{% endif %}
So now the url is build like search?q=Test for the first page. And for the other pages (which suprisingly works) is search?page=2&q=Test. Now im happy it works but i dont quite how just adding &q={{ query }} solved my problem. Is this some kind of universal RFC? I dont quite understand since i was just checking out how this side does it. implemented it and works?
A query string [wiki] is the part after the question mark (?) of a URL. It is a string that consists out of a sequence of key-value pairs separated by an ampersand (&). The key and the value are separated by the equals sign (=). Both the key and the value are percent-encoded [wiki]. So as you found out:
page=2&q=Test
is a querystring that contains two key-value pairs: page maps to 2 and q to test.
The code is however not entirely "safe". If the query itself contains an ampersand &, etc. then this can result in an incorrect query. You should make use of the |urlencode template filter [Django-doc] to percentage encode the value:
<a class="ml-3" href="?page={{ post_list.next_page_number }}&q={{ query|urlencode }}">nächste</a>

How can I limit and filter a collection by key value in a jekyll theme?

I'm trying to output one item from a collection where sticky: "true".
I've tried Getting a specific item from a collection in Jekyll
From collection _defense/dui.html
---
layout: right-sidebar
title: "Drug Offense"
crumbtitle: "drug-related crime"
permalink: "/practice-law/criminal-law/drug-offense.html"
sticky: true
twitter: "Working on this"
facebook: "Civil Litigation can cost you hundreds if not thousands of dollars if you are not adequately protecting your rights. Civil Litigation can be anything from traffic tickets, hurricane insurance claims to medical malpractice. Call or write for a free, no obligation review of your situation."
web: "An arrest for a drug offense can have significant consequences. Drug offense charges can range from possession to trafficking and can carry significant penalties, including minimum mandatory prison sentences.
The Prosecutor’s process of deciding what if any criminal charges to file can be a very critical stage in the case. This process happens very early in a case and as your lawyers, we can impact this decision.
**DO NOT** make a statement to law enforcement without consulting us first. Call Cagan & Cagan today for a free consultation."
In collection page _practices/criminal-defense.html
<!-- One -->
{% assign defenses = site.defense | where:"true", "page.sticky" | limit:1 %}
{% assign defense = defense[0] %}
{{ defense | inspect }}
<section class="wrapper style4 container">
<div class="row gtr-150">
<div class="col-8 col-12-narrower">
<!-- Content -->
<div class="content">
<section>
<header>
<a href="#" class="image featured">
<h2>{{ defense.title }}</h2>
</a>
</header>
<p>{{ defense.web | markdownify }}</p>
</section>
</div>
</div>
I get nil value right now. I want the first item with sticky true and end there.
where filter syntax is :
{% assign res = array | where: "key", "expected value" %}
but, you've reversed arguments order :
{% assign res = hash | where: "expected value", "key" %}
see jekyll documentation here
So, you can replace
{% assign defenses = site.defense | where:"true", "page.sticky" | limit:1 %}
{% assign defense = defense[0] %}
by
{% assign defense = site.defense | where: "sticky", "true" | first %}
notes :
first takes the first element of you array, replacing limit:1 > array[0].
limit is a for loop control structure parameter. You cannot use it in an assign tag.
Edit : if you want to get some items from posts, pages or collection, depending on a front matter variable, you can do :
{% assign items = site.anycollection | where: "any_key", "string_value" %}
You can then print anything from this resulting array, using a for loop and, eventually limit and offset parameters.
{% for item in items limit:2 %}
and so on ....

jinja2 get whole context in single object within template

For example, I have template index.html and custom_jinja2_filter
<h1> My name is {{ name }} </h1>
<h2> I'm {{ year }} years old </h2>
<p> I'd like to pass template context to custom
filter like single object. Is it possible?
{{ ??? | custom_jinja2_filter }}
</p>
def custom_jinja2_filter(context):
name = context['name']
year = context['year']
You can pass the current context to a function marked as callable with #contextfunction:
from jinja2 import contextfunction
#contextfunction
def custom_jinja2_filter(context):
name = context.name
year = context.year
return '(c) {} {}'.format(year, name)

Django: combine items from two queries (two different Models) together

I'm building an event platform with Django with some events public and some invite-only. Here's how it basically works:
Every event is based on the Event model.
Private events are only viewable by invited guests: this is achieved
through an Invite model referencing both the event and the guest.
Users can inform whether they will be attending an event or not
through an Answer model that stores the associated event, user
and the answer.
The Answer and Invite models are totally independent from each other, that way I am able to use the Answer model for both public and private events.
What I'm trying to achieve:
For each event where I'm invited, display the invitation (event.creator invited you to event.name) and, if it exists, my associated answer, else display a form to answer.
So I think what I'm trying to do is getting all events where I'm invited (I have no problem with that) and joining that to my answer (where user=me). The problem is that the answer might not even exist yet --if I haven't answered.
Ideally, I would have all of that in one single query so that I could do something like this in the templates: invited_event.answer and display the answer if it exists.
EDIT:
So I think what I need ultimately is to mix two queries: one that gets all the events where I'm invited (1) and an other that grabs all answers for those events (2).
(1)
latest_events_invited = Event.objects.filter(invite__user_invited=request.user)
(2)
answers_events_invited = Answer.objects.filter(user_answering=request.user, event__in=latest_events_invited)
Something like: for each event in latest_events_invited, append corresponding answer from answers_events_invited.
Any ideas?
Code:
My template (index.html):
<h3>Invites</h3>
{% if latest_invites_list %}
<ul>
{% for event in latest_events_invited %}
<li>
{{ event.creator }} invited you to {{ event }}<br/ >
<!--IDEALLY:-->
{% if event.answer %}
You answered: {{ answer.answer }}
{% else %}
<form action="{% url 'events:answer_event' invite.event.id %}" method="post">
{% csrf_token %}
Answer:
<select name="answer">
<option value="1" >Attending</option>
<option value="2" >Not attending</option>
</select>
<input type="submit" name="submit" value="Answer">
</form>
{% endif %}
</li>
</ul>
{% else %}
<p>No invites.</p>
{% endif %}
The view (views.py)
def index(request):
if request.user.is_authenticated():
latest_events_invited = Event.objects.filter(invite__user_invited=request.user)
latest_answers_list = Answer.objects.filter(user_answering=request.user, event__in=latest_events_invited)
#do something with those to get: "latest_events_invited_with_answers"
context = {'latest_events_invited':latest_events_invited, 'latest_answers_list':latest_answers_list}
else:
[...]
return render(request, 'events/index.html', context)
And the models.
Event
class Event(models.Model):
PRIVACY_CHOICES = (
(0, 'Public'),
(1, 'Invite only'),
)
name = models.CharField(max_length=200)
[...]
privacy = models.PositiveSmallIntegerField(max_length=1, choices=PRIVACY_CHOICES, default=0)
invited = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Invite', related_name='events_invited', blank=True)
answers = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Answer', related_name='events_answered', blank=True)
Invite
class Invite(models.Model):
user_invited = models.ForeignKey(settings.AUTH_USER_MODEL)
event = models.ForeignKey(Event)
created = models.DateTimeField(default=timezone.now, editable=False)
class Meta:
unique_together = (("user_invited", "event"),)
Answer
class Answer(models.Model):
ANSWER_CHOICES = (
(1, 'Attending'),
(2, 'Not attending'),
)
user_answering = models.ForeignKey(settings.AUTH_USER_MODEL)
event = models.ForeignKey(Event)
answer = models.PositiveSmallIntegerField(max_length=1, choices=ANSWER_CHOICES)
class Meta:
unique_together = (("user_answering", "event"),)
Hopefully someone here can help me out.
Thanks.
One option is to automatically add an Answer whenever someone is invited, with a default of No. Use the post_save signal of Invite or overwrite the save method so you don't have to create an associated Answer each time.
Another option, probably better, is to do the logic in the view rather than in the template. Check if an answer exists; if it does, pass a list of answers to the template; if not, pass an empty list.
EDIT: Or, better yet:
In the view:
try:
user_answer = Answer.objects.filter(user_answering = request.user).filter(event=event)
except ObjectDoesNotExist:
answer = None
EDIT2: Or how about:
Answer.objects.filter(user_answering = request.user).filter(event__in=Event.objects.filter(user__in = event.invited))
Ok, so I figured it out. I don't know if this is the optimal solution but it works for now.
In the end, it is only 2 queries, which I combine with for loops.
Here's the code: (views.py)
def index(request):
if request.user.is_authenticated():
latest_events_invited = Event.objects.filter(invite__user_invited=request.user)
latest_answers_for_events = Answer.objects.filter(user_answering=request.user, event__in=latest_events_invited)
for event in latest_events_invited:
for answer in latest_answers_for_events:
if answer.event.id == event.id:
event.answer = answer.answer
else:
event.answer = ''
context = {'latest_events_invited': latest_events_invited,}
return render(request, 'events/index.html', context)
I can then access the answer directly in the template (index.html) like so:
{{ event.answer }}