django-profile passing variables to template - django

Django newbie at work and I could use some pointers. I'm using django-profile and have been working on my profile page which is handled by the views.profile_detail. The problem I am facing is I am unable to put another variable in my template by using this view. Here is my view function:
def profile_detail(request, username, public_profile_field=None,
template_name='profiles/profile_detail.html',
extra_context=None):
"""
Detail view of a user's profile.
If no profile model has been specified in the
``AUTH_PROFILE_MODULE`` setting,
``django.contrib.auth.models.SiteProfileNotAvailable`` will be
raised.
If the user has not yet created a profile, ``Http404`` will be
raised.
**Required arguments:**
``username``
The username of the user whose profile is being displayed.
**Optional arguments:**
``extra_context``
A dictionary of variables to add to the template context. Any
callable object in this dictionary will be called to produce
the end result which appears in the context.
``public_profile_field``
The name of a ``BooleanField`` on the profile model; if the
value of that field on the user's profile is ``False``, the
``profile`` variable in the template will be ``None``. Use
this feature to allow users to mark their profiles as not
being publicly viewable.
If this argument is not specified, it will be assumed that all
users' profiles are publicly viewable.
``template_name``
The name of the template to use for displaying the profile. If
not specified, this will default to
:template:`profiles/profile_detail.html`.
**Context:**
``profile``
The user's profile, or ``None`` if the user's profile is not
publicly viewable (see the description of
``public_profile_field`` above).
**Template:**
``template_name`` keyword argument or
:template:`profiles/profile_detail.html`.
"""
user = get_object_or_404(User, username=username)
# accuracy = ''
try:
profile_obj = user.get_profile()
accuracy = str(profile_obj.number_of_answers / profile_obj.number_of_answers) + '%'
except ObjectDoesNotExist:
raise Http404
if public_profile_field is not None and \
not getattr(profile_obj, public_profile_field):
profile_obj = None
if extra_context is None:
# extra_context = {'accuracy': potato}
extra_context = {}
context = RequestContext(request)
# context['accuracy'] = 'potato'
for key, value in extra_context.items():
context[key] = callable(value) and value() or value
return render_to_response(template_name,
{'profile': profile_obj},
# { 'profile': profile_obj, 'accuracy': accuracy},
# locals(),
context_instance=context)
and here is my template:
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<p><strong>Level:</strong><br>{{ profile.level }}</p>
<p><strong>Bounty Points:</strong><br>{{ profile.total_bounty_points }}</p>
<p><strong>Number of questions:</strong><br>{{ profile.number_of_questions_asked }}</p>
<p><strong>Number of replies:</strong><br>{{ profile.number_of_replies }}</p>
<p><strong>Number of answers:</strong><br>{{ profile.number_of_answers }}</p>
<p><strong>Accuracy:</strong><br>{{ accuracy }}</p>
<p><strong>Number of times reported:</strong><br>{{ profile.reported_by_others }}</p>
{% endblock %}
May I know where the value profile is being passed from? Is it from the dictionary {'profile': profile_obj} or is it from the context? I tried commenting out both but the template still renders fine.
I have also tried to create a new variable called accuracy in my template but I am unable to get it to render and the template simply fails silently. I then added TEMPLATE_STRING_IF_INVALID = '%s' to my settings file which allowed me to see that the accuracy variable is not found. May I know what I did wrong?
Any advice would be greatly appreciated! Thanks :)

Argh I found the problem! I was changing the wrong file >_< as my python install had written to the default directory.

Related

Pass other object/data into Flask Admin model view edit template

I'm extending the edit template for a ModelView so that I can show some other information from the database that is relevant for determining how to edit the record in this view. I know how to extend the template and get it to work, but I can't figure out how to query an object and use it in the template.
Also I need to use the value from the model/record in querying the new object I need to pass.
Here is my code from init.py:
class MilestoneView(ModelView):
edit_template = '/admin/milestone_model/milestone_edit.html'
can_delete = True
#i need something like this to work:
referrals = Referral.query.filter_by(email=model.email)
#then i need to pass referrals into the template
admin = Admin(app, name="My App", template_mode='bootstrap3')
admin.add_view(MilestoneView(Milestone, db.session, name='Milestones'))
Then from milestone_edit.html, I want something like this to work:
{% extends 'admin/model/edit.html' %}
{% block body %}
{{ super() }}
{% for r in referrals %}
<p>{{ r.name }}</p>
{% endif %}
{% endblock %}
But of course the referrals object is not available to use in the template. How do I customize this ModelView in order to pass this object in from the init file? I've reviewed the available posts on this subject(ish) on here and haven't found an answer. Thanks in advance.
Override your view's render method, see code on Github, and test if the view being rendered is the edit view. Now you can inject any data into the kwargs parameter. For example:
class MilestoneView(ModelView):
def render(self, template, **kwargs):
# we are only interested in the edit page
if template == 'admin/model/milestone_edit.html':
# Get the model, this is just the first few lines of edit_view method
return_url = get_redirect_target() or self.get_url('.index_view')
if not self.can_edit:
return redirect(return_url)
id = get_mdict_item_or_list(request.args, 'id')
if id is None:
return redirect(return_url)
model = self.get_one(id)
if model is None:
flash(gettext('Record does not exist.'), 'error')
return redirect(return_url)
referrals = Referral.query.filter_by(email=model.email)
kwargs['referrals'] = referrals
return super(MilestoneView, self).render(template, **kwargs)
Note how the model is retrieved. This is a direct copy of the code in method edit_view code. Adjust the code for your use-case.
Use the variable referrals in your edit Jinja2 template.
The render method is called in the following routes for each view:
'/' - i.e. the list view code
'/new/' - code
'/edit/' - code
'/details/' - code

Restrict access to Django template

I need to restrict access to a template in Django. This is the scenario:
A guest user uses a form
If the form is validated and fine send the user to the example.com/success/ url.
If the guest user tries to send that link example.com/success to a friend. The friend will see that page as a 404.
I have no clue how to achieve this. Any ideas?
Instead of going to a different URL (/success/), you could just show something different when the form was properly filled. For example, in your view:
def my_view(request, ...):
form = ...
show_success = False
if ... post method ...:
if form.is_valid():
... save etc. ...
show_success = True
return render(request, ..., {'show_success': show_success})
In your template:
{% if show_success %}
Success message here
{% else %}
Form here
{% endif %}

Update non-request user information in a Django template and view

So I have a ManageUserForm in forms.py-- it renders correctly but it doesn't pull the right data from the user i'm trying to edit.
In the template, I have a for loop that works correctly
{% for tenants in tenants %}
{{ tenants.user }} {{ tenants.type }}
{% endfor %}
This template renders the list of objects in the UserProfile. And it does it correctly. The challenge I face is updating the "tenants.type" attribute. Again, the type shows up correctly but I don't know how to update it from this template page.
#views.py
def manage_users(request):
tenants = UserProfile.objects.all()
form = ManageUserForm(request.POST or None)
if form.is_valid():
update = form.save(commit=False)
update.save()
return render_to_response('manage_users.html', locals(), context_instance=RequestContext(request))
#forms.py
class ManageUserForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ('full_name', 'user',)
`I think I need to call an instance but I have no idea how to do so for the non-request users AND still follow the pattern for the template. The template basically is a list of users where the request user (staff user) will be able to change the data in the list.
Thank you for your help!
You have one form for one user. You need a FormSet if you want to use that form to edit multiple tenants. Editing objects and displaying them are entirely different beasts; dont' confuse them.
formset = modelformset_factory(form=ManageUserForm, queryset=tenants)
Update:
You should have one {{ form.management_form }} and the rest of the {% for form in formset %}{{ form }}{% endfor %} in one <form> tag. All of your forms are the first form in the formset.
You should rewrite your template loop to iterate through formset forms instead of tenant objects. The tenant object can be accessed through {{ form.instance }}
Update 2:
You have an extra form because you probably haven't passed in the extra=0 parameter to the modelformset_factory function. These forms are typically used to add/edit data; thus it has support for adding N blank forms for creating.

Django user injection in model properties

I have this models in Django:
News
Comments
Reactions
Relations are:
a News has various Comments
a Comment has various Reactions
The problem is the user (in request / session): the user may subscribe to a reaction, or a comment; he may be logged in or not. (it's a foo example, it doesn't have much sense)
I can't do in template:
{% for reaction in this_news.comments.reactions %}
{{ reaction.name }}
{% if reaction.user_subscribed %} #reaction.user_subscribed(request.user)...
You have subscribed this reaction!
{% endif %}
{% endfor %}
Problems are:
I can't call the method in the template with a parameter (see the comment above)
Models don't have access to request
Now i'm calling an init_user method in News Model, passing the request. Then i have the same method in Comment and Reaction model, and i have to set the user_subscribed property cycling the children of each model.
Isn't there a smarter way to do this?
EDIT: thanks to the Ignacio's hint about using custom tag i'm trying to do a generic mode to pass the user (avoiding the use of closures because i don't know how to use them atm):
def inject_user(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, method_injected, user = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires exactly three arguments" % token.contents.split()[0])
return InjectUserNode(method_injected, user)
class InjectUserNode(template.Node):
def __init__(self, method_injected, user):
self.method_injected = template.Variable(method_injected)
self.user = template.Variable(user)
def render(self, context):
try:
method_injected = self.method_injected.resolve(context)
user = self.user.resolve(context)
return method_injected(user)
except template.VariableDoesNotExist:
return ''
When i use it {% inject_user object.method_that_receives_a_user request.user %} i come to this error 'str' object is not callable in method_injected(user); how i can fix that?
Write custom template tags that take the user and set a context variable to indicate presence or absence of the criterion.
I've resolved it in a less elegant way, but it worked for me.
I've created a sort of singleton in my User defined class, with a property that i set in every view i need it.
The property is User.current.
Then, inside the models, where i need that i get the current user looking in User.current.

Django question: how can I get user specific information related to a domain object?

Here are my models:
class Activity(models.Model):
title = models.CharField(blank=False, max_length=100)
description = models.TextField(blank=False)
class UserActivityWork(models.Model):
activity = models.ForeignKey(Activity)
user = models.ForeignKey(User)
hours_worked = models.FloatField()
comment = models.TextField()
Example data would be, an Activity of "climbing Mt Everest" and each user would be able to input how long it took them and a comment.
Here's my question: How can I display a list of all the Activities, and if the user has entered data for that Activity, display the pertinent details next to the Activity?
So far, I have considered:
creating a dictionary of
UserActivityWork with a key of the Activity id and a value of the user's UserActivityWork. This would be fine with
me, but I have no idea of how to do
this in django's templating system (ie, how do you say: {{ user_work[activity.id] }})
creating an object that would hold
both the Activity and
UserActivityWork. I haven't done this
one, because I am hoping that django
has a better way to do this.
Any insight would be greatly appreciated!
Assuming you have 2 querysets accessable from within your template (say as activities and user_activities)
A naive way would be to iterate over each activity and then over each user activity.
{% for activity in activities %}
{{ activity.title }}
{% for user_activity in user_activities %}
{% ifequal user_activity.activity activity %}
Display userdata
{% endifequal %}
{% endfor %}
{% endfor %}
Dictionary lookups can be performed in templates by using a dot (.)
Technically, when the template system encounters a dot, it tries the following lookups, in this order:
Dictionary lookup
Attribute lookup
Method call
List-index lookup
Another option would be to create a custom template tag. You could loop over the activity list as before and then pass the activity and either the user_activity list or the user to the tag to perform the lookup and render the required data.
Thanks for the hint, Gerry. I found that writing a custom template tag as you suggested was the way to go.
Here are the gory details, in case anyone stumbles across this.
In the view method, I published a dictionary "user_activity_status" which contains a key of activity.id and value of UserActivityWork object for the logged in user's work on that activity
This is the the relevant section of the template. Basically this going to add a variable "map_value" with a value of
getattr(user_activity_status[activity.id], "comment")
Here's the template:
{% load *file-name-of-the-templatetag-file* %}
{% access_map_method user_activity_status activity.id comment %}
{% if map_value %}
{{ map_value }}
{% else %}
get working sucka!
{% endif %}
here is the section of the templatetag file (see Gerry's links for the details of how to set this up)
from django import template
register = template.Library()
#register.tag(name="access_map_method")
def do_access_map_method(parser, token):
try:
tag_name, dict_name , key_name, method_name = token.contents.split()
except ValueError:
msg = '%r tag requires three arguments' % token.contents[0]
raise template.TemplateSyntaxError(msg)
return MapNode(dict_name , key_name, method_name)
class MapNode(template.Node):
def __init__(self, dict_name, key_name, method_name):
self.dict_var = template.Variable(dict_name)
self.key_var = template.Variable(key_name)
self.method_name = method_name
def render(self, context):
try:
dict_obj = self.dict_var.resolve(context)
key_obj = self.key_var.resolve(context)
if key_obj in dict_obj.keys():
if self.method_name:
context['map_value'] = getattr(dict_obj[key_obj], self.method_name)
else:
context['map_value'] = dict_obj[key_obj]
else:
context['map_value'] = ''
except template.VariableDoesNotExist:
context['map_value'] = ''
return ''