Dynamic URL with variable django template - python

I'm trying to build a dynamic list of urls, based on a list of pages.
In my urls.py, the whole application is behind the namespace base:
urlpatterns = patterns('',
url(r'^(?P<pk>[\w]+)/title/$', TitleSection.as_view(), name='title'),
url(r'^(?P<pk>[\w]+)/amount/$', AmountSection.as_view(), name='amount'),
url(r'^(?P<pk>[\w]+)/description/$', DescriptionSection.as_view(), name='description'), )
And in my context data I have the following list:
sections: ['title', 'amount', 'description']
I am trying to build the urls for each of the element in the sections.
I tried the following:
{% for section in sections %}
..
{% endfor %}
But I got the following error:
Could not parse the remainder: '+section' from '"base:"+section'
Then I tried:
{{ section }}
Error:
Reverse for '{{section}}' with arguments '()' and keyword arguments '{u'pk': 77}' not found. 0 pattern(s) tried: []
Do you know how to do this?

You can use the add template filter:
{% url "base:"|add:section pk=project.id %}

for my case that also worked
{% for section in sections %}
..
{% endfor %}
where url pattern is
url=[
path('base/<pk>/',base,name='base'),
]

Each of my models has a list view, a create/update view, and a delete view. These will be used by different functions within the customer's organisation to maintain the data they are responsible for. Each list view has links to the relevant create, update, and delete views. I wanted to build a page with a list of links to the list view. Here's how I did it.
I created a function based view in views.py.
def index(request):
app = request.resolver_match.app_name
models = apps.get_app_config(app).get_models()
names = [model._meta.model.__name__ for model in models]
context = {
"names" : names,
}
return render(request, app + '/index.html', context)
I created a template app/templates/app/index.html
{% for name in names %}
<li>{{ name}}</li>
{% endfor %}

Related

TypeError: topics() missing 1 required positional argument: 'topic_id'

I'm following along a Django tutorial book to make a basic blogging application where users can write journal entries about whatever topic they choose. I've written the url, pattern, view, and template for my topic page but I keep getting this same error. but I think something is wrong with the url pattern.
urls.py
# Defines url patterns for learning_logs app
from django.conf.urls import url
from . import views
urlpatterns = [
# Home Page
url(r'^$', views.index, name='index'),
# Topic Main Page
url(r'^topics/$', views.topics, name='topics'),
# Detail page for a single topic
url(r"^topics/(?P<topic_id>\d+)/$", views.topics, name='topic'),
]
views.py
from django.shortcuts import render
from .models import Topic
# Create your views here.
def index(request):
# This is the home page for our learning_logs app
return render(request, 'learning_logs/index.html')
# This is the view for 'topics' page 9.5.20
def topics(request, topic_id):
'''show a single topic and its entries '''
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topics.html', context)
And the error:
TypeError: topics() missing 1 required positional argument: 'topic_id'
Any advice? Thank you.
EDIT**
What I'm trying to do is to have a page that lists all of the topics in general. When the link for one topic in particular is clicked, it will lead to the page for that particular topic.
Here's the code for the topics.html (all topics displayed) and topic.html (when one particular topic is chosen)..
topics.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>
{{ topic }}
</li>
{% empty %}
<li>No topics have been added yet</li>
{% endfor %}
</ul>
{% endblock content %}
and topic.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topic: {{ topic }}</p>
<p>Entries:</p>
<ul>
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, YH:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>No topics have been added yet</li>
{% endfor %}
</ul>
{% endblock content %}
The error clearly specifies that the function is not getting "topic_id", so there can be two cases
First, if you are using a template to redirect to that function(or view) you are not providing id, in that case, I might want to have a look at your template
Second, it's because of complex URL patterns so switch to something more simple likepath('topics/<int:topic_id>/', views.topics, name='topics'),
Lastly do not follow tutorial word to word because of things changes with update.
Your view is expecting a parameter passed to it ...
def topics(request, topic_id):
But your url does not have one:
url(r'^topics/$', views.topics, name='topics'),
You need to change the url to be something like:
path('topics/<int:topic_id>/', views.topics, name='topics'),
This means you would access that specific record at the url:
yourdomain.com/topics/1/
The 1 would be passed to the view, and used in topic = Topic.objects.get(id=topic_id)
The fact that you also have the url:
url(r'^topics/$', views.topics, name='topics'),
confuses matters - what is this view going to show with no topic set? From your code I suspect you want a list view here, which should ideally link to a different view, and maybe be named topics-list for clarity and ease of access in reverse lookups later. Your topics view specifically states in the docstring it is for viewing a single record, so don'ttry and use it for multiple ones as well. Much simpler to create a distinct view for that.
Edit: I see you've updated your path in urls.py in the question now. That should work for the single access, but note that your link topics/ is still directing to the same view - i.e. it's looking for a single record, but you're not telling it which.

In Django, can a url tag have an arbitrary number of arguments?

BACKGROUND: I'm learning Django by working on a simple to-do-list app using Django 1.11. The app has "groups" and "items", where items are the individual to-do items and every item belongs to exactly one group. The URLs may end up working something like this:
# snippet from urls.py
url(r'^groups$', views.all_groups, name="all_groups"),
url(r'^groups/(\d+)/$', views.view_group, name="view_group"),
url(r'^items$', views.all_items, name="all_items"),
url(r'^items/(\d+)/$', views.view_item, name="view_item"),
Each of the above pages would display a one or two column table (e.g. all items, or all items in a specific group, etc.), where each table element would be a link to a page to show either a specific group or a specific item.
I have a separate view for each of the above URLs, but I was able to have a single HTML template to render each table successfully. Depending on the URL, there are either 0 or 1 arguments to the URL tag: {% url 'my_url' %} or {% url 'my_url' arg1 %}
Here is a snippet of the HTML template that can render the table with an arbitrary number of rows and columns, but at most two arguments for the url tag:
# lists.html - works for several different views
# every table entry is a dictionary with an 'page_ref' key and a 'display_text' key
<table>
{% for row in url_table %}
<tr>
{% for col in row %}
{% if col.arg2 %}
<td>{{col.display_text}}</td>
{% elif col.arg1 %}
<td>{{col.display_text}}</td>
{% else %}
<td>{{col.display_text}}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
MY QUESTIONS:
1) My code in the HTML template to handle {% url 'my_url' %} vs. {% url 'my_url' arg1 %} vs. {% url 'my_url' arg1 arg2 %} works but it's ugly and limited to at most 2 levels deep on the URL. That is probably fine for what I want, but I don't like the code. Is there a cleaner way to handle an arbitrary number of arguments? When I tried simply not passing arg1/arg2 if not needed, I got an exception about not being able to reverse '' (or some similar error message).
2) In general, is it a bad idea to make a generic template like this? I.e., it is a better practice to have one specific HTML template per view, without making it more general? My guess is no, but I figured that I'd ask.
I can provide the view code as well, but I don't think that it is needed for my question.
Thanks
As you have different views, but you are using same template
So what you can do is remove conditions from template and
send same context variable name from each view containing different absolute url with respect to your view.
# view1 no arguments
class AllGroupView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('all_groups')
return context
#view2 one argument
class GroupView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('view_group', args=[arg1])
return context
...
#view3 two argument
class ItemView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('view_item', args=[arg1, arg2])
return context
In html just use this variable
# use my_url absolute: http://localhost:8000/...
<td>{{display_text}}</td>

Django: Listing documents from a local folder

I've been trying to find a way to display my documents from a local folder on to a web page. I was wondering about this in two ways: one was to use django's ListView, but I am not using models in this case, so I am unsure if it would work. The other way I'm going with this is by through this list method that I've made, but I am having trouble getting the proper contents (title, date) on to the webpage. They show up in lists that I created, but wont translate to the webpage. Its just a blank page. Here's my code:
views.py
import os, string, markdown, datetime
from P1config.settings import STATICBLOG_COMPILE_DIRECTORY,STATICBLOG_POST_DIRECTORY,STATICBLOG_STORAGE
def doclist(request):
mdown = markdown.Markdown(extensions = ['meta','extra', 'codehilite', PyEmbedMarkdown()])
posts = []
for item in os.listdir(STATICBLOG_POST_DIRECTORY):
if item.endswith('.md'):
continue
try:
with open(os.path.join(STATICBLOG_POST_DIRECTORY, item)) as fhandle:
content = fhandle.read() # (opening and reading the ENTIRE '.md' document)
mdown.convert(content) # (converting file from '.md' to ".html")
post = { 'file_name' : item }
if 'title' in mdown.Meta and len(mdown.Meta['title'][0]) > 0:
post['title'] = mdown.Meta['title'][0]
else:
post['title'] = string.capwords(item.replace('-', ' '))
if 'date' in mdown.Meta:
post['date'] = mdown.Meta['date'][0]
post['date']= datetime.datetime.strptime(post['date'], "%Y-%m-%d")
posts.append(post)
except:
pass
from operator import itemgetter
posts = sorted(posts, key=itemgetter('date'))
posts.reverse()
return render(
request,
'list.html',
{'post' : posts}
)
list.html
{% extends 'base.html' %}
{% block content %}
{% if post %}
{% for i in post %}
<h2>{{post.title}}</h2>
<p class="meta">{{post.date}}</p>
{% endfor %}
{% endif %}
{% endblock %}
and my urls.py:
from django.conf.urls import include, url, patterns
urlpatterns = patterns('blog_static.views',
(r'^postlist/', 'list'),
)
I have two questions:
Can you figure out where I am going wrong in this code?
Are there any alternative ways that I may go about doing this? This may be an inefficient way of listing documents from a local folder, so I am open to other options as well.
Any sort of help would be appreciated. Thanks!
It sounds like you are already familiar with, and could execute this using ListView. You can use ListView without a model - as referenced in various parts of the documentation ("is not necessarily a queryset"):
https://docs.djangoproject.com/en/1.8/ref/class-based-views/mixins-multiple-object/#django.views.generic.list.MultipleObjectMixin.get_queryset
Get the list of items for this view. This must be an iterable and may be a queryset (in which queryset-specific behavior will be enabled).
Therefore you should be able to do the following:
class MyListView(generic.ListView):
template_name = 'foobar.html'
def get_queryset(self):
return [1, 2, 3]
What's wrong with your example... it's the fact you're referencing post in your inner for loop as opposed to the i that you defined as the actual post.
It's confusing because you rename the python posts variable to post in the template context, then iterate over it as i.
posts in your template context is just a list, and has no attributes, keys, etc., named post.title.
post is array of dict objects. So
{% extends 'base.html' %}
{% block content %}
{% if post %}
{% for i in post %}
<h2>{{i.title}}</h2>
<p class="meta">{{i.date}}</p>
{% endfor %}
{% endif %}
{% endblock %}

How can I create a sidebar in all my Django templates using CBV?

My problem is that I need dynamic tags list on all my pages and all posts (one post) content on that page too. How can I include tag sidebar on all the pages using Class Based Views? Thanks.
EDIT:
the tags list must be sorted by the frequency of using.
My code:
class AllTagCloudView(ListView):
model = Tag
template_name = 'tag_cloud.html'
context_object_name = 'tag_cloud'
def get_queryset(self):
qs = Tag.objects.values("name", "slug").annotate(Count("post")).order_by('-post__count')
return qs
I've tried to use
#register.inclusion_tag('tag_cloud.html', takes_context=True)
def sidebar_sorted_tags(context):
but I don't understand how to do it to work.
Also I have tried to use {% include 'tag_cloud.html' %}:
<div>
<p>Tags</p>
{% for tag in tag_cloud %}
<ul>
<li>{{ tag.name }}</li>
</ul>
{% empty %}
There is no tags yet
{% endfor %}
</div>
I think this is something stupid or I do something wrong.
This task is not related to class based views. You need to use custom inclusion template tag.
#register.inclusion_tag('tag_cloud.html')
def sidebar_sorted_tags():
return {'tag_cloud': Tag.objects.values("name", "slug")
.annotate(Count("post")).order_by('-post__count')}
And now in your base.html template write:
{% load my_tags %}
{% sidebar_sorted_tags %}
You can use context processors and global base template that all of your other templates will extend. Also you can use simple include in your template tag instead of global base template.

Django: Named URLs / Same Template, Different Named URL

I have a webapp that lists all of my artists, albums and songs when the appropriate link is clicked. I make extensive use of generic views (object_list/detail) and named urls but I am coming across an annoyance. I have three templates that pretty much output the exact same html that look just like this:
{% extends "base.html" %}
{% block content %}
<div id="content">
<ul id="starts-with">
{% for starts_with in starts_with_list %}
<li>{{ starts_with|upper }}</li>
{% endfor %}
</ul>
<ul>
{% for song in songs_list %}
<li>{{ song.title }}</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
My artist and album template look pretty much the same and I'd like to combine the three template's into one. The fact that my variables start with song can easily be changed to the default obj. It's my <ul id="starts-with"> named url I don't know how to correct. Obviously I want it to link to a specific album/artist/song using the named urls in my urls.py but I don't know how to make it context aware. Any suggestions?
urls.py:
urlpatterns = patterns('tlkmusic.apps.tlkmusic_base.views',
# (r'^$', index),
url(r'^artists/$', artist_list, name='artist_list'),
url(r'^artists/(?P<starts_with>\w)/$', artist_list, name='artist_list_x'),
url(r'^artist/(?P<artist_id>\d+)/$', artist_detail, name='artist_detail'),
url(r'^albums/$', album_list, name='album_list'),
url(r'^albums/(?P<starts_with>\w)/$', album_list, name='album_list_x'),
url(r'^album/(?P<album_id>\w)/$', album_detail, name='album_detail'),
url(r'^songs/$', song_list, name='song_list'),
url(r'^songs/(?P<starts_with>\w)/$', song_list, name='song_list_x'),
url(r'^song/(?P<song_id>\w)/$', song_detail, name='song_detail'),
)
You could define url patterns for a generic object_type instead of individually for artists, albums and songs:
urlpatterns = patterns('tlkmusic.apps.tlkmusic_base.views',
# (r'^$', index),
url(r'^(?P<object_type>\w+)/$', music_object_list, name='music_object_list'),
url(r'^(?P<object_type>\w+)/(?P<starts_with>\w)/$', music_object_list, name='music_object_list_x'),
url(r'^(?P<object_type>\w+)/(?P<object_id>\d+)/$', music_object_detail, name='music_object_detail'),
)
Then in your template, your url tag becomes
{% url music_object_list_x object_type starts_with %} *
You may find you only need one view, music_object_list. If you find you need different functions for each object type, then call the individual functions in music_object_list.
def music_object_list(request, object_type, starts_with=None):
if object_type == 'artists':
return artist_list(request, starts_with=starts_with)
elif object_type == 'albums':
return album_list(request, starts_with=starts_with)
...
If you are using django.views.generic.list_detail.object_list, then remember to add object_type to the extra_context dictionary. This will add object_type to the template context, allowing the url tag to work.
extra_context = {'object_type': 'songs', ...}
* This is the new url tag syntax for Django 1.2. For older versions you would use a comma.
{% url music_object_list_x object_type,starts_with %}
See the docs (Current, 1.1) for more information

Categories

Resources