Django question here. So in my base.html, I have a header. The header has links that go to user specific pages. That is, URL's like: /1234/home, /1234/about, etc. where 1234 is the id of a user. My question is, if I want to create a url pointing to a django view method called home with signature home (request, user=0) where user is a different user from the logged in user, how would I do this in the template? To clarify, I am using Django's {% url ... %} template tag and I can't just do {% url [view_name] request.user.id %} because I want the user id from the url, not the id of the logged in user. Additionally, I would prefer avoiding passing a template variable to get the id of the user in the url because then I would have to do that on every page.
Get the id you want as a kwarg to your view, and pass it as a variable to your template through the context to use in the {url ...}.
If you want to persist a variable across all your templates, look at context processors. They are added to each RequestContext so they are accessible to all templates assuming you use the render shortcut or pass the RequestContext to your template.render.
https://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext
https://github.com/django/django/blob/stable/1.5.x/django/core/context_processors.py
There are some default ones that django uses. It's fairly simple to add your own.
I imagine your context processor will look something like
context_processors.py
def id_from_url(request):
# Find url id component in request.path
return { 'url_id' : url_id }
Then just add to your settings file appname.context_processors.id_from_url
Related
After setting up the All-Auth app, when user logs in, he gets redirected to: accounts/profile/ page which tells us that the view doesn't exist.
I'm trying to figure out what kind of thing to include there, and decided to allow the user to change their basic information.
I have a users app with a Teacher model, which is set up as follows:
class Teacher(models.Model):
user = models.ForeignKey(User, unique=True)
rate = models.CharField(max_length=200)
availability = models.BooleanField(default=False)
verified = models.BooleanField(default=False)
I want the accounts/profile/ page to show a form, containing this information. The user can edit these fields and also edit their Firstname, Lastname and Email, which belong to a different Model - User.
I can't seem to get started on this. When I created a detailed view for the profile page, I get an error saying:
No PK or SLUG provided
I want Django to change the current users info, not based on the primary key in the URL. Do I need a custom view? I've looked at [other solutions1
but they seem to be utilising the private key parameter.
What I need is a working view function, something similar to (not working):
def get_teacher_info(request):
current_user = request.user
teacher = get_object_or_404(Teacher, username=current_user.username)
return render(request, 'account/profile.html', {
'user':current_user,
'teacher': teacher,
'error_message': "The field is blank",
})
and in the accounts/urls.py I've added:
url(r"^profile/$", views.get_teacher_info, name="account_profile"),
but when I make calls like {% teacher.rate %} in the html template, I get:
Invalid block tag on line 5: 'teacher.rate'. Did you forget to register or load this tag?
The def get_teacher_info(request) function and the urls.py entry are working. Looks like the issue may be in the template. Use {{ instead of {% tags. So use {{ teacher.rate }} not {% teacher.rate %} in the template.
Redirecting to /accounts/profile/ After the login is the default behavior of Django. django-allauth is using the same default behavior from Django settings.
Now if you want to modify this default redirect url then change it to
LOGIN_REDIRECT_URL = '/custom/redirect-url'
Important Note: Without a starting / you're redirecting to a path that is appended to the CURRENT URL. You need to use a leading slash to redirect to a path that is appended to the domain root. further details on it. In short, without a starting / you will end up with
../login/callback/custom.redirect-url (appended to current url)
I wonder if there is a way to provide all templates in django application with the current user variable so that I manage login and logout functionality in all pages.
It is a very common practice so there must be an obvious solution I have missed.
I know about the RequestContext Class but that means I have to add it to every view in the application which seems impractical.
May be a custom template tag?
There's a template context processor for this: django.core.context_processors.auth. Check out the documentation, you can access the current user and the permissions in the templates.
You can indeed use a custom tag to achieve that.
Note that by default the custom tag has no access to the request object and therefore to the user data. In order to gain access to the request you can define the custom tag with the takes_context attribute and grab the request from the context:
Something like:
#register.inclusion_tag('userdata.html', takes_context = True)
def userdata(context):
request = context['request']
user = request.user
return {'user':user}
You can just use {{ request.user }} to display the name of the current user, or maybe for example {{ request.user.is_authenticated }} to find out if it's an AnonymousUser or not.
I'm doing a website in html and base (where all pages extend) I want
to put a session of social network icons. As this session is base on
html it should be displayed on all pages of the website.
I do not want
to put this session in a static html, I want to do in django using
models. This is already done.
Question: Do I have to put the session of social network icons on each view, or can I make a separate view and all others extend this view?
How can I do this?
Try using an inclusion tag. You can create a function for doing all of the work to create the sessions and then associate that with a particular block of HTML.
templatetags/session.py
#register.inclusion_tag('includes/session_box.html')
def output_session_box(...):
...
return { .. }
The associated template file, includes/session_box.html, would have the HTML like any template.
And then your base.html would have:
{% load session %}
{% output_session_box ... %}
Use RequestContext and a context_processor to inject template variables into every view using RequestContext.
It's as simple as a python function accepting request as an arg, and returning a dictionary to be passed into your template.
https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.RequestContext
def my_processor(request):
return {'foo': 'bar'}
TEMPLATE_CONTEXT_PROCESSORS = (
# add path to your context processor here.
)
I generally have a per-project processor for the basics... It's exactly how django adds {{ user }} or {{ STATIC_URL }} to every template.
I have a place in my Django app where I need to construct a callback to my domain after a third-party auth, but I'm stuck on how to do this since that view in question doesn't really map to one model (or rather, the view code references multiple models), and the docs for get_absolute_url() construction and permalinks all reference models.
For instance, in my template I currently have something like:
<a class="btn btn-danger large" href="http://to/third/party?api_key=noneyobiz&cb=http://localhost:8000/signup">Join via Somethingorother</a>
the line for this view in urls.py is:
url(r'^signup/$', 'signup', name="signup"),
I want the hardcoded 'http://localhost:8000/signup' to be dynamic. I'm hoping this functionality doesn't depend on my using generic views. Actually I don't understand why generating a permalink is even tied to models at all, it seems like it should only depend on the urlconf. What am I missing here?
permalink is only for the use case when you are referencing a model directly. To find a non-model-based URL, you can use the url tag - in your case, {% url signup %}.
permalink is a thin wrapper of django.core.urlresolvers.reverse. Its belongs to django.db.models to be a shortcut because we usually write reverse inside get_absolute_url of models. So use reverse here
from django.core.urlresolvers import reverse
path = reverse('signup')
Update
To use absolute URI, you could
hardcode in settings or use something like Site.objects.get_current() w/ the path you get from reverse or url to get the absolute URI, as Daniel suggested.
If your callback URI is in the same domain w/ the view rendering the template, you could rely on request to get actual absolute URI:
request.build_absolute_uri(reverse('signup'))
Furthermore, you may want to escape the URI in template, like {{ absolute_uri|urlencode }}. or in view through urllib.quote or urllib.urlencode
{% include 'django.contrib.auth.views.login' %}
I don't want to write everything by hand.. I hate this really, django full of automatic stuff.
Goal is to include registration/login.html into base.html, so that I could have this form in every page
If I include only template itself (registration/login.html), problem appears that "form.login", I mean "form" var is not defined because this one comes from VIEW which called when you going to login url. So how can I call that view MANUALLY with include or at least to grab django.contrib.auth.views.login variables by my self in my own view and pass then to base.html?
P.s. It's not just about login form, I think there will be more situations like this
I have found better solution in #django irc.
They called inclusion tags
I'll give you my code, because I got lot's of problem learning new stuff in django =)
file: templatetags/form_login.py
from django import template
register = template.Library()
from django.contrib.auth.forms import AuthenticationForm
#register.inclusion_tag('registration/login.html')
def form_login():
return { 'form': AuthenticationForm() }
Now you can have your form anywhere, this will prerender template and THAT'S IT! no stupid context processors which requires to modify whole project settings.py, which is really sux if you writing stand alone little application..
If you need login-form on every page
Create a context processor:
def login_form_processor(request):
return {
'login_form': LoginForm(request.POST or None)
}
Add it to settings.CONTEXT_PROCESSORS.
Include the template for login form:
{% with login_form as form %}
{% include "registration/login.html" %}
{% endwith %}
You can also make you form lazy-loading, so form will not be created until it is used for the first time.
from django.utils improt functional
def login_form_processor(request):
create_login_form = lambda: LoginForm(request.POST or None)
return {
'login_form': functional.lazy(create_login_form, LoginForm)
}
But I guess you won't want the lazy-loading feature, because login-form is cheap to initialize.
Reusing views
Concerning the "grabbing variables" part from your question: you cannot grab variable from view. Django view is method which returns response object. You can not get variables from response. However some of views accept extra_context and other attributes. Those attributes allow you to configure those views in urls, or to wrap them with your own view, for example:
def my_login_view(request):
some_extra_data = get_some_data()
extra_context = {
'some_extra_var': some_extra_data
}
return login_view(request, extra_context=extra_context, template="my_template.html")
This is not exactly grabbing the variables from views, more like augmentation of existing views.
If you expect to have more situations like this, do less data-porcessing in views. Call some methods which checks for permissions. Collect some data from context-processors. Return rendered response. Now you can reuse the data in other views.
You can specify the action on the form html to point to the URL that accesses the corresponding view.
If you want a form, say called as login_form always populated in all templates, then put it in the context_processors.
Browsing the code for django.contrib.auth.views, you will see that the variables form, site and *site_name* are passed to the template.
Either you (1) provide your custom registration form or (2) you can just import django.contrib.auth.forms.AuthenticationForm in your view if you want to use it.