Adding extra_context in Django logout built-in view - django

In django/contrib/auth/views.py there is the definition of the logout view :
def logout(request, next_page=None,
template_name='registration/logged_out.html',
redirect_field_name=REDIRECT_FIELD_NAME,
current_app=None, extra_context=None):
I would like to add extra_context to get rid of the 'Logged out' title that appear when I log off
so I'm trying this in my url confs :
(r'^accounts/logout/$', logout(extra_context={'title':'something else'}) ),
but then I get this error : logout() takes at least 1 non-keyword argument (0 given)
what I'm doing wrong?
ps:
when I do
(r'^accounts/logout/$', logout ),
it works, but then I get the 'Logged out' text...
Thanks,
Fred

When you write logout(extra_context={'title':'something else'}), you're actually calling logout right there in the URLconf, which won't work. Any URLconf tuple can have an optional third element, which should be a dictionary of extra keyword arguments to pass to the view function.
(r'^accounts/logout/$', logout, {'extra_context':{'title':'something else'}}),
Alternatively, you could write your own view which calls logout passing in whatever arguments you want -- that's typically how you would "extend" function-based generic views in more complicated cases.

Adding my findings for django 2.0 as the previous answer on this thread no longer works for the most recent django version.
With 2.0, the proper way of adding a URL to your urls.py file is by using path():
from django.urls import path
from django.contrib.auth import views as auth_views
path('accounts/logout/', auth_views.LogoutView.as_view(
extra_context={'foo':'bar'}
)),
The code snippet to highlight here is the .as_view() function. Django 2.0 implements auth views as classes. You can read more about this in the Authentication Views documentation
You then "convert" the class to a view using `.as_view() and you are able to pass in any class attributes defined in the source code as named parameters.
Passing in extra_context (which defaults to None) automatically exposes these context variables to your templates.
You can access the source code for LogoutView by following this python path: django.contrib.auth.views
Here you can see the other class attributes you can pass into LogoutView and the other auth view classes.

I had a similar problem with titles and generic views in django 1.11 (though the problem was mostly that I didn't switch docs version from 2.0). I wanted to pass title via extra_context to the view inherited from CreateView, and discovered that django's generic view had no such attribute. So, here are my crutches:
Create custom mixin (hope that's more or less what ContextMixin in 2.0 does):
class ExtraContextMixin():
extra_context = {}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(self.extra_context)
return context
Add mixin to view's ancestors (that's all code I had to change):
class CustomView(ExtraContextMixin, CreateView):
Pass extra_context from url:
url(r'^custom-view/$', views.CustomView.as_view(extra_context={'title': 'just any'}), name='custom-view')
Unfortunately, I have no idea whether such solution is acceptable (no need since 2.0, obviously), but at least it's working.

Related

How to Django reverse to pages created in Wagtail?

I have a Wagtail model for an object.
class ObjectPage(Page):
# fields, not really important
I want to make the object editable by front-end users, so I have also created a generic update view to accomplish this. My question is how I can use Django's reverse() function to point to the edited object in my get_success_url() method:
class EditObjectPage(LoginRequiredMixin, UpdateView):
# model, template_name, and fields defined; not really important
def get_success_url(self):
return("ObjectPage", kwargs={"slug" : self.object.slug}) # doesn't work
return("object_page_slug", kwargs={"slug" : self.object.slug}) # also doesn't work
I know how to do this when I'm explicitly defining the URL name in my urls.py file, but in this case, the URL is created as a Wagtail page and is handled by this line of my urls.py:
url(r"", include(wagtail_urls)),
I've been searching the documentation for whether Wagtail pages are assigned names that can be reversed to, but I'm finding nothing in the official documentation or here on StackOverflow.
The page object provides a get_url method - get_success_url should be able to accept this directly, so there's no need to use reverse.
def get_success_url(self):
return self.object.get_url()
Internally the URL route used by get_url is wagtail_serve, so if you absolutely need to go through reverse, you could replicate the logic there.

django-rest-swagger UI doesn't have form for POST request body (function based view)

I have this function-based view with django-rest-swagger decorated. However, I can't find place in UI that allow me to post the payload (request.body).
I saw a couple solutions about doing it with class-based view, but I was wondering if there is a way to do it with function-based view.
Thank you in advance!
#renderer_classes([JSONRender])
#api_view(['POST'])
def some_method(request):
body = json.loads(request.body)
return JsonResponse({'status': 'ok'})
I am gonna answer my question since the django-rest-swagger was deprecated in June 2019 and I just found out 2 feasible solutions.
First one will change the UI globally.
In ping.views (or any other location you wish) add following class.
from rest_framework.schema import AutoSchema
class CustomSchema(AutoSchema):
def __init__(self):
super(CustomSchema, self).__init__()
def get_manual_fields(self, path, method):
extra_fields = [
coreapi.Field('command', required=True, location='form', schema=String(), description='', type='', example='',
coreapi.Field('params', required=False, location='form', schema=String(), description='', type='', example='',
]
manual_fields = super().get_manual_fields(path, method)
return manual_fields + extra_fields
Add following settings in settings.py of your Django project.
REST_FRAMEWORK = {
# Corresponding path to where you added the class
'DEFAULT_SCHEMA_CLASS': 'ping.views.CustomSchema',
}
Second solution can be applied on a per-view basis. You may check here for official guide
Use #schema from rest_framework.decorators.schema to overwrite DEFAULT_SCHEMA_CLASS.
#api_view(['POST'])
#schema(CustomSchema())
def your_view(request):
print(request.body)
return JsonResponse({'task_status': 200'})
Basically, the idea is to overwrite DEFAULT_SCHEMA_CLASS. The word schema is the term that they used to refer to swagger UI for each view in rest_framework.
When you use #api_view() to decorate your function-based view, it will assign your function an attribute schema with value APIView.schema from rest_framework.views.APIView.
rest_framework.views.APIView will in further call DefaultSchema() to load the DEFAULT_SCHEMA_CLASS from your REST_FRAMEWORK configuration in settings.py.
Without other specifying, DEFAULT_SCHEMA_CLASS is rest_framework.schemas.openapi.AutoSchema by this official announcement. You might want to change it to rest_framework.schemas.coreapi.AutoSchema since it is the one that compatible with django_rest_swagger.
Hope this tutorial helps people who use django-rest-swagger (2.2.0) with function-based views for their Django project.
Please leave comments if there are anything I can help on this issue.

How to extend django admin view?

i want to display additional sidebar in my django admin index. I have created templates/admin/index.html and it show up. Now i need some data from model. To do this I have created index function in the file admin/views.py
def index(request):
var = 'var'
return render_to_response('admin/index.html', {'var': var})
Without this function I have error ViewDoesNotExist.
However template not react for this sample variable 'var'. Moreover my app doesn't display in the index. I have only auth app.
I think that I'm overwriting index function form admin view. How to properly overwrite this function?
Instead of overwriting the view entirely, you can add logic to the views in your ModelAdmin (admin.py) class:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#other-methods
so for example:
class MyAdmin(admin.ModelAdmin)
...
def add_view(self, request, form_url='', extra_context=None):
# Do some extra queries that will get passed to the template
c = {'x':SomeModel.objects.all()}
super(MyAdmin, self).add_view(request, extra_context=c)
Consider using django admin tools https://bitbucket.org/izi/django-admin-tools/wiki/Home
then you get commands like manage.py customdashboard, manage.py custommenu etc.
It even has a nice bookmark-functionality to quickliy jump to certain objects or list pages.

Django: How do I use a different form_class with django-registration

I want django-registration (version 0.8) to use my custom form rather than the default one. However, I want to continue to use the default django-registration view. What should the rest of the line below look like to achieve this?
(r'^accounts/register'...),
I've tried this below but get a syntax error:
(r'^accounts/register/$',
'registration.views.register',
{'form_class': 'MyRegistrationForm'}, name='registration_register'),
And when I try this one below I get register() takes at least 2 non-keyword arguments (1 given)
(r'^accounts/register/$',
'registration.views.register',
{'form_class':'MyRegistrationForm'}),
Looking at the views.register function,
def register(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None):
you can see that backend is a required argument. Try the following:
url(r'^accounts/register/$',
'registration.views.register',
{'form_class': MyRegistrationForm,
'backend':'registration.backends.default.DefaultBackend'},
name='registration_register'),
note that you need to use url(r'^...) if you wish to name your url.

CRUD pattern for urls.py, passing object from URI to view

Can the object value be looked up and passed to the generic view? Would the generic views need to be moved to views.py in order to support the object lookup?
urls.py
urlpatterns = patterns('myapp.views',
url(r'^mymodel/create/$', view='mymodel_create', name='mymodel_create'),
)
urlpatterns += patterns('django.views.generic',
url(r'^(?P<model>\w+)/create/$','create_update.create_object', name='object_create'),
url(r'^(?P<model>\w+)/(?P<id>\d+)/update/$', 'create_update.update_object', name='object_update' ),
url(r'^(?P<model>\w+)/(?P<id>\d+)/delete/$', 'create_update.delete_object', name='object_delete'), url(r'^(?P<model>\w+)/(?P<object_id>\d+)/$', 'list_detail.object_detail', name='object_detail'),
url(r'^(?P<model>\w+)/$','list_detail.object_list', name='object_list'),'
)
Example URL's:
http://localhost/myapp/thing/create
http://localhost/myapp/thing/1
http://localhost/myapp/thing/1/update
http://localhost/myapp/thing/1/delete
I'd like to use (?P\w+) to find the corresponding Model, 'Thing', but when this code is executed, the following error occurs: object_list() got an unexpected keyword argument 'model'
Yes, you can do this, and I've done it. The Django generic views don't support it directly. You need to wrap the generic view with your own view that looks up the model and constructs the queryset. Here's an example (showing only the detail view, you can do the others similarly). I've changed your named patterns in the URLconf to use "model" instead of "object", which is clearer naming:
in urls.py:
url(r'^(?P<model>\w+)/(?P<object_id>\d+)/$', 'my_app.views.my_object_detail', name='object_detail'),
in my_app/views.py
from django.views.generic.list_detail import object_detail
from django.db.models.loading import get_model
from django.http import Http404
def get_model_or_404(app, model):
model = get_model(app, model)
if model is None:
raise Http404
return model
def my_object_detail(request, model, object_id):
model_class = get_model_or_404('my_app', model)
queryset = model_class.objects.all()
return object_detail(request, queryset, object_id=object_id)
If you're clever and want to keep things as DRY as possible, you could create a single wrapper that accepts the generic view function to call as one of its arguments, rather than having a wrapper for create, a wrapper for update, etc.
I think you might be a bit confused about what the generic views are designed to do. If you go and read django.views.generic.list_detail, for instance, you will see that neither object_list nor object_detail accept an "object" argument, but both require a "queryset" argument. You don't need to "find the corresponding Model" - both generic views are simply common ways to render the provided queryset (in the case of object_detail, after being filtered by either object_id or slug and slug_field). I'm not entirely sure what you're trying to do but I don't think these generic views are what you're looking for.