I am new to Django, and I am reading one app on github:
https://github.com/rogargon/myrecommendations/blob/web2-html/myrestaurants/urls.py#L18
There is one urlpattern like
url(r'^restaurants/(?P<pk>\d+)/$',
RestaurantDetail.as_view(),
name='restaurant_detail')
It revoke RestaurantDetail view, here: https://github.com/rogargon/myrecommendations/blob/master/myrestaurants/views.py#L36
class RestaurantDetail(DetailView):
model = Restaurant
template_name = 'myrestaurants/restaurant_detail.html'
def get_context_data(self, **kwargs):
context = super(RestaurantDetail, self).get_context_data(**kwargs)
context['RATING_CHOICES'] = RestaurantReview.RATING_CHOICES
return context
Here I know pk is set to one number indicating the id of restaurant, but in the html model, https://github.com/rogargon/myrecommendations/blob/master/myrestaurants/templates/myrestaurants/restaurant_detail.html, I didn't see any where using pk, but the page shows only the one restaurant. Could you how does pk work in this process? How the template know which restaurant I want to show? And why there is no return in this view?
{% extends "myrestaurants/base.html" %}
{% block title %}MyRestaurants - {{ restaurant.name }}{% endblock %}
{% block content %}
<span vocab="http://schema.org/" typeof="Restaurant">
<h1>
<span property="name">{{ restaurant.name }}</span>
{% if user == restaurant.user %}
(edit)
{% endif %}
</h1>
<h2>Address:</h2>
<p>
{{ restaurant.street }}, {{ restaurant.number }} <br/>
{{ restaurant.zipcode }} {{ restaurant.city }} <br/>
{{ restaurant.stateOrProvince }} ({{ restaurant.country }})
</p>
<h2>
Dishes
{% if user.is_authenticated %}
(add)
{% endif %}
</h2>
<ul>
{% for dish in restaurant.dishes.all %}
<li><a href="{% url 'myrestaurants:dish_detail' restaurant.id dish.id %}">
{{ dish.name }}</a></li>
{% empty %}<li>Sorry, no dishes for this restaurant yet.</li>
{% endfor %}
</ul>
<h2>Reviews</h2>
{% if restaurant.restaurantreview_set.all|length > 0 %}
<span rel="aggregateRating">
<p typeof="AggregateRating">
Average rating <span property="ratingValue">{{ restaurant.averageRating|stringformat:".1f" }}</span>
{% with restaurant.restaurantreview_set.all|length as reviewCount %}
from <span property="reviewCount">{{ reviewCount }}</span> review{{ reviewCount|pluralize }}
{% endwith %}
</p>
</span>
<ul rel="review">
{% for review in restaurant.restaurantreview_set.all %}
<li typeof="Review">
<p rel="reviewRating" typeof="Rating">
<span property="worstRating" content="{{ RATING_CHOICES.0.0 }}"></span>
<span property="ratingValue">{{ review.rating }}</span> star{{ review.rating|pluralize }}
{% with RATING_CHOICES|last as best %}
<span property="bestRating" content="{{ best.0 }}"></span>
{% endwith %}
</p>
<p property="description">{% if review.comment %}{{ review.comment }}{% endif %}</p>
<p>Created by <span property="author">{{ review.user }}</span> on
<span property="datePublished" content="{{ review.date|date:'Y-m-d' }}">{{ review.date }}</span></p>
</li>
{% endfor %}
</ul>
{% endif %}
</span>
<h3>Add Review</h3>
<form action="{% url 'myrestaurants:review_create' restaurant.id %}" method="post">
{% csrf_token %}
Message: <textarea name="comment" id="comment" rows="4"></textarea>
<p>Rating:</p>
<p>{% for rate in RATING_CHOICES %}
<input type="radio" name="rating" id="rating{{ forloop.counter }}" value="{{ rate.0 }}" />
<label for="choice{{ forloop.counter }}">{{ rate.1 }} star{{ rate.0|pluralize }}</label>
<br/>{% endfor %}
</p>
<input type="submit" value="Review" />
</form>
{% endblock %}
{% block footer %}
Created by {{ restaurant.user }} on {{ restaurant.date }}
{% endblock %}
The behavior you're asking about ("How the template know which restaurant I want to show") is view behavior, not template behavior.
The template renderer gets passed a restaurant value by the view. The view has no (explicit) return because it's a class-based view - a subclass of DetailView, in particular. DetailView itself inherits standard methods that this view does not override to accept a PK parameter and load a particular instance.
You can read the source code for DetailView to get more of a sense of what's going on, if you like, eg https://github.com/django/django/blob/master/django/views/generic/detail.py - but class-based views are a bit of an advanced topic, and not every project will even use them. I'd focus on other areas first.
Related
I have an inlineformset_factory: PollFormset = inlineformset_factory(Post, Poll, form=PollForm, max_num=10, extra=1, can_delete=False) and I want below the form the option to add and create another Poll like in the django admin panel,
template:
{% for form in poll_form %}
{{ form.non_field_errors }}
<div class="container" id="poll_form">
<div class="row" name="service_form">
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form %}
<div class="col-sm">
{{ field.errors }}
{{ field|as_crispy_field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
I also tried {{poll_form.management_form}} but it doesnt work.
{% extends 'halls/base.html' %}
{% block content %}
<div class="container">
<h2>Add Video to {{hall.title}}</h2>
<form method="post">
{% csrf_token %}
{% load widget_tweaks %}
{% for field in form %}
<div class="form-group {% if field.errors %} alert alert-danger {% endif %}">
{{ field.errors }}
{{ field.label_tag }}
{% render_field field class='form-control' %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary"> Add</button>
</form>
<br>
<h2>OR</h2>
<form>
{% for field in search_form %}
<div class="form-group ">
{{ field.errors }}
{{ field.label_tag }}
{% render_field field class='form-control' %}
</div>
{% endfor %}
</form>
<div id="search_results"></div>
<script type="text/javascript">
var delayTimer;
$("#id_search_term").keyup(function (){
clearTimeout(delayTimer);
$('#search_results').text('Loadingggg');
});
</script>
</div>
{% endblock %}
this is the HTML code. SO basically the whole idea here is when I inspect the search_form, it should give the id of the input box as "id_search_term". But when I inspect the box, it gives as id="id_search".
I am not able to find a way to reference the input box with the ID in the script tag
I am trying to use a pagination to paginate one of my pages. I have tried a few different methods I have found online but for some reason, when I use paginate_by = 3, it doesn't actually paginate anything, yet still shows the html for the pagination at the bottom of the page.
View:
class SearchListView(ListView):
model = Post
template_name = "public/search.html"
paginate_by = 3
HTML:
{% extends 'public/base.html' %}
{% load staticfiles %}
{% block head %}
<link rel="stylesheet" type="text/css" href="{% static "public/css/search.css" %}" />
{% endblock %}
{% block content%}
<div class="search container-fluid">
<img src="/media/about-us.jpg" alt="">
<div class="search-title">
<h1 class="title">Search</h1>
</div>
<div class="search-main mb-5">
<form method='GET' action=''>
<input type="text" name='q' class="homebanner-search" placeholder="Enter your keywords" value='{{ request.get.q }}'>
</form>
</div>
</div>
<div class="container mt-5 mb-5">
<div class="detail-container">
{% for post in queryset %}
<a href="{% url 'post-detail' post.slug %}">
<div class="post-main">
<div class="post-image">
<img src="{{ post.image.url }}" class="card-img-top" alt="#">
<p class="post-category">{{ post.category }}</p>
</div>
<div class="post-body">
<div class="post-title">
<p class="post-title-p">Day in the life of {{ post.title }}</p>
</div>
<div class="post-text">
<p class="post-author-text text-muted">{{ post.sub_description|truncatewords:22 }}</p>
</div>
<div class="post-button">
<p>READ MORE ></p>
</div>
</div>
</div>
</a>
{% endfor %}
</div>
<div id="page_navigation" >
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}
So on the page, 3 items should be showing, and pagination should take me to the the next set of 3. The html is showing, and when I click the links it is taking me 2 page 2. The problem is the fact that 6 items are showing, and not 3, and when I go to page 2, there are the same 6 items there.
Unfortunately I couldn't reproduce your error exactly, but what did happen is that my objects wouldn't render when looping through queryset. So what I would recommend is try to loop through object_list instead:
{% for post in object_list %}
{{ post.name }}
{% endfor %}
Another thing you can do is add a context_object_name argument to your view:
class SearchListView(ListView):
model = Post
template_name = "public/search.html"
paginate_by = 3
context_object_name = 'posts'
and then loop through that:
{% for post in posts %}
{{ post.name }}
{% endfor %}
Also, I can't visualise what the search form is doing on this page since the ListView's model (Post) is the queryset, not whatever is searched for? So perhaps the link href is causing some trouble. Maybe try something like this instead:
<li>«</li>
Again, I could not reproduce the exact problem you are having, which is a shame because I feel like I have had the same issue before, so these are just suggestions pieced together from pagination that works for me and the code you posted. Hope it points you in the right direction.
I have the following dictionary defined in a python config file:
AUTHORS = {
u'MyName Here': {
u'blurb': """ blurb about author""",
u'friendly_name': "Friendly Name",
u'url': 'http://example.com'
}
}
I have the following Jinja2 template:
{% macro article_author(article) %}
{{ article.author }}
{{ AUTHORS }}
{% if article.author %}
<a itemprop="url" href="{{ AUTHORS[article.author]['url'] }}" rel="author"><span itemprop="name">{{ AUTHORS[article.author]['friendly_name'] }}</span></a> -
{{ AUTHORS[article.author]['blurb'] }}
{% endif %}
{% endmacro %}
And I call this via:
<div itemprop="author creator" itemscope itemtype="http://schema.org/Person">
{% from '_includes/article_author.html' import article_author with context %}
{{ article_author(article) }}
</div>
When I generate my Pelican template I get the following error:
CRITICAL: UndefinedError: dict object has no element <Author u'MyName Here'>
If I remove the {% if article.author %} block from my template, the page generates properly with the {{ AUTHORS }} variable displaying correctly. It clearly has a MyName Here key:
<div itemprop="author creator" itemscope itemtype="http://schema.org/Person">
MyName Here
{u'MyName Here': {u'url': u'http://example.com', u'friendly_name': u'Friendly Name', u'blurb': u' blurb about author'}}
</div>
How do I access the MyName Here element correctly in my template?
The article.author isn't just 'Your Name', it's an Author instance with various properties. In your case, you want:
{% if article.author %}
<a itemprop="url" href="{{ AUTHORS[article.author.name].url }}" rel="author">
<span itemprop="name">{{ AUTHORS[article.author.name].friendly_name }}</span>
</a> -
{{ AUTHORS[article.author.name].blurb }}
{% endif %}
or, to reduce some of the boilerplate, you can use:
{% if article.author %}
{% with author = AUTHORS[article.author.name] %}
<a itemprop="url" href="{{ author.url }}" rel="author">
<span itemprop="name">{{ author.friendly_name }}</span>
</a> -
{{ author.blurb }}
{% endwith %}
{% endif %}
as long as you have 'jinja2.ext.with_'in your JINJA_ENVIRONMENT's extensions list.
Note you can use dot.notation rather than index['notation'] in Jinja templates.
I haven't found similar questions on stackoverflow, I'd like to change the save button to submit or confirm on the edit form. I know this might not be easily changed. Thanks for any advise in advance.
After search in the code of flask-admin, I found the button is rendered with macro render_form, render_form_buttons, extra. The value of these buttons is hard code with {{ _gettext("blabla") }}.
As these buttons are not fields of data model, we can't use rendering rules to custom the value. I think there are two work arounds to get this done:
change the macro which render these buttons in the source of flask-admin(render_form_buttons, extra)
flask-admin use flask-babelex to do localization({{ _gettext("blabla") }}), you can 'translate' Save to submit or confirm with flask-babelex
UPDATE:
You can custom edit.html in your own template directory.
{% extends 'admin/model/edit.html' %}
{% from 'admin/lib.html' import extra with context %}
{% from 'admin/lib.html' import form_tag with context %}
{% from 'admin/lib.html' import render_form_fields with context %}
{% macro my_render_form_buttons(cancel_url, extra=None, is_modal=False) %}
<hr>
<div class="form-group">
<div class="col-md-offset-2 col-md-10 submit-row">
<input type="submit" class="btn btn-primary" value="{{ _gettext('Submit') }}" />
{% if extra %}
{{ extra }}
{% endif %}
{% if cancel_url %}
<a href="{{ cancel_url }}" class="btn btn-danger" role="button" {% if is_modal %}data-dismiss="modal"{% endif %}>{{ _gettext('Cancel') }}</a>
{% endif %}
</div>
</div>
{% endmacro %}
{% macro my_render_from(form, cancel_url, extra=None, form_opts=None, action=None, is_modal=False) -%}
{% call form_tag(action=action) %}
{{ render_form_fields(form, form_opts=form_opts) }}
{{ my_render_form_buttons(cancel_url, extra, is_modal) }}
{% endcall %}
{% endmacro %}
{% block edit_form %}
{{ my_render_form(form, return_url, extra(), form_opts) }}
{% endblock %}