I'm trying to override Django admin index.html to show table contain latest 5 records from a model
I managed to override the template with the following in my app:
from django.contrib import admin
admin.site.index_template = 'admin/dashboard.html'
admin.autodiscover()
But i have no idea how to add the latest records from model as a context to the index.html
Hope someone can help me
class CustomAdminSite(admin.AdminSite):
def index(self, request, extra_context=None):
extra_context = extra_context or {}
# Add your context here
return super(CustomAdminSite, self).index(request, extra_context)
admin_site = CustomAdminSite()
Don't forget to replace default admin with admin_site
Something that worked for me to add extra context was using context processor. Explained here: https://stackoverflow.com/a/34903331/13436391
Django documentation
Related
In the last few weeks I've switched from developing an application that processes a simple XML file, then writes its contents to an Oracle DB (cx_Oracle) and outputs in HTML, to using a Django framework. The Django framework switch wasn't necessary, but since I have an opportunity to develop something by using Django, I thought why not as it is a new area for me and can't damage my CV.
Anyway, I'm having issues with knowing what to write in my urls.py file when importing a Class from the views.py file. Here are the current contents:
urls.py
from myproj.views import Pymat_program
pymatProgram = Pymat_program()
urlpatterns = (
url(r'^pymat/$', pymatProgram.abc),
)
views.py
class Pymat_program:
def abc(self, request):
test = "<html><body>Random text.</body></html>"
return HttpResponse(test)
I've tried various permutations of using request, not using request, and also how the class is called in the url tuple, all to no avail. When I use a definition outside of the class (i.e. not in any class), then this is correctly displayed in HTML.
You don't want to wrap your program in a class. (In general, in Python you should treat modules like, say, Java might treat classes with static members only.)
There are two approaches, really:
Function-based views
urls.py
from myproj.views import abc_view
urlpatterns = (
url(r'^pymat/$', abc_view),
)
views.py
def abc_view(request):
test = "<html><body>Random text.</body></html>"
return HttpResponse(test)
Class-based views
urls.py
from myproj.views import AbcView
urlpatterns = (
url(r'^pymat/$', AbcView.as_view()),
)
views.py
from django.views.generic import View
class AbcView(View):
def get(self, request, *args, **kwargs):
test = "<html><body>Random text.</body></html>"
return HttpResponse(test)
Since you are using class based views in Django, so you have to call this class in URL like:
from myproj.views import Pymat_program
pymatProgram = Pymat_program()
urlpatterns = (
url(r'^pymat/$', pymatProgram.as_view()),
)
and then use get or post methods name in class like:
class Pymat_program:
model = ModelName
def post(self, request, *args, **kwargs):
....
2nd way is to use function based views:
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.view_name),
]
views.py
from django.http import HttpResponse
import datetime
def view_name(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
for more details look into class based views docs
I have a class based view which just displays a list of configurations.
This view is added to the Django Admin site by using the following code:
#admin.register(ZbxHostConf)
class ZbxHostConfListViewAdmin(admin.ModelAdmin):
review_template = 'admin/admzbxhostconf_list.html'
def get_urls(self):
urls = super(ZbxHostConfListViewAdmin, self).get_urls()
my_urls = patterns('',
(r'^zbxhostconflist/$', self.admin_site.admin_view(self.review)),
)
return my_urls + urls
def review(self, request):
return ZbxHostConfListView.as_view()(request)
The template extends the admin/base_site.html template. I can access the site only after I log in to the Django Admin site. Unfortunately the template cannot access the context data provided by the admin view.
As the Django documentation suggests the context data will be provided directly to the TemplateResponse function:
def my_view(self, request):
# ...
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else you want in the context...
key=value,
)
return TemplateResponse(request, "sometemplate.html", context)
For function-based views there is the possibility of the extra_context argument but class-based views don't provide this argument. I guess I have to modify the get_context_data function but I don't really understand how I can provide the admin context data to the get_context_data function of my class based view. Any suggestions?
This might not be a correct answer, but I believed you could try something like this.
#!/usr/bin/python3
from django.contrib import admin
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(admin.site.each_context(self.request))
return context
I want to customize django admin.. For additing I did this :
class SomeAdmin(admin.ModelAdmin):
"""
Customized admin template and corresponding
views for adding media.
"""
add_form_template = "admin/add.html"
def add_view(self, request, form_url='', extra_context=None):
if request.method == "POST":
//YOur logic
return super(SomeAdmin,self).add_view(request)
Its working fine.. In the same way how can I get custom list view and custom change view ?
Any help ?
ModelAdmin has both the change_view() and changelist_view() methods so you can override them the same way.
I have a django website with many urls and views. Now I have asked to redirect all non-authenticated users to a certain landing page. So, all views must check if user.is_authenticated() and return to a new set of landing pages.
Can it be done in a pretty way, instead of messing with my views.py/urls.py that much?
There is a simpler way to do this, just add the "login_url" parameter to #login_required and if the user is not login he will be redirected to the login page. You can find it here
from django.contrib.auth.decorators import login_required
#login_required(login_url='/accounts/login/')
def my_view(request):
...
You can use Middleware.
Something like this will check user auth every request:
class AuthRequiredMiddleware(object):
def process_request(self, request):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('landing_page')) # or http response
return None
Docs: process_request
Also, don't forget to enable it in settings.py
MIDDLEWARE_CLASSES = (
...
'path.to.your.AuthRequiredMiddleware',
)
see the docs for login required decorator
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
another option is to add it to your urls.py patterns, see this answer
urlpatterns = patterns('',
(r'^foo/$', login_required(direct_to_template), {'template': 'foo_index.html'}),
)
As of Django 1.10, the custom middleware classes must implement the new style syntax. You can use the following class to verify that the user is logged in while trying to access any views.
from django.shortcuts import HttpResponseRedirect
class AuthRequiredMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
if not request.user.is_authenticated: # in Django > 3 this is a boolean
return HttpResponseRedirect('login')
# Code to be executed for each request/response after
# the view is called.
return response
You can avoid specifying login_url by setting LOGIN_URL.
Therefore, in settings.py add:
LOGIN_URL = '<some_url>'
And in your views.py annotate relevant functions with only #login_required:
#login_required
def some_view_function(request):
If you need to redirect within a view function, you can do so with:
return redirect_to_login(request.get_full_path())
This can be done with middleware.
I've found a really nifty djangosnippet that does exactly what you are asking for. You can find it here, and it looks like:
from django.http import HttpResponseRedirect
from django.conf import settings
from re import compile
EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]
class LoginRequiredMiddleware:
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
you can copy from your urls.py).
Requires authentication middleware and template context processors to be
loaded. You'll get an error if they aren't.
"""
def process_request(self, request):
assert hasattr(request, 'user'), "The Login Required middleware\
requires authentication middleware to be installed. Edit your\
MIDDLEWARE_CLASSES setting to insert\
'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\
work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
'django.core.context_processors.auth'."
if not request.user.is_authenticated():
path = request.path_info.lstrip('/')
if not any(m.match(path) for m in EXEMPT_URLS):
return HttpResponseRedirect(settings.LOGIN_URL)
All you have to do is to save the file as middleware.py and include the class in you're settings.py, i.e.
MIDDLEWARE_CLASSES += ('projectname.common.middleware.RequireLoginMiddleware',)
You can also define a LOGIN_URL in settings.py, so that you'll be redirected to your custom login page. The default LOGIN_URL is '/accounts/login/'.
Maybe too late but in django 1.9+ it's too easy.
Django introduced Login Required mixin for generic classes and this a great example
here by William S. Vincent
simply in your view add LoginRequiredMixin as parent class
from django.contrib.auth.mixins import LoginRequiredMixin
class BlogUpdateView(LoginRequiredMixin, UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body']
Also you can use login_required decorator for method request
from django.contrib.auth.decorators import login_required
#login_required(login_url='/login/')
def home(request):
return render(request, "home.html")
It's showing like this: http://127.0.0.1:1235/login/?next=/home/
I'm using class based views and I couldn't get any of these solutions to work for what I needed so I'll post my working solution here.
class ManagementPageView(TemplateView):
# Make a dispatch method to handle authentication
def dispatch(self, *args, **kwargs):
# Check if user is authenticated
if not self.request.user.is_authenticated:
# Redirect them to the home page if not
return redirect('home')
# Render the template if they are
return render(self.request, 'management.html')
I have a good many class based views that use reverse(name, args) to find urls and pass this to templates. However, the problem is class based views must be instantiated before urlpatterns can be defined. This means the class is instantiated while urlpatterns is empty leading to reverse throwing errors. I've been working around this by passing lambda: reverse(name, args) to my templates but surely there is a better solution.
As a simple example the following fails with exception:
ImproperlyConfigured at xxxx
The included urlconf mysite.urls doesn't have any patterns in it
mysite.urls
from mysite.views import MyClassView
urlpatterns = patterns('',
url(r'^$' MyClassView.as_view(), name='home')
)
views.py
class MyClassView(View):
def get(self, request):
home_url = reverse('home')
return render(request, 'home.html', {'home_url':home_url})
home.html
<p><a href={{ home_url }}>Home</a></p>
I'm currently working around the problem by forcing reverse to run on template rendering by changing views.py to
class MyClassView(View):
def get(self, request):
home_url = lambda: reverse('home')
return render(request, 'home.html', {'home_url':home_url})
and it works, but this is really ugly and surely there is a better way. So is there a way to use reverse in class based views but avoid the cyclic dependency of urlpatterns requiring view requiring reverse requiring urlpatterns...
EDIT:
I'm using this as so:
views.py
def sidebar_activeusers(cls):
sidebar_dict = {'items' = []}
qs = models.random.filter.on.users
for user in qs:
item = {
'value': user.name,
'href': reverse('user_profile', args=[hash_id(user.id)])}
sidebar = loader.get_template('sidebar.html')
cls.base_dict['sidebar_html'] = sidebar.render(Context(sidebar_dict))
return cls
#sidebar_activeusers
class MyView1(View):
base_dict = {}
...
#other_sidebar_that_uses_same_sidebar_template
class MyView2(View):
basically I'd like to use the same sidebar template for a few different content types. Although the models displayed in the sidebar will be arbitrary the format will always be the same.
For your example MyClassView, it's ok to use reverse
class MyClassView(View):
def get(self, request):
home_url = reverse_lazy('home')
return render(request, 'home.html', {'home_url': home_url},
The reverse method is not called when the class is defined -- It is only called when the request is processed and the get method is called, so there shouldn't be an error. I have tested the example above and it works fine.
However, When you use your sidebar_activeusers decorator, the reverse call occurs before the url conf is loaded. In cases like these, you can use reverse_lazy. Here's an example of reverse_lazy in action:
from django.core.urlresolvers import reverse_lazy
class MyOtherClassView(View):
home_url = reverse_lazy('home')
def get(self, request):
return render(request, 'home.html', {'home_url':self.home_url})
This time, home_url is set when the class is defined, so reverse_lazy is required instead of reverse, because the url conf hasn't loaded yet.
Your home url can be accessed directly in your template by using declaring {% url 'home' %}. It's a standard way to access your named urls in your urls.py file.
You do not have to send the home_url variable to your template explicitly in your class-based view function.
In other words, in your home.html file:-
<p>Home</p>
will do.