This may seem like a silly question but I can't find anything to help.
How would you create a logout button on every view like the one available in the admin page?
Use templates inheritance:
https://docs.djangoproject.com/en/dev/topics/templates/#template-inheritance
or include tag:
https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#include
Example with template inheritance:
We have a base template for all pages on our application:
base.html
<html>
<head>...</head>
<body>
logout # or use the "url" tag: {% url logout_named_view %}
{% block content %} {% endblock %}
</body>
</html>
other_pages.html
{% extends "base.html" %}
{% block content %}
<div class="content">....</div>
....
....
{% endblock %}
Now, we have a logout link on all pages inherited from the base.html
Example with include tag:
user_panel.html
<div class="user_panel">
logout
</div>
other_pages
<html>
<head>...</head>
<body>
{% include "user_panel.html" %}
...
...
</body>
</html>
I recommend for a solution to your problem using template inheritance
I wanted to extend the approved reply with my solution. What i did is:
from django.shortcuts import redirect
from django.contrib.auth import logout
If you want a method that checks if the user is already logged in you can create a function:
def UserLoggedIn(request):
if request.user.is_authenticated == True:
username = request.user.username
else:
username = None
return username
this checks if the user is already logged in and returns its username
then create a request function that checks if the username is not None (or just ommit the if statement)
def logout_view(request):
username = UserLoggedIn(request)
if username != None:
logout(request)
return redirect(your_home_view_here)
lastly just import your view into urls.py and create a path in urlpatterns
path('logout/', logout_view, name='logout')
Related
I'm making a messaging system which has a message model and some views for things like inbox and conversation.
Inbox view and template works and is linked to the conversation through the id of the sender. However, when I click on the senders name (The senders name is the link (template link is {{ sender }}) that is meant to trigger the conversation view which is supposed to render the template convo.html ( the project level url for that is url(r'^messaging/inbox/conversation/(?P<id>[\w-]+)/$', message_views.Conversation.as_view(), name="conversation"),) ), Django changes the url but stays on the same page(So it triggers the inbox view and stays on inbox.html and changes the url but doesn't trigger the conversation view and doesn't render the convo.html template). I've tried changing from a function based view to a class based view and it did nothing. What am I doing wrong?
Note: I'm still new to django so forgive me for any bad practices; I'm attempting to teach myself.
views.py (inbox and conversation views)
def inbox(request):
messages = Message.objects.filter(receiver=request.user.id)
senders = {}
for message in messages:
if message.sender not in senders:
senders[message.sender] = message
return render(request, 'inbox.html', {'senders': senders})
# There is only ever one of this view, either the CBV or FBV
def conversation(request, other_user_id):
print(request.method)
if request.method == "POST":
reply = request.post['reply']
other_user = CustomUser.objects.get(id=other_user_id)
message = Message(sender=request.user, receiver=other_user)
message.save()
return redirect("conversation")
else:
other_user = CustomUser.objects.get(id=other_user_id)
messages = Message.objects.filter(Q(receiver=request.user) & Q(sender=other_user))
print(messages)
return render(request, 'convo.html', {'messages': messages})
# Class based conversation view
class Conversation(View):
def post(request, other_user_id):
reply = request.post['reply']
other_user = CustomUser.objects.get(id=other_user_id)
message = Message(sender=request.user, receiver=other_user)
message.save()
return redirect("conversation")
def get(request, other_user_id):
other_user = CustomUser.objects.get(id=other_user_id)
messages = Message.objects.filter(Q(receiver=request.user) & Q(sender=other_user))
print(messages)
return render(request, 'convo.html', {'messages': messages})
template - inbox.html
<!DOCTYPE html>
{% extends "accbase.html" %}
<!--Omitted outbox in favor of conversation-->
{% block content %}
<div class="inbox">
<h1>Inbox</h1>
<hr>
{% for sender in senders %}
{{ sender }}
<br>
{{sender.message.created_at}}
<br>
{% endfor %}
</div>
{% endblock %}
template - convo.html
<!DOCTYPE html>
<!--Template for reciever side of conversation-->
{% extends "accbase.html"%}
{% block content %}
<div class="conversation">
<h1>Conversation with {{sender}}</h1>
<p>
{% for msg in messages %}
<!--Order is descending ~~ bottom to top: newest to oldest-->
{{ msg.content }}
<br>
{% endfor %}
<hr>
<form method="POST">
<label>Reply</label>
<input type="text" name="reply">
</form>
</p>
<button type="submit">Send</button>
</div>
{% endblock %}
project level urls.py
from django.urls import path, re_path, include
from django.conf.urls import url
from messaging import views as message_views
urlpatterns = [
url(r'^messaging/inbox', message_views.inbox, name="inbox"),
url(r'^messaging/inbox/conversation/(?P<id>[\w-]+)/$', message_views.Conversation.as_view(), name="conversation"),
]
As Daniel Roseman said in comments " The problem here is that you didn't terminate the regex for your inbox URL, so it also matches the conversation URL. " so I added the needed $ to the end of the inbox url pattern and that fixed it!
I have tried to create a minimal working example of FormPreview of Django FormTools, but it did not work.
forms.py
This is a very simple form.
from django.forms import CharField, Form, Textarea
class TextForm(Form):
text = CharField(widget=Textarea)
preview.py
This is with slight adjustments just copied from the documentation. It is of curse incomplete but I did not changed it because I did not even got to a preview page of any kind.
from django.http import HttpResponseRedirect
from formtools.preview import FormPreview
class TextFormPreview(FormPreview):
def done(self, request, cleaned_data):
# Add stuff later, once I get to a preview.
return HttpResponseRedirect('/')
urls.py
Here must be some mistake, but I don't know how it should be correct. Should I remove the first URL? How does FormPreview know what the correct view to use is?
from django.conf.urls import url
from django import forms
from .forms import TextForm
from .preview import TextFormPreview
from . import views
urlpatterns = [
url(r'^$', views.textform),
url(r'^post/$', TextFormPreview(TextForm)),
]
views.py
My simple views.py.
from django.shortcuts import render
from .forms import TextForm
def textform(request):
if request.method == 'POST':
form = TextForm(request.POST)
if form.is_valid():
text = form.cleaned_data['text']
print(text)
if request.method == 'GET':
form = TextForm
context = {
'form': form
}
return render(request, 'text.html', context)
templates/text.html
And a simple template.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Text</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.manual_recipients.errors }}
{{ form.text }}
</div>
<input type="submit" value="OK"/>
</form>
</body>
</html>
templates/base.html
base.html is just an empty file. If I go to ^post/$ it says it requires a base.html. Why? I do not know.
As said, I did all the steps from the documentation but it is obviously incomplete. How do I fix it?
The default template for FormPreview extends base.html. and includes a single block content.
In your base.html, you need to include {% block content %}{% endblock %}. A minimal template would be:
<html>
<body>
{% block content %}{% endblock %}
</body>
</html>
See the docs on template inheritance for more info.
I have a number of templates that extend base.html. I want the base.html template to house my global navigation and have the text and links in the global navigation be based on a model Division (i.e. the CharField in the model will be used as the button text in the global nav, and the id will be used to build the URL). I thought tags might work, but what I end up with is this (yes, I'm new to Django and Python):
current_tags.py
from django import template
# Import your model
from libs.display.models import Division
from django.db import models
register = template.Library()
#register.simple_tag
def do_get_divisions(self):
d = Division.objects.all()
mylist = []
for each in d:
mylist.append(str(each.DivisionValue))
return my list
I'm attempting just getting the text value in each object to print at this point because I can't get or work with the objects in the template, as you'll see below.
base.html
<!DOCTYPE html>
<html>
<head>
{% load staticfiles %}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}style.css" />
</head>
<body>
{% load current_tags %}
<p>{% do_get_divisions "" %}</p> **THIS ACTUALLY PRINTS mylist**
{% for each in do_get_divisions %} **THIS DOESN'T PRINT ANYTHING**
<p>{{ each }}</p>
{% endfor %}
{% block content %}
{% endblock %}
</body>
</html>
I'm sure there is a better way to do global nav based on a model in Django. I basically want to get all the Division objects and put them into a <ul> to use as my global nav in base.html. I am not that familiar with Django, but my views.py don't help me because I am rendering other templates, not base.html, which are extending base.html. For what it's worth, here's one views.py, where /display/info.html template extends base.html:
# Create your views here.
from django.http import HttpResponse
from apps.pulldata.models import Data
from django.shortcuts import render, get_object_or_404
from django.http import Http404
def info(request, group_id):
group = get_object_or_404(Data, pk=group_id)
s = group.XInGroup.all()
return render(request, 'display/info.html', {'Group': group, 's': s})
You cannot put a templatetag into another. Your for-loop is a templatetag that expects the name of an element in your context to iterate over.
If you want to handle the navigation in a template tag you should consider using inclusion tags.
Inclusion tags are functions that use templates to render their data.
A very basic implementation could look something like this:
tags.py
#register.inclusion_tag('navigation.html')
def navigation(selected_id=None):
return {
'navigation': Division.objects.all(),
'selected':selected_id,
}
In your templatetag file you create a dictionary with the navigation items and optionally the currentl selected item to highlight this navigation element.
navigation.html
<ul>
{% for item in navigation %}
<li{% if item.id == selected %} class="selected"{% endif %}>
{{ item.DivisionValue }}
</li>
{% endfor %}
</ul>
the navigation.html uses the dictionary from the python function as context so you start with simply iterating over the navigation.
base.html
{% navigation %}
or
{% navigation current_division.id %}
In the base.html you call the inclusion tag like a normal template tag. if you want to highlight the current item you add its id as a argument.
I would like to add a button next to "add" button in list view in model for my model and then create a view function where I will do my stuff and then redirect user back to list view.
I've checked how to overload admin template, but I still dont know, where should I put my view function where I will do my stuff, and how can I register that view into admin urls.
There is also question about security. I would like to have that action inside admin, so if u r not logged in, u cannot use it.
I've found this, but I don't know if it's the right way: http://www.stavros.io/posts/how-to-extend-the-django-admin-site-with-custom/
When several applications provide different versions of the same
resource (template, static file, management command, translation), the
application listed first in INSTALLED_APPS has precedence.
- Django documentation on INSTALLED_APPS
Make sure your app is listed before 'django.contrib.admin' in INSTALLED_APPS.
Create a change_list.html template in one of the following directories:
# Template applies to all change lists.
myproject/myapp/templates/admin/change_list.html
# Template applies to change lists in myapp.
myproject/myapp/templates/admin/myapp/change_list.html
# Template applies to change list in myapp and only to the Foo model.
myproject/myapp/templates/admin/myapp/foo/change_list.html
The template should be picked up automatically, but in case it is not on one of paths listed above, you can also point to it via an admin model attribute:
class MyModelAdmin(admin.ModelAdmin):
#...
change_list_template = "path/to/change_list.html"
You can lookup the contents of the original change_list.html it lives in path/to/your/site-packages/django/contrib/admin/templates/admin/change_list.html. The other answer also shows you how to format the template. Nikolai Saiko shows you how to override the relevant parts using 'extends' and 'super'. Summary:
{% extends "admin/change_list.html" %} {% load i18n %}
{% block object-tools-items %}
{{ block.super }}
<li>
<a class="historylink" href="...">My custom admin page</a>
</li>
{% endblock %}
Let's fill href="..." with an url. The admin url names are in the namespace 'admin' and can be looked up like this:
{% url 'admin:custom_view' %}
When you are adding a button to change_form.html you maybe want to pass in the current object id:
{% url 'admin:custom_view' original.pk %}
Now create a custom view. This can be a regular view (just like other pages on your website) or a custom admin view in admin.py. The get_urls method on a ModelAdmin returns the URLs to be used for that ModelAdmin in the same way as a URLconf. Therefore you can extend them as documented in URL dispatcher:
class MyModelAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(MyModelAdmin, self).get_urls()
my_urls = patterns('',
url(r'^my_view/$', self.my_view, name="custom_view")
)
return my_urls + urls
def my_view(self, request):
# custom view which should return an HttpResponse
pass
# In case your template resides in a non-standard location
change_list_template = "path/to/change_list.html"
Read the docs on how to set permissions on a view in ModelAdmin: https://docs.djangoproject.com/en/1.5/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_urls
You can protect your view and only give access to users with staff status:
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def my_view(request):
...
You might also want to check request.user.is_active and handle inactive users.
Update: Take advantage of the framework and customise as little as possible. Many times actions can be a good alternative: https://docs.djangoproject.com/en/1.5/ref/contrib/admin/actions/
Update 2: I removed a JS example to inject a button client side. If you need it, see the revisions.
Here is another solution , without using of jQuery (like one provided by allcaps). Also this solution provides object's pk with more intuitive way :)
I'll give my source code based on that link (follow link above for more info):
I have an app Products with model Product. This code adds button "Do Evil", which executes ProductAdmin.do_evil_view()
File products/models.py:
class ProductAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super().get_urls()
my_urls = patterns('',
(r'^(?P<pk>\d+)/evilUrl/$', self.admin_site.admin_view(self.do_evil_view))
)
return my_urls + urls
def do_evil_view(self, request, pk):
print('doing evil with', Product.objects.get(pk=int(pk)))
return redirect('/admin/products/product/%s/' % pk)
self.admin_site.admin_view is needed to ensure that user was logged as administrator.
And this is template extention of standard Django admin page for changing entry:
File: {template_dir}/admin/products/product/change_form.html
In Django >= 1.8 (thanks to #jenniwren for this info):
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools-items %}
{{ block.super }}
<li><a class="historylink" href="evilUrl/">{% trans "Do Evil" %}</a></li>
{% endblock %}
If your Django version is lesser than 1.8, you have to write some more code:
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools">
<li><a class="historylink" href="history/">{% trans "History" %}</a></li>
<li><a class="historylink" href="evilUrl/">{% trans "Do Evil" %}</a></li>
{% if has_absolute_url %}
<li><a class="viewsitelink" href="../../../r/{{ content_type_id }}/{{ object_id }}/">{% trans "View on site" %}</a></li>
{% endif%}</ul>
{% endif %}{% endif %}
{% endblock %}
Can someone please explain in details how to make a registration and authentication in as easy words as possible ? I made authentication (login) with django.contrib.auth but what I want to get is a full register(social/non)+login. Already saw the django-allauth, django-social-auth, django-social but still can't get it working without hacking a lot. Heard that django-registration and django-profiles can make it a lot easier, but i can't handle it. For example,
~/.virtualenvs/plinter/lib/python2.7/site-packages/registration/backends/default/urls.py
needs a small hack to work:
# from django.views.generic.simple import direct_to_template
from django.views.generic import RedirectView
...
RedirectView.as_view(url='/registration/activation_complete.html'),
# direct_to_template,
# {'template': 'registration/activation_complete.html'},
...
The DjangoBook gives simple examples of Contact and search forms. But i can't expand it on user registration and login.
So can anyone give kis example of working registration and login?
Update
Here is a simple example of login. Now django-allauth or social auth or registration2 are in consideration...
Update2
django-allauth seems to be the best solution for easier authentication. Add correctly apps in settings, register fb/google/etc apps and register through admin and use template inheritance to change default pages design.
THIS is a very good tutorial about login & Co. It explains very well how to perform login by ourself ad override existing django login pages.
UPDATE:
Here Overview for Registration and Login. For more details go to the link.
To Register:
Views and URLs
Go to the lower site folder (where the settings.py file is) and open
the views.py file. At the top make sure the following imports are
included. Add them if not:
from django.shortcuts import
render_to_response from django.http import HttpResponseRedirect from
django.contrib.auth.forms import UserCreationForm from
django.core.context_processors import csrf
Below that add the following functions (you can put them after the
Login functions):
def
register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register/complete')
else:
form = UserCreationForm()
token = {}
token.update(csrf(request))
token['form'] = form
return render_to_response('registration/registration_form.html', token)
def registration_complete(request):
return render_to_response('registration/registration_complete.html')
Open the urls.py file in the site folder (same folder as
settings.py). Below urlpatterns = patterns('', insert the following
lines.
# Registration URLs
url(r'^accounts/register/$', 'simplesite.views.register', name='register'),
url(r'^accounts/register/complete/$', 'simplesite.views.registration_complete',
name='registration_complete'),
Templates We will assume your site already has a templates
directory and a base.html file with the navigation bar. Open the
base.html file and in the nav element add a navigation menu link to
the login page
register
If one does not already exist, go to the templates folder and create
a folder inside it named registration. Create a file called
registration_form.html, save it to the templates/registration folder,
then populate it with the following:
{% extends "base.html" %} {% block title %}Register{%
endblock %} {% block content %}
<h2>Registration</h2>
<form action="/accounts/register/" method="post">{% csrf_token %}
{{form.as_p}} <input type="submit" value="Register" />
</form>
{% endblock %}
Create a file called registration_complete.html, save it to the
templates/registration folder, and populate it with the following:
{% extends "base.html" %} {% block title %}You are
Registered{% endblock %} {% block content %}
<h2>Thank you for Registering</h2> <p>Please Login</p>
{% endblock %}
To Login:
Views and URLs Open the views.py file in the lower site folder (where the settings.py file is). If there isn't one then create and
save it. At the top of the file insert the following import: from
django.shortcuts import render_to_response Below that you only need to
add one function rendering the loggedin page. The other functions
(login and logout) are in the views.py file in the Django Auth folder.
def loggedin(request):
return render_to_response('registration/loggedin.html')
# Optionally, if you want to show their username when they login then call their username in the view. Change the loggedin function to:
def loggedin(request):
return render_to_response('registration/loggedin.html',
{'username': request.user.username})
Open the urls.py file in the site folder (same folder as settings.py).
Below urlpatterns = patterns('', insert the following lines.
# Auth-related URLs:
url(r'^accounts/login/$', 'django.contrib.auth.views.login', name='login'),
url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', name='logout'),
url(r'^accounts/loggedin/$', 'simplesite.views.loggedin', name='loggedin'),
With simplesite being the name of the folder that holds the views.py
file that you are calling. Open the settings.py file and at the bottom
insert LOGIN_REDIRECT_URL = '/accounts/loggedin/'. Django's default
is to redirect to /accounts/profile when you log in, which is fine if
you have an profile page at that url. If not you need to change your
settings default for the Login redirect url to the one holding your
loggedin.html page.
Templates
We will assume your site already has a templates directory and a
base.html file with the navigation bar. Open the base.html file and in
the nav element add a navigation menu link to the login page login Add a logout link too logout Create a directory called
registration inside the templates folder. If you do this through the
command line, type mkdir registration Create a file called login.html,
save it to the templates/registration folder, and populate it with the
following:
{% extends "base.html" %}
{% block title %}Log In{% endblock %}
{% block content %}
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="login" />
</form>
{% endblock %}
{{ form.as_table }} uses the Django Forms module to create the form.
You can create an unformatted form by using {{ form }} without the
HTML table tags, or have each field put inside paragraph tags with {{
form.as_p }}, or as an unordered list {{ form.as_ul }}. Optionally, you
can also lay out your own form structure and use the form field tags
as follows:
{% extends "base.html" %}
{% block title %}Log In{% endblock %}
{% block content %}
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{% if form.errors %}
<p>Your Username or Password were not entered correctly. Please try again.</p>
{% endif %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
<td>{{ form.username.errors }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
<td>{{ form.password.errors }}</td>
</tr>
</table>
<input type="submit" value="login" />
</form>
{% endblock %}
Create a file called loggedin.html, save it to the
templates/registration folder, and populate it with the following:
{% extends "base.html" %}
{% block title %}Logged In{% endblock %}
{% block content %}
<h2>You are logged in</h2>
{% endblock %}
If you want to display the username, you would make the adjustment to
the view discussed in the views section. Then change the loggedin.html
template to the below (change the wording as you see fit):
{% extends "base.html" %}
{% block title %}Logged In{% endblock %}
{% block content %}
<h1>Welcome {{username}}</h1>
<p>Thank you for logging in.</p>
<p>Logout</p>
{% endblock %}
Create a file called logged_out.html, save it to the
templates/registration folder and populate it with the following:
{% extends "base.html" %}
{% block title %}Logged Out{% endblock %}
{% block content %}
<h2>Logged out!</h2>
<p>Log back in</p>
{% endblock %}
Trix's approach worked for me, but the logout link was redirecting to the admin logout, instead of logged_out.html.
To fix the redirect, I added a next option to the href:
Logout
In templates/registration:
Renamed logged_out.html to loggedout.html; the underscore caused it to still route to the admin logout page.
In views.py:
def loggedout(request):
return render_to_response('registration/loggedout.html')
And finally, in urls.py:
url(r'^myapp/loggedout/$', 'myapp.views.loggedout', name='loggedout'),