How to enable django admin sidebar navigation in a custom view? - python

I have a view inheriting from LoginRequiredMixin, TemplateView, which renders some data using the admin/base_site.html template as the base. I treat it as a part of the admin interface, so it requires an administrator login. I'd like to make this view a little bit more a part of the Django admin interface by enabling the standard sidebar navigation on the left-hand side.
Note that I don't have a custom ModelAdmin definition anywhere, I simply render the template at some predefined URL. There are no models used in the interface either, it parses and displays data from database-unrelated sources.
Currently, I just build the required context data manually, e.g.:
data = super().get_context_data(**kwargs)
data.update(**{
"is_popup": False,
"site_header": None,
"is_nav_sidebar_enabled": True,
"has_permission": True,
"title": "My title",
"subtitle": None,
"site_url": None,
"available_apps": []
})
The sidebar is visible, but displays an error message:
Adding an app to available_apps ("available_apps": ["my_app"]) doesn't help either:
So my question is - how do I do that? Is there a class I can inherit from to achieve this behaviour? Or a method I can call to get all required context data for base_site.html? Or perhaps I should insert some information in my template? Perhaps I need an AdminSite object, or can somehow call methods of the default one?

By accident I noticed there is a DefaultAdminSite object in django.contrib.admin.sites, and it's instantiated as site. Therefore, in my case, simply using site is sufficient.
from django.contrib.admin import AdminSite
from django.contrib.admin.sites import site
admin_site: AdminSite = site
data.update(**admin_site.each_context(self.request))
Furthermore, turns out I can just use the apps in case importing site would be an issue, just like DefaultAdminSite does it:
AdminSiteClass = import_string(apps.get_app_config("admin").default_site)
self._wrapped = AdminSiteClass()

The app_list.html template which is included in your base template requires a context variable available_apps with a structure as described here.
As you probably won't be able to make this include work properly with your app you could also override the {% block nav-sidebar %} in your base template and provide some HTML that fits your use-case.
But after all it's probably not such a good idea to reuse templates which are made for a different app (admin) with your app because it's difficult to predict how they will behave, what their requirements are and beyond this maintainance in the long term will also be a problem as they might change with future Django versions.

You have to include django admin's default context variables used in the template. This can be done by calling each_context function:
data.update(self.admin_site.each_context(request))
I also have custom admin views and templates in my projects and this works well for me.

Related

How to display local-memory cache in Django Templates?

I set the local-memory cache "message" as shown below:
from django.core.cache import cache
cache.set("message", "success", 300)
Then, I tried to display it in the Django Template "index.html" as shown below:
# "index.html"
{{ cache.get.message }}
But, "success" is not displayed.
So, are there any ways to display the local-memory cache in Django Templates?
Django's template system is not simply Python embedded into HTML. It has a few wrappers built-in for looping, conditionally rendering, etc.
From Django Docs:
If you have a background in programming, or if you’re used to languages which mix programming code directly into HTML, you’ll want to bear in mind that the Django template system is not simply Python embedded into HTML. This is by design: the template system is meant to express presentation, not program logic.
In order to get a value from the cache you need to run cache.get('key_name') which is not allowed from within a template.
You can retrieve the value you want to display in the view and pass it to its template directly.

Too many templates in Django

I am learning Django and I programming a CRUD for various objects. Currently I need to generate three templates per object: one for creation, one for update and one for a list. Look at how many templates I got in the picture.
The process is becoming painful.
What I am doing wrong and how to correct it?
Thanks.
From your comment:
The problem is that If I need to make a change, for example, add a new button, I have to make it in all the templates.
That sounds wrong. Either a button belong to one type of thing and then it should only exist in that template, or it should be on every page and then it should be in the base template.
You can make one template with your basic page layout (including such buttons), and have "blocks" in them that are then filled out by each template that extends the base template.
See the Django documentation about template inheritance.
Also, I think that in most cases the "create" and "update" pages are going to be extremely similar; usually they use the same template, one with data already filled in, the other without.
You could have a base template, then a generic list template and a generic edit/create template, with templates for each type of thing extending the list and the edit templates to fill in only the relevant fields.
Also, maybe you use HTML that is close enough to what the Django forms can render themselves. Then as long as you call the edit/create form the same in each of your views (like "form"), the template can just render the form and they can all use the same template.
If you're building a big web-app then this is normal for templates to be more.
Heavy (big) web-app means = more templates + more code + more time + more features

Add custom Django admin action

I'm looking for ways of adding custom actions to a Django admin change page. I am not looking for actions that can be added to the dropdown box in the overview.
One model in my Django application contains information about documents, and those are automatically compiled to PDF files at the frontend. I'd like to give the administrator the possibility to quickly render the PDF directly from the change page to be able to quickly check the results.
I've already played around with overriding change_form.html/submit_line.html, and it's absolutely no problem to add a button. But I wonder how to extend the admin module in a clean way so that it includes my custom action.
Since custom admin views are basically just normal views there aren't many specialities. A few things you should consider for a cleaner integration:
Add the url pattern through get_urls() of the AdminSite.
Provide the current_app argument to RequestContext or Context when rendering a template which subclasses admin templates.
EDIT:
Found a an example here:
http://shrenikp.webs.com/apps/blog/show/5211522-add-custom-view-method-for-django-admin-model-
Note, that the example doesn't utilize the current_app argument i've mentioned. I suppose your view to generate the PDF just returns a HttpResponse with the appropriate content type rather than rendering a response with a Context, so it's not needed. All in all the current_app only makes sense when you subclass an admin template used by your custom view which actually makes use of current_app somewhere.
The example encapsulates the urls and view in a ModelAdmin. It is also possible to do this through a AdminSite subclass, but at least in your use case this is probably overkill.
By the way, overriding the change_form.html template for your app to add a button to the standard change view is just fine. There is no special api for this in the admin (unfortunately).

Where is a good place to work on accounts/profile in Django with the Django registration app?

I've noticed that after I log in with Django registration it redirects me to accounts/profile/. By default Django registration's url.py doesn't handle accounts/profile/, so I need to create my own.
Actually this questions is three-fold:
Why does after logging in, it redirects to accounts/profile/? Is there a way to change that? Preferably after successfully logging in I would like Django to redirect back to the page before the login page.
If I were to create my own view and template for accounts/profile/, then where should I put it? Django's built-in users (auth_user) is shared among all Django apps inside a project, so should I place the view.py in the project folder and not inside the app folder?
Or does Django profile actually takes care of this whole account/profiles/ thing? I already extended Django's User class with my own UserProfile, but it's more like additional fields to the User table than an actual "profile" (I didn't create avatars or anything like that, just simple stuff like addresses and phone numbers, but most importantly, some custom user types that my app depends on).
Why does after logging in, it redirects to accounts/profile/? Is there
a way to change that? Preferably after successfully logging in I would
like Django to redirect back to the page before the login page.
Just change setting LOGIN_REDIRECT_URL
If I were to create my own view and template for accounts/profile/,
then where should I put it? Django's built-in users (auth_user) is
shared among all Django apps inside a project, so should I place the
view.py in the project folder and not inside the app folder?
I like to create an app called "project_specific" in every project. That's where I put all the stuff that's not meant to be reusable and that couples many apps.
You can also create a views.py at the project level, but that is sort of messy compared to making a project specific app.
In reality it does not matter where you put it.
Or does Django profile actually takes care of this whole
account/profiles/ thing? I already extended Django's User class with
my own UserProfile, but it's more like additional fields to the User
table than an actual "profile" (I didn't create avatars or anything
like that, just simple stuff like addresses and phone numbers, but
most importantly, some custom user types that my app depends on).
That's not the way to add extra user fields. I recommend that you read the docs on Storing additional information about users.
For a minimal approach that doesn't require a standalone app,
Create a template and call it profile.html or anything you want.
<p>This is your profile, {{ user.username }}.</p>
In urls.py, add a url pattern that points to your profile template, mark it login_required, and give the url a name:
# ...
from django.views.generic import TemplateView
from django.contrib.auth.decorators import login_required
urlpatterns = [
# ...
url(r'^accounts/profile/$', login_required(TemplateView.as_view(template_name='profile.html')), name='user_profile'),
# ...
]
In settings.py, add the following line:
LOGIN_REDIRECT_URL = 'user_profile'
This line tells Django to perform a reverse URL lookup by name when redirecting a user after a login. Without this line, your app will still work but it will be fragile because it relies on an arbitrary hard-coded URL that is implicitly configured by Django. With this line, if you or someone else decides that user profiles should be at /me/, you could change the URL in step 2 without breaking your app.
Set LOGIN_REDIRECT_URL in settings - https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url
Create account app, where contains code for this.
You may use django userena for full-stack user area: https://django-userena.readthedocs.org/en/latest/

Basic MVT issue in Django

I have a Django website as follows:
site has several views
each view has its own template to show its data
each template extends a base template
base template is the base of the site, has all the JS/CSS and the basic layout
So up until now it's all good. So now we have the master head of the site (which exists in the base template), and it is common to all the views.
But now I want to make it dynamic, and add some dynamic data to it. On which view do I do this? All my views are basically render_to_response('viewtemplate.html', someContext). So how do add a common view to a base template?
Obviously I will not duplicate the common code to each separate view...
I think I'm missing something fundamental in the MVT basis of Django.
You want to use context_instance and RequestContexts.
First, add at the top of your views.py:
from django.template import RequestContext
Then, update all of your views to look like:
def someview(request, ...)
...
return render_to_response('viewtemplate.html', someContext, context_instance=RequestContext(request))
In your settings.py, add:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
...
'myproj.app.context_processors.dynamic',
'myproj.app.context_processors.sidebar',
'myproj.app.context_processors.etc',
)
Each of these context_processors is a function takes the request object and returns a context in the form of a dictionary. Just put all the functions in context_processors.py inside the appropriate app. For example, a blog might have a sidebar with a list of recent entries and comments. context_processors.py would just define:
def sidebar(request):
recent_entry_list = Entry.objects...
recent_comment_list = Comment.objects...
return {'recent_entry_list': recent_entry_list, 'recent_comment_list': recent_comment_list}
You can add as many or as few as you like.
For more, check out the Django Template Docs.
Context processors and RequestContext (see Tyler's answer) are the way to go for data that is used on every page load. For data that you may need on various views, but not all (especially data that isn't really related to the primary purpose of the view, but appears in something like a navigation sidebar), it often makes most sense to define a custom template tag for retrieving the data.
or use a generic view, because they are automatically passed the request context.
a simple direct to template generic view can be used to avoid having to import/pass in the request context.

Categories

Resources