So I want to add a extra link in the Wagtail admin. I am following the docs (Register Admin Menu Item Hook) and using this hook to contruct a new menu item.
They use this hook for adding menu items:
#hooks.register('register_admin_menu_item')
def register_edit_menu_item():
return MenuItem('Edit profile', 'edit_link', classnames='icon icon-folder-inverse', order=10000)
This should become a link which starts editing a page that is owned by the current logged in user. In a template I use this code to create a direct link to edit the profile of a user:
{% with request.user.owned_pages.all as pages %}
{% if pages.exists %}
{% for p in pages %}
{% if p.get_parent.id == 17 %}
<a class="list-group-item" href="/dashboard/pages/{{ p.id }}/edit/"><i class="fe-icon-edit mr-1 text-muted"></i>Edit profile</a>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
This is working just fine, it links directly to the page that it is supposed to link to. However how do I implement a condition like this where I generate the menu link based on the current logged in user?
I would think something like:
if request.user.owned_pages.exists():
for p in request.user.owned_pages.all():
if p.get_parent.id == 17:
edit_link = "/dashboard/pages/" + p.id + "/edit/"
But how can I implement something like this into the Wagtail menu hook?
The default implementation of MenuItem assumes that the URL will be constant over all page requests, meaning that we can pass that fixed URL to the MenuItem constructor inside the register_admin_menu_item hook (that runs on server startup).
In your case, this isn't true, so you'll need to define a custom subclass of MenuItem. If you look at the code for MenuItem, you'll see that it implements a get_context(self, request) method to gather all the template variables it needs to render the menu item, including url. You can override this method to set a dynamic URL in place of the fixed one:
class EditProfileMenuItem(MenuItem):
def get_context(self, request):
context = super().get_context(request)
edit_link = None
if request.user.owned_pages.exists():
for p in request.user.owned_pages.all():
if p.get_parent().id == 17:
edit_link = "/dashboard/pages/" + p.id + "/edit/"
if edit_link:
context['url'] = edit_link
return context
You can then use this subclass in the register_admin_menu_item hook, in place of MenuItem:
#hooks.register('register_admin_menu_item')
def register_edit_menu_item():
return EditProfileMenuItem('Edit profile', 'edit_link', classnames='icon icon-folder-inverse', order=10000)
(Depending on your requirements you may want to override the is_shown method too, so that the menu item is hidden from those users who don't have a profile.)
Related
I am trying to make my navbar element active if the user is on the current url and any url's leading from it.
For example, I want the navbar to be active on:
http://example.com/products
AND
http://example.com/products/discounted-items
I was using this:
{% if url_name == 'products' %}active{% endif %}
and was very happy till I realised that once I progress from the 'products' page to 'products/discounted-items' it would cease being active.
Is there a clean way to do this in django?
Thank you very much.
In your case you could simply do the following:
{% if 'products' in url_name %}active{% endif %}
Be aware that this also causes /productsfoobar to be active.
To prevent that you could use products/ instead of checking it without the trailing-slash.
In case (as i seen the page) if u are using Bootstrap u can add "active" in the class field in nav
I have a simple follow/following setup running.
When a user (request.user) see's an object she likes, she can click the follow button and follow that user.
When she returns I want that button on the object to now not be enabled cause she is already following that user.
What is happening is that in the background, the follower/followee record is being made. The object in question has the id of the followee. I just can't figure out how to add that representation to the object_list.
In REST I would add a field to the serializer and that would take care of it. I could then evaluate the truthiness of the new field.
Any ideas on how to accomplish this?
You should do it as a separate query and make the test in the template.
view:
def objects_list(request):
...
return render(request, "the_template_path.html", {
'objects': ObjectName.objects.all()[0:100],
'followed_object_ids': ObjectName.objects.filter(follower=request.user).values_list('id', flat=True)
})
template:
{% for object in objects %}
{% if object.id in followed_object_ids %}
...
{% else %}
...
{% endif %}
{% endfor %}
That will get you started. Obviously you don't want to use generic names like object.
It would then probably be better to move the followed_object_ids query in a middleware so you don't always do it (really useful if you have featured objects widgets everywhere for instance).
I need to show different button in my template based on user's log in. The scenario is my template show follow button:
Check whether the user logged in or not-
if user logged in-
check which channel he follwed:
if he followed any channel
beside that channel name show "followed" button
else
show "follow" button with the path follow_save
elif user not logged in:
show follow button with the path follow_save
I am stuck how to do that? Is this a task for views or for template? how to do that? Any help from you experts will save me..Also I have wanted to take the user_id from session.
Here is my views.py
e= EventArchiveGallery.objects.all()
user = request.session['user_id']
if user:
j = EventArchiveGallery()
joining_list = j.joiningList(user)
return render_to_response('gallery/allevents.html',
{
'joining_list':joining_list,
},
context_instance=RequestContext(request)
)
This is my templates:
{% for event in live %}
<p>Event Title:{{event.event_title}}<p/>
<p>Channel Name:{{event.channel_id.channel_title}}</p>
<p>Event Description{{event.event_description}}</p>
{% if event in joining_list %}
<p>followed</p>
{%else%}
<p>follow</p> #I have wanted to show follow_save function call from this button,when user clicked
{endif%}
{% endfor %}
You are asking 2 questions better if you can split in 2 questions, anyway ...
show different button in my template based on user's log in
Better way would be identify what to show in view and pass context parameter to template. Depending upon this variable template will render different HTML.
example template : Assumes view passes followed_channel parameter.
{% if user.is_authenticated %}
{%if followed_channel %}
{# render html you want #}
{%else%}
{# render another way #}
{%endif%}
{%endif%}
Also I have wanted to take the user_id from session
Before you get from session, you have to store it. So you can update your view as
e= EventArchiveGallery.objects.all()
user = request.session.get('user_id')
if not user:
request.session['user_id'] = request.user.id
if user:
j = EventArchiveGallery()
joining_list = j.joiningList(user)
return render_to_response('gallery/allevents.html',
{
'joining_list':joining_list,
},
context_instance=RequestContext(request)
)
I'm implementing a custom permissions application in my Django project, and I'm lost as to how to implement a custom template tag that checks a logged in user's permissions for a specific object instance and shows a piece of HTML based on the outcome of the check.
What I have now is (pseudocode):
{% check_permission request.user "can_edit" on article %}
<form>...</form>
{% endcheck %}
('check_permission' is my custom template tag).
The templatetag takes in the user, the permission and the object instance and returns the enclosed HTML (the form). This currently works fine.
What I would like to do however, is something like:
{% if check_permission request.user "can_edit" on article %}
<form>...</form>
{% else %}
{{ article }}
{% endif %}
I've read about the assignment tag, but my fear is that I would pollute the context variable space with this (meaning I might overwrite previous permission context variables). In other words, as the context variables are being defined on different levels (the view, middleware in my case, and now this assignment template tag), I'm worried about maintainability.
You can use template filters inside if statements. So you could rewrite your tag as a filter:
{% if request.user|check_can_edit:article %}
Note that it's tricky to pass multiple arguments of different types to a filter, so you'll probably want to use one filter per permission, above I've used check_can_edit.
You can definitely do that if you're willing to write some more lines of python code to improve your template readability! :)
You need to parse the tag content yourself, even the parameters it takes and then resolve them, if you want to use variables on them.
The tag implemented below can be used like this:
{% load mytag %}
{% mytag True %}Hi{% else %}Hey{% endmytag %} Bro
Or with a variable:
{% mytag myobject.myflag %}Hi{% else %}Hey{% endmytag %} Bro
So, here's the way I did it:
from django.template import Library, Node, TemplateSyntaxError
register = Library()
#register.tag
def mytag(parser, token):
# Separating the tag name from the "test" parameter.
try:
tag, test = token.contents.split()
except (ValueError, TypeError):
raise TemplateSyntaxError(
"'%s' tag takes two parameters" % tag)
default_states = ['mytag', 'else']
end_tag = 'endmytag'
# Place to store the states and their values
states = {}
# Let's iterate over our context and find our tokens
while token.contents != end_tag:
current = token.contents
states[current.split()[0]] = parser.parse(default_states + [end_tag])
token = parser.next_token()
test_var = parser.compile_filter(test)
return MyNode(states, test_var)
class MyNode(Node):
def __init__(self, states, test_var):
self.states = states
self.test_var = test_var
def render(self, context):
# Resolving variables passed by the user
test_var = self.test_name.resolve(context, True)
# Rendering the right state. You can add a function call, use a
# library or whatever here to decide if the value is true or false.
is_true = bool(test_var)
return self.states[is_true and 'myvar' or 'else'].render(context)
And that's it. HTH.
In Django 2 the assignment tag was replaced by simple_tag() but you could store the custom tag result as a template variable:
# I'm assuming that check_permission receives user and article,
# checks if the user can edit the article and return True or False
{% check_permission user article as permission_cleared %}
{% if permission_cleared %}
<form>...</form>
{% else %}
{{ article }}
{% endif %}
Check the current doc about custom template tags: https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/#simple-tags
inside my_tags.py
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def make_my_variable_true(context):
context['my_variable'] = True
return '' # without this you'll get a "None" in your html
inside my_template.html
{% load my_tags %}
{% make_my_variable_true %}
{% if my_variable %}foo{% endif %}
In this case best solution is to use custom filter. If you don't want write long code for custom tag. Also if you don't want to copy/paste others code.
Here is an example
Inside templatetag
register = template.Library()
def exam_available(user, skill):
skill = get_object_or_404(Skill, id=skill)
return skill.exam_available(user)
register.filter('exam_available', exam_available)
Inside template
{{ request.user|exam:skill.id }}
or
{% if request.user|exam:skill.id %}
Since one of the main common of it is to use request.user or any specific object(id) inside model's custom method, so filtering that individual object or user is the easiest way to make it done. :)
I'm sure I've seen this question on Stack Overflow before, but I couldn't find it by my life, so here goes nothing.
I have a normal Django menu which uses the {% url %} tag and static names for the menu items. Now I want to have a different style for the menu item which has been selected. But the menu is being rendered in the base template, so how do I figure out which menu item it is?
You could surely do this with some ugly template code, but a better more globally known way is to use a CSS selector. This lets CSS do all of the work automatically for you.
Here's how it works:
You simply put an id in your body depending on which page you are on.
Then in css you do something like this:
#section-aboutme #nav-aboutme,
#section-contact #nav-contact
/* ... put one of these per body/menu item ... */
{
font-color: red;
}
You put the nav-aboutme, and nav-contact ids on each of your menu items.
The style will automatically be selected by CSS depending on which body id they are inside of.
I normally do it the way Brian suggested, but to accommodate for a template which a designer gave me which used the more common class="selected" method, I wrote a {% nav %} template tag.
Your HTML navigation template will look something like:
{% block nav %}
<ul class="nav">
<li{% if nav.home %} class="selected"{% endif %}>Home</li>
<li{% if nav.about %} class="selected"{% endif %}>About</li>
</ul>
{% endblock %}
To set the navigation in a child template, do:
{% include "base.html" %}
{% load nav %}
{% block nav %}
{% nav "about" %}
{{ block.super }}
{% endblock %}
How about a custom tag which you use to generate your nav item?
The following takes the name of the url for which a nav item should be generated and the text it should display. It generates a li tag with a class of "selected" if the named url's path is the same as the current url (requires 'django.core.context_processors.request' in your TEMPLATE_CONTEXT_PROCESSORS). Within the li, it generates an a tag with the path of the url specified by the url_name. It has the contents specified by contents.
Obviously, this could be tweaked to generate different markup for the nav item, as required.
The rest can be done using CSS.
Advantages:
Easy to use
Little code required
DRY
Could be made to be more flexible
Disadvantages:
Requires 'django.core.context_processors.request'
Requires urls to be named e.g. urlpatterns = patterns('django.views.generic.simple',
...
(r'^$', 'direct_to_template', {'template': 'index.html'}, 'index'),
...
). This could potentially be done differently (e.g. pass in url).
Doesn't cope with pages not exactly equal to the specified and therefore will not apply the selected class to the li when on a page lower in the url heirarchy. For example, if I'm on /products/, it will highlight the nav item directing to /products/. If I'm on /products/myProduct/, it will not highlight the /products/ link. This could be coded around, but it would force people to use sensible urls. For example, change the additionalAttrs assignment to additionalAttrs = ' class=selected' if (context['request'].path.startswith(path) and path != '/') or (context['request'].path == path) else ''.
Code:
from django import template
from django.core.urlresolvers import reverse
register = template.Library()
class NavNode(template.Node):
def __init__(self, url_name, contents):
self.url_name = url_name
self.contents = contents
def render(self, context):
path = reverse(self.url_name)
additionalAttrs = ' class=selected' if path == context['request'].path else ''
return '<li'+additionalAttrs+'>'+self.contents+'</li>'
#register.tag
def nav_link(parser, token):
bits = token.split_contents()
if len(bits) == 3:
contents = bits.pop()
url_name = bits.pop()
else:
raise template.TemplateSyntaxError, "%r tag requires a single argument" % bits[0]
if contents[0] == contents[-1] and contents[0] in ('"', "'"):
contents = contents[1:-1]
return NavNode(url_name, contents)
You can pass request.path to your template
from django.shortcuts import render_to_response
from django.template import RequestContext
return render_to_response('templ.html', {'page':request.path}, context_instance=RequestContext(request))
then use an ugly if template tag to add a CSS class to your menu item
Let's say one has an app named "stackoverflow" and inside of that app folder one has a templates folder with a stackoverflow.html file that extends from the base template.
One way to achieve it is by defining a variable in the views.py of ones stackoverflow app, like
def stackoverflow(request):
return render(request,
'stackoverflow/stackoverflow.html',
{'section': 'stackoverflow'})
Then, in the base template
<li {% if section == "stackoverflow" %} class="selected" {% endif %}>
StackOverflow
</li>
Essentially having the variable section allows to figure the section. Note that one needs a space between section and ==... if one doesn't respect that, then one will get a Django Template Error saying
Could not parse the remainder.