Django custom AdminSite and ModelAdmin - python

I have overridden standard AdminSite with my own one:
project/admin/base.py:
from django.contrib import admin
class MyAdmin(admin.AdminSite):
...
my_admin = MyAdmin()
and registered some application's model with it:
fact/admin.py:
from django.contrib import admin
from project.admin.base import my_admin
from models import Fact
class FactAdmin(admin.ModelAdmin):
fields = ('section', 'description')
my_admin.register(Fact, FactAdmin)
How can I get this model displayed on an index page? I can't use autodiscover as stated here: How to use custom AdminSite class?
The workaround suggested in that question also doesn't work for me and I'd like to make it it a more clean way.
The documentation only states that
There is really no need to use autodiscover when using your own AdminSite instance since you will likely be importing all the per-app admin.py modules in your myproject.admin module.
I don't import per app modules in project.admin module though.
So is there any way to tell custom AdminSite about the registered model and show it on the index?
Edit: I have hooked URLs and I see my_admin, not admin. With standard admin everything works correctly. Here is the code of project/urls.py:
from django.conf.urls import patterns, include, url
from admin.base import my_admin
urlpatterns = patterns('',
...
url(r'^admin/', include(my_admin.urls, app_name='admin')),
)

I think you need to do:
from django.contrib import admin
class MyAdmin(admin.AdminSite):
...
admin.site = MyAdmin()

Related

Facing troubles on bringing the Django admin user tools like welcome, view site, log out on the custom template

I have to override the blocks like branding,site_title, and index title to a custom template. But the user tools are not displaying.
How I get.
How I want.
You can update the Site Header from your main urls.py. Just add in:
from django.contrib import admin
....
admin.site.site_header = "the title you want"
If you want to customise the admin site alot, you will likely want to subclass/override the default admin site. The documentation has most the info you need.
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#customizing-the-adminsite-class
If you’d like to set up your own admin site with custom behavior, you’re free to subclass AdminSite and override or add anything you like. Then, create an instance of your AdminSite subclass (the same way you’d instantiate any other Python class) and register your models and ModelAdmin subclasses with it instead of with the default site. Finally, update myproject/urls.py to reference your AdminSite subclass.
app/admin.py
from django.contrib.admin import AdminSite
from .models import MyModel
class MyAdminSite(AdminSite):
site_header = 'Monty Python administration'
admin_site = MyAdminSite(name='myadmin')
admin_site.register(MyModel)
project/urls.py
from django.urls import path
from myapp.admin import admin_site
urlpatterns = [
path('myadmin/', admin_site.urls),
]

What is the equivalent of from django.views.generic.simple import direct_to_template in django 1.9

I want to make my home page as index.html which is located inside the template directory named as templates/castle_tm/index.html, but the url shows
"no module named simple".
Generic based views are deprecated in django >1.4. Now, How can i redirect the home page to index.html
urls.py
from django.conf.urls import url, patterns, include
from django.conf.urls.static import static
from django.conf import settings
from django.contrib import admin
from castle import views
from django.views.generic.simple import direct_to_template
admin.autodiscover()
url(r'^api/casinova$', direct_to_template,{"template":"castle_tm/index.html"}),
In latest versions of django you can use TemplateView
from django.views.generic import TemplateView
...
url(r'^api/casinova$', TemplateView.as_view(template_name='castle_tm/index.html')),
I believe you're looking for a TemplateView
from django.views.generic import TemplateView
url(r'^api/casinova$', TemplateView.as_view(template_name="castle_tm/index.html")),
Generic based views have been replaced by class based generic views which allows you to override them easily to provide extra context data and reduce code repetition
For more information, Russell Keith-Magee did a very good presentation at djangocon a couple of years ago, you can watch it here - Class-based Views: Past, Present and Future
A good solution is handling robots.txt via nginx:
location /robots.txt {
alias /path/to/static/robots.txt;
}

admin.py for project, not app

How can I specify a project level admin.py?
I asked this question some time ago and was just awarded the Tumbleweed award because of the lack of activity on the question! >_<
Project:
settings.py
admin.py (This is what I am trying to get to work)
...
App
admin.py (I know how to do this)
For example, admin.autodiscover() is typically put in the project level urls.py (yes, it will be automatically included in 1.7)
I would like to move this, and the following, into their own admin.py file:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
UserAdmin.list_display = ('email', 'first_name', 'last_name', 'is_active')
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
admin.autodiscover()
I tried doing just that, making an admin.py file and adding the code into it. Didn't work.
I tried adding a project level folder called admin, added an __init__.py and threw admin.py into the admin folder. Didn't work
Further I tried adding this admin folder to INSTALLED_APPS in settings.py. No luck.
From the admin.autodiscover doc string:
Auto-discover INSTALLED_APPS admin.py modules and fail silently when
not present. This forces an import on them to register any admin bits they
may want.
And here is the complete function very well documented:
def autodiscover():
"""
Auto-discover INSTALLED_APPS admin.py modules and fail silently when
not present. This forces an import on them to register any admin bits they
may want.
"""
import copy
from django.conf import settings
from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule
for app in settings.INSTALLED_APPS:
mod = import_module(app)
# Attempt to import the app's admin module.
try:
before_import_registry = copy.copy(site._registry)
import_module('%s.admin' % app)
except:
# Reset the model registry to the state before the last import as
# this import will have to reoccur on the next request and this
# could raise NotRegistered and AlreadyRegistered exceptions
# (see #8245).
site._registry = before_import_registry
# Decide whether to bubble up this error. If the app just
# doesn't have an admin module, we can ignore the error
# attempting to import it, otherwise we want it to bubble up.
if module_has_submodule(mod, 'admin'):
raise
So the only thing autodiscover does for you is look for some module called admin.py inside installed app directories, hence you can put your admin.py where you want just make sure you import it, this make the code in it (registration of models ect..) get executed.
IMPORTANT: I'm not sure the correct moment for importing your custom-path admin.py. But it's sure you have to import it after load all the related apps.
All admin.autodiscover() is import all the admin files it finds. There is no point putting the line itself in an admin files.
To understand why it's not working, you need to realise how it works. What it does is import the admin.py files inside registered apps. A registered app is a package included in INSTALLED_APPS that has a models.py. That's worth repeating: an app isn't considered if it doesn't have a models.py - even a blank one will work, but without it the package isn't an app.
So if you create a blank models.py in your directory, ensure that that directory is in INSTALLED_APPS, and move admin.autodisover back to urls.py, everything will work.
Edit
I'm not exactly sure what you do want, and why. As I mentioned, autodiscover depends on apps; but as I also mentioned, all autodiscover does is import all the admin files. If you really don't want to follow best practices, all you need to do is import your admin file manually: you can do that in the same place that you normally call autodiscover from, ie in the main urls.py.
Sooner or later, a heavily customized admin class will most likely need a project wide admin.py, as it is the place to register all per-app admins that are not being auto-discovered anymore. The documentation mentions this, but there is no extensive example to illustrate it:
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#customizing-the-adminsite-class
Note that you may not want autodiscovery of admin modules when using your own AdminSite instance since you will likely be importing all the per-app admin modules in your myproject.admin module. This means you need to put 'django.contrib.admin.apps.SimpleAdminConfig' instead of 'django.contrib.admin' in your INSTALLED_APPS setting.
There is a bit outdated but more or less valid example in the wiki as well for multiple admin sites:
https://code.djangoproject.com/wiki/NewformsAdminBranch
# project-level admin.py
from django.contrib import admin
from myproject.myapp.models import Book, Author
from myproject.anotherapp.models import Musician, Instrument
site1 = admin.AdminSite()
site1.register(Book)
site1.register(Author)
site2 = admin.AdminSite()
site2.register(Musician)
site2.register(Instrument)
I'm curious to see if this gets downvoted. It seems to work, but it feels wrong. ;-)
The question is how to override the UserAdmin settings.
Put an admin.py in your project folder (alongside settings.py)
from django.contrib.auth import admin
from django.contrib.auth.admin import UserAdmin
UserAdmin.list_display = ('email', 'first_name', 'last_name',
'is_active', 'date_joined', 'last_login',
'is_staff')
UserAdmin.ordering = ['-date_joined']
add your project to installed_apps (this seems wrong, but I don't know why)
And now you should see the new columns and sort order on /admin/auth/user/
This is working for me with Django 1.11. So far no issues that I can find. What say you StackOverflow?

How to use different view for django-registration?

I have been trying to get django-registration to use the view RegistrationFormUniqueEmail and following the solution from this django-registration question. I have set my urls.py to
from django.conf.urls import patterns, include, url
from registration.forms import RegistrationFormUniqueEmail
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
(r'^users/', include('registration.backends.default.urls')),
url(r'^users/register/$', 'registration.backends.default.views.RegistrationView',
{'form_class': RegistrationFormUniqueEmail,
'backend': 'registration.backends.default.DefaultBackend'},
name='registration_register'),
)
However, I can still create multiple accounts with the same email. What is the problem? Shouldn't django-registration be using the view that I specified? I am currently using django-registration 0.9b1.
The version of Django registration you are using has been rewritten to use class based views. This means a different approach is required in your urls.py.
First, You need to subclass the RegistrationView, and set the custom form class.
from registration.backends.default.views import RegistrationView
from registration.forms import RegistrationFormUniqueEmail
class RegistrationViewUniqueEmail(RegistrationView):
form_class = RegistrationFormUniqueEmail
Then, use your custom RegistrationViewUniqueEmail subclass in your urls. As with other class based views, you must call as_view().
url(r'^user/register/$', RegistrationViewUniqueEmail.as_view(),
name='registration_register'),
Make sure your customised registration_register view comes before you include the default registration urls, otherwise it won't be used.
The version 1.2 of django-registration-redux allows the unique email option with the following urls.py patterns:
url(r'^accounts/register/$', RegistrationView.as_view(form_class=RegistrationFormUniqueEmail), name='registration_register'),
url(r'^accounts/', include('registration.backends.default.urls')),
If you need to do something more, like a specific URL option, you can subclass the RegistrationView in your app views.py and RegistrationForm in your app forms.py

Django RedirectView and reverse() doesn't work together?

I'm having this weird problem.
When I did this:
from django.core.urlresolvers import reverse
reverse('account-reco-about-you')
# returns '/accounts/recommendations/about-you/'
But when I did this:
# Doesn't Work
recommendations = login_required(RedirectView.as_view(url=reverse('account-reco-about-you')))
# Work
recommendations = login_required(RedirectView.as_view(url='/accounts/recommendations/about-you'))
Error message I get if unrelated. It says my last view is not found, which is there. Any explanation? Meantime, i'll make do with the non-reverse style.
This problem is to do with trying to reverse something at import time before the URLs are ready to be reversed. This is not a problem with RedirectView itself - it would happen with anything where you tried to reverse in your urls.py file, or possibly in a file imported by it.
In the development version of Django, there is a function called reverse_lazy specifically to help in this situation.
If you're using an earlier version of Django, there is a solution here: Reverse Django generic view, post_save_redirect; error 'included urlconf doesnt have any patterns'.
You need to use "reverse_lazy" that is defined in "django.core.urlresolvers" in Django 1.4 and above.
Here is an example urls.py:
from django.conf.urls import patterns, include, url
from django.views.generic import RedirectView
from django.core.urlresolvers import reverse_lazy
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('apps.website.views',
url(r'^$', 'home', name='website_home'),
url(r'^redirect-home/$', RedirectView.as_view(url=reverse_lazy('website_home')),
name='redirect_home'),
)
So in the above example, the url "/redirect-home" will redirect to "/". Hope this helps.
no need for reverse() or reverse_lazy().
simply specify the pattern_name parameter:
RedirectView.as_view(pattern_name='account-reco-about-you')
#wtower
pattern_name will be ok, but you may need to add namespace as below.
RedirectView.as_view(pattern_name='polls:index')

Categories