Add context to every Django Admin page - python

How do I add extra context to all admin webpages?
I use default Django Admin for my admin part of a site.
Here is an url entry for admin:
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
And my apps register their standard view models using:
admin.site.register(Tag, TagAdmin)
My problem, is that I want to display an extra field in admin template header bar and I have no idea how to add this extra context.
My first bet was adding it in url patterns like below:
urlpatterns = [
url(r'^admin/', admin.site.urls, {'mycontext': '123'}),
]
But that gives an error:
TypeError at /admin/tickets/event/4/change/
change_view() got an unexpected keyword argument 'mycontext'
Can you give any suggestion? I really do not want to modify every AdminModel class I have to insert this context, as I need it on every admin page.
Thanks.

Found the solution, url registration has to be:
urlpatterns = [
url(r'^admin/', admin.site.urls, {'extra_context': {'mycontext': '123'}}),
]
Its a context dictionary inside of a dictionary with 'extra_context' as a key.

Another technique, more complex but allows different context per request (probably unavailable at OP time):
my_project/admin.py (create if missing)
from django.contrib import admin
from django.contrib.admin.apps import AdminConfig
class MyAdminConfig(AdminConfig):
default_site = 'my_project.admin.MyAdminSite'
class MyAdminSite(admin.AdminSite):
def each_context(self, request):
context = super().each_context(request)
context.update({
"whatever": "this is",
"just a": "dict",
})
return context
settings.py
INSTALLED_APPS = [
...
'my_project.admin.MyAdminConfig', # replaces 'django.contrib.admin'
...
The replace / extend admin class code is taken from the official docs except this is all in one file.

Related

Django,DRF, get another app's view name for a reverse

I'm trying to create a link for another app in my serializer using the solution provided here:
https://stackoverflow.com/a/45850334/12177026
I'm trying to match the view's name but every way I try I get this error:
Reverse for 'KnownLocationView' not found. 'KnownLocationView' is not a valid view function or pattern name.
serializers:
class MissionSerializer(HyperlinkedModelSerializer):
gdt = ChoiceField(choices=lazy(get_locations, tuple)())
location = SerializerMethodField(label='Open Location')
def get_location(self, obj):
request = self.context.get('request')
return request.build_absolute_uri(reverse('KnownLocationView', kwargs={'Name': obj.gdt}))
class Meta:
model = Mission
fields = ('MissionName', 'UavLatitude', 'UavLongitude', 'UavElevation', 'Area',
'gdt', 'location')
KnownLoction/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import KnownLocationView
app_name = 'KnownLocation'
router = DefaultRouter()
router.register(r'knownlocations', KnownLocationView)
urlpatterns = [
path('', include(router.urls)),
]
I tried replacing view_name with either of the following:
'knownlocations'
'KnownLocation:knownlocations'
'KnownLocation:KnownLocationView'
But get the same error
even tried to reorder the installed apps.
api/urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
path('', include('landingpage.urls')), # API Landing Page
path('', include('ThreeLocations.urls')), # Triangulation between two Known GDTs and uav location.
path('', include('SecondGDT.urls')), # Options For Second GDT Positioning.
path('', include('KnownLocation.urls', namespace='knownlocations')),
# Add Known Location To the map.
] + staticfiles_urlpatterns()
KnownLocation/views.py
from rest_framework.renderers import AdminRenderer
from rest_framework.viewsets import ModelViewSet
from .models import KnownLocation
from .serializers import KnownLocationSerializer
class KnownLocationView(ModelViewSet):
"""
List of Known Locations In which we can place one of our ground positions.
press create to add a new location.
"""
serializer_class = KnownLocationSerializer
queryset = KnownLocation.objects.all()
def get_serializer_context(self):
data = KnownLocationSerializer(self.queryset, many=True, context={'request': self.request})
return data
renderer_classes = [AdminRenderer]
I think you should try to change how you are calling the reverse.
You can find reference to it below
Note: If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters on the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view.
The automatic view_name generation uses a pattern like %(model_name)-detail. Unless your models names actually clash you may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.
Try using this
reverse('KnownLocationView:mission-detail, kwargs={'Name': obj.gdt})
Or
reverse('mission-detail, kwargs={'Name': obj.gdt})

Password_reset_done error when attempting to create reset password authentication

I want to allow the user to reset password when the user is signed out and cannot remember the password.
I am using the django authentication framework and have created the reset_password and password_reset_done mappers.
Issue : Though I have created the password_reset_done function I continue to get the below error. Is there a step that I missed that is causing this error? I do not know what I have done wrong.
I have posted all the code that I think is relevant to what I attempting to do.
Edit with full TraceBack:
Here is the code :
relative urls.py
from django.conf.urls import url
from . import views
from django.contrib.auth.views import login, logout, password_reset, password_reset_done
urlpatterns = [
url(r'^$', views.vedic_view, name = 'vedic_home_view'),
url(r'^login/$', login, {'template_name' : 'exist/login.html'}, name = 'login'),
url(r'^logout/$', logout, {'template_name' : 'exist/logout.html'}, name = 'logout'),
url(r'^register/$', views.register_view, name = 'register'),
url(r'^profile/$', views.view_profile, name = 'view_profile'),
url(r'^profile/edit/$', views.edit_profile, name = 'edit_profile'),
url(r'^change-password/$', views.change_password, name = 'change_password'),
url(r'^reset-password/$', password_reset, name = 'reset_password'),
url(r'^reset-password/done/$', password_reset_done, name = 'password_reset_done')
]
main urls.py
from django.conf.urls import url
from django.contrib import admin
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
url(r'^admin/', admin.site.urls),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
from django.conf.urls import include
from django.views.generic import RedirectView
urlpatterns += [
url(r'^exist/', include('exist.urls', namespace = 'exist')),
url(r'^$', RedirectView.as_view(url='/exist/', permanent=True)),
]
password_reset_done is within the exists namespace. It looks like you are trying to reverse the named URL without including the namespace argument somewhere. We would need to see your full traceback to see exactly where that is happening.
Since you are using the built-in auth views, the easiest fix would probably be to move your password reset handling up to your main urls.py. Notice in your traceback that the built-in password_reset view does this:
post_reset_redirect = reverse('password_reset_done')
The default implementation here is to reverse to password_reset_done without any namespace. Moving the relevant URLs up to your main urls.py will allow them to be accessed via reverse without a namespace argument, making the built-in views happy.
I realize what the issue is.
For the built-in reset view there is a post_reset_redirect variable that uses the default implementation reverse(password_reset_done) to go to the password_reset_done view.
The issue is that in my main urls.py document I created the namespace variable
namespace = exist
However I did not override the default post_reset_redirect implementation
from reverse(password_reset_done) to reverse(exist:password_reset_done).
So my current
url(r'^reset-password/$', password_reset, name = 'reset_password'),
should now look like
url(r'^reset-password/$', { 'post_reset_redirect':
'exist:password_reset_done'}, password_reset, name =
'reset_password')
As I see, you didn't include relative.url in your main urls.
EDIT
in relative urls
app_name='exists' #with this name you can call in main urls
urlpatterns = [
url(r'^$', views.vedic_view, name = 'vedic_home_view'),
#...
In main url:
urlpatterns = [
url(r'^exists/', include('exists.urls')),
#...
EDIT 2
Here'sthe docs on the subject, it explains it better than me with examples.

Django Write Authentication Redirect for Blackboxed Route URLs

I'm using a django library called django-dashing that has a predefined set of urls that render a dashboard. I import them like this
urlpatterns = [
...
url(r'^dashboard/', include(dashing_router.urls)),
...
]
I want the router to be accessible only by administrators, which I can do with some config settings within django-dashing. However, when a non-admin user attempts to access /dashboard/, I want to redirect them to django's /admin/ panel to have them log in, instead of throwing the 403 that django-dashing does.
Since the django-dashing views are effectively blackboxed, I was wondering if there was a way to write a 'pre-view' that would intercept the request to /dashboard/, run some code – specifically, doing the appropriate redirects – and then continue onto the actual dashboard.
I know this would be easy enough to do by writing two urls, like /dashboard-auth/ which redirects to /dashboard/, but I don't want the user to have to go to one URL to get to another
Any suggestions?
A Django simple custom middleware is another option...
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
class DashingRedirectMiddleware(object):
def process_request(self, request):
if request.path.startswith('/dashing/') and not request.user.is_staff:
return HttpResponseRedirect(reverse('admin:login'))
return
Don't forget to add this middleware to your DJANGO SETTINGS...
MIDDLEWARE_CLASSES = [
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'yourapp.middleware.DashingRedirectMiddleware',
...
]
Or something like that.
The way I would do it is by overridding dashing's default router. All of the urls are generated dynamically by the Router class, so by overriding the get_urls method, you can wrap each function in the staff_member_required decorator.
from django.contrib.admin.views.decorators import staff_member_required
from django.conf.urls import url
from dashing.utils import Router
from dashing.views import Dashboard
class AdminRequiredRouter(Router):
def get_urls(self):
urlpatterns = [
url(r'^$', staff_member_required(Dashboard.as_view()), name='dashboard'),
]
for widget, basename, parameters in self.registry:
urlpatterns += [
url(r'/'.join((
r'^widgets/{}'.format(basename),
r'/'.join((r'(?P<{}>{})'.format(parameter, regex)
for parameter, regex in parameters.items())),
)),
staff_member_required(widget.as_view()),
name='widget_{}'.format(basename)),
]
return urlpatterns
router = AdminRequiredRouter()
Then include your router instead of dashing's
from .router import router
urlpatterns = [
...
url(r'^dashboard/', include(router.urls)),
...
]
If you are willing to look inside the 'black box' of the dashing urls, you can see that the /dashboard/ is handled by the Dashboard view. You could subclass this view, and catch the PermissionDenied error.
from django.core.exceptions import PermissionDenied
from dashing.views import Dashboard
class MyDashboard(Dashboard):
def get(self, request, *args, **kwargs):
try:
return super(MyDashboard, self).get(request, *args, **kwargs)
except PermissionDenied:
return redirect('/admin/')
Then add your view above the dashing urls:
urlpatterns = [
...
url(r'^dashboard/$', MyDashboard.as_view())
url(r'^dashboard/', include(dashing_router.urls)),
...
]

Exclude URLs from Django REST Swagger

I have a few URLs that I want to exclude from my REST API documentation. I'm using Django REST Swagger and the only documentation I can find (https://github.com/marcgibbons/django-rest-swagger) doesn't really tell me much. There is the "exclude_namespaces" part of SWAGGER_SETTINGS in settings.py, but there is no real explanation or example of how to use this.
Simply put, I want to exclude any URLs from the docs that start with the following:
/api/jobs/status/
/api/jobs/parameters/
How could I go about doing this?
Thanks in advance for any help offered :P
the namespaces to exclude are the one defined in your urls.py.
So for example, in your case:
urls.py:
internal_apis = patterns('',
url(r'^/api/jobs/status/',...),
url(r'^/api/jobs/parameters/',...),
)
urlpatterns = urlpatterns + patterns('',
url(r'^', include(internal_apis, namespace="internal_apis")),
...
)
and in your settings.py:
SWAGGER_SETTINGS = {
"exclude_namespaces": ["internal_apis"], # List URL namespaces to ignore
}
This is well described in there
For all of those who found the above answer not helpful:
I guess that "exclude_namespaces" doesn't work anymore in new versions of django swagger. I had almost the same problem (I didnt't want to show my internal apis in documentation) and the above solution didn't work for me. I've been searching for like an hour for a solution and finally found something helpful.
There are some attributes that you can pass to SchemaGenerator. One of them is urlconf. You can set it to be "yourproject.api.urls" and it will get only urls defined there! Of course, you have to make sure that all the urls that you want to exclude from your api documentation are not included there.
I hope that at least one person found my answer helpful ;).
A problem comes when you want to have many urls.py included in your api documentation. I don't know what should be done then. If anyone comes up with an answer to this new problem - feel free to comment my answer. thanks!
With new version of django swagger, we don't need to create view to exclude some urls. Below code will disable test2 url.
from rest_framework_swagger.views import get_swagger_view
urlpatterns1 = [
url(r'^', include(router.urls)),
url(r'^test/', include('test.urls')),
url(r'^test1/', Test2.as_view()),
]
schema_view = get_swagger_view(title='API Documentation', patterns=urlpatterns1)
urlpatterns = urlpatterns1 + [
url(r'^docs/', schema_view),
url(r'^test2/', Test2.as_view()),
]
Ola's answer is correct. exclude_namespaces is no longer supported.
For finer control of the documentation, create your own schema view by using a function-based or class-based view. This can be useful if you want to produce documentation for specific URL patterns, or URL confs.
In your views.py, you can do the following:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.schemas import SchemaGenerator
from rest_framework_swagger import renderers
class SwaggerSchemaView(APIView):
renderer_classes = [
renderers.OpenAPIRenderer,
renderers.SwaggerUIRenderer
]
def get(self, request):
generator = SchemaGenerator(title='Your API Documentation', urlconf='your_app.urls')
schema = generator.get_schema(request=request)
return Response(schema)
The above will only render documentation for the URLs that are specified in the urlconf argument of the SchemaGenerator. Also, don't forget to set up your urls.py as well:
from django.conf.urls import url
from views import SwaggerSchemaView
urlpatterns = [
url(r'^api/v1/docs/$', SwaggerSchemaView.as_view(), name='docs'),
]
For the newest version of drf-swagger you can defile url patterns in the schema generator.
For example:
url_patterns = (
url(r'^api/v1/', include(router.urls, namespace='api')),
)
generator = schemas.SchemaGenerator(title='Core API', patterns=url_patterns)
A more flexible solution would be:
from django.contrib import admin
from django.urls import include, path
from rest_framework_swagger.views import get_swagger_view
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('user.urls', namespace="user")),
path('locations/', include('location.urls')),
path('departments/', include('department.urls', namespace="department")),
path('my_secret_api/', include('secret.urls', namespace="secret_api")),
]
to_exclude = ['secret_api',] # some more namespaces here
swagger_urls = [item for item in urlpatterns if hasattr(item,"namespace") and item.namespace not in to_exclude]
schema_view = get_swagger_view(title='Highky', patterns=swagger_urls)
urlpatterns += [
path('api/docs/', schema_view),
]
urlpatterns will have all five paths, but swagger_urls will have four paths excluding secret_api.
All of your URLs and includes will continue to work as they were, except we are now passing our modified urlpatterns that we want to show in the Swagger docs. The checks will also cover the include where you don't specify a namespace (like in our case, where the namespace is not defined in the location).
views.py
any view class
class ...ViewSet(viewsets.ModelViewSet):
queryset = ....objects.all().order_by('-id')
serializer_class = ...Serializer
http_method_names = ['get', 'post', 'patch', 'delete'] # add or exclude
any function-based view
#api_view(['get']) # target field
def function(request):
...
return Response(...)

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

Categories