I want to pass the previous URL in a context variable for a generic view:
class PostDeleteView(DeleteView, LoginRequiredMixin):
previous_url = self.request.META.get('HTTP_REFERER')
...
However, I can't access either self or request. How do I go about this?
For example, if you wanted to use the tag {{ previous_url }} in your templates you would override the get_context_data() method.
Normally you could also pass extra context using the top level attribute extra_context but the request object isn't available yet so you will be forced to override.
class PostDeleteView(LoginRequiredMixin, DeleteView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['previous_url'] = self.request.META.get('HTTP_REFERER')
return context
There's a site called classy class based views that breaks down all the methods used in Django's built in class based views so you can get an idea of how everything is put together behind the scenes.
Actually I just remembered an easier solution, if you have the request context processor enabled in TEMPLATES in your project settings then you can access {{ request }} directly in your templates.
'context_processors': [
...
'django.template.context_processors.request',
...
]
Related
I am trying to design a Django application that facilitates the lending and borrowing of musical instruments between musicians.
I have one template page that includes the form to post an instrument for lending or borrowing, and another template that includes the form for searching for available listings.
The difference between the two views other than the templates that are rendered (slightly different designs and buttons) is the name of the form they add to the context (i.e. PostForm() and SearchForm())
Basically, I have two views with almost completely the same code. This is bad practice usually.
Is there any way I can consolidate the two views into a "super-view" of sorts so that changes to one view are automatically made across both? I want to avoid duplicate code wherever possible.
This is very easy to do with Class Based Views (CBV).
For example, you may use django.views.generic.FormView, as follows in your views.py:
from django.views import generic
class ClassyFormView(generics.FormView): # Of course name the view whatever you like
# Note that I'm not setting any of the specific attributes here
# As I am planning on overriding this view for each form's specifics
# This is where you may override methods of the FormView
def get_context_data(self, *args, **kwargs):
""" This method allows you to edit the context passed to the template """
context = super(ClassyFormView, self).get_context_data(*args, **kwargs) # Get context from FormView
# Add variables to the context data
context['message'] = "Hello World!"
return context
def form_valid(self, form):
"""
This method is called once the form has been instantiated with
POST data and has been validated
"""
# Do whatever you want after the form is validated
print(form.cleaned_data['some_field'])
def form_invalid(self, form):
# Do something if the form is invalid
pass
You can then override your custom class, to maintain the specific things it does over FormView, but use the correct form class:
class SearchFormView(ClassyFormView):
form_class = SearchForm
class PostFormView(ClassyFormView):
form_class = PostForm
Note that you can (and probably will) also set fields such as prefix, success_url, and template_name, as well as override tons of other methods that may be useful!
Note that if your forms are ModelForms, then you will probably want to use one of the model specific generic form views, such as CreateView or UpdateView. Using these will allow you to access the object that the form is acting on. So, after setting the correct model in the class, your form_valid method may look like this:
def form_valid(self, form):
self.object = form.save(commit=False)
# Do stuff here to the object before you save it
# Such as altering or setting fields
self.object.some_field = "I'm a string"
self.object.save()
I can't explain everything about CBV, or even the class based form views here, so make sure to look at further documentation:
Django Class-Based-View Inspector is a really awesome site that not many people seem to know about! Usually easier than diving into the source code.
Relevant Django docs:
CBVs
Generic editing views
Decided to add a few more details that may be helpful.
The generic views specify defaults for their class attributes, but unless you're being really generic, it's usually a good idea to override them. Some explanation on the specific attributes:
prefix specifies a prefix for the form. Useful in cases where using multiple forms.
Defaults to None.
If you require more advanced logic, you can set the prefix by returning it in the get_prefix() method.
success_url specifies where the form will redirect to on success. For the model form views, this will default to the model's get_absolute_url()
Can be set by returning success url in get_success_url() method
template_name specifies the name of the template that the view will display upon a get request
Can be set by returning template name in get_template_name() method.
Configuring URLs for CBV is easy, too. Use the as_view() method as follows:
url(r'^some_url/', SearchFormView.as_view(), name="some-url")
In my url conf, I have several URL's which have the same named parameter, user_id.
Is it possible to access this parameter either in a middleware - so I can generically pass it on to the context_data - or in the template itself?
Sample URL conf to illustrate the question:
url(r'^b/(?P<user_id>[0-9]+)/edit?$', user.edit.EditUser.as_view(), name='user_edit'),
url(r'^b/(?P<user_id>[0-9]+)/delete?$', user.delete.DeleteUser.as_view(), name='user_delete')
For class based views, the view is already available in the context, so you dont need to do anything on the view side. In the template, just do the following:
{{ view.kwargs.user_id }}
See this answer
If you need this data in the template, just override your view's get_context_data method:
class MyView(View):
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['user_id'] = self.kwargs.get('user_id')
return context
For function based views:
template
{% url 'view' PARAM=request.resolver_match.kwargs.PARAM %}
views.py
def myview(request, PARAM):
...
Django 2.2
Each time a request is made to my app, I am using middleware to retrieve and store a 'Page' object which has information relevant that particular page. I am storing a reference to this object in the request object, here is an example:
class PageMiddleware(object):
def process_request(self, request):
if not hasattr(request, 'page'):
request.page = Page.objects.get(slug=<slug>)
return None
It works well enough, but I want to access this object in a template tag. Template tags only have a reference to 'context' thought, meaning I am unable to see my Page object.
I know I can use a custom context processor for this but that means modifying the settings file further and I'd like to try and keep this app as encapsulated as possible. I noticed, for example, that the Debug Toolbar app manages to append data to the template context without modifying the TEMPLATE_CONTEXT_PROCESSORS.
In short, I want to access my Page object in a template tag ideally by just using middleware. Any ideas?
Edit: I am using a standard template tag, with the following class:
class GetPageContentNode(Node):
def __init__(self, key):
self.key = key
def render(self, context):
return context['request'].page
Have a look at this, you can get access to the request object (and your object) by passing takes_context when registering the template tag
Access request in django custom template tags
Have a search for "takes_context" on this page:
https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#registering-the-tag
Call every render_to_response with a context_instance parameter, like:
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
EDITED as sugested by Daniel Roseman:
And add django.core.context_processors.request to your TEMPLATE_CONTEXT_PROCESSORS settings.
Try this:
class GetPageContentNode(Node):
def __init__(self, key):
self.key = key
def render(self, context):
request = template.Variable('request').resolve(context) # here's the magic!
return request.page
In Django - Overriding get_form to customize admin forms based on request the problem is to select a different form based on the permissions of the user in the request object by hooking the get_form() method.
I would like to actually invoke a method on the object during iteration that uses the request context to output some information.
The documentation lists four ways to hook the form display.
But the function signatures don't include the request object. If they did, you could write something like (note that request is not in fact an argument):
class CustomAdmin(admin.ModelAdmin):
list_display = [ 'name', 'user_specific', ]
#
def user_specific(self, obj, request):
return obj.func1(request)
#
output.short_description = 'UserSpecific'
Overriding get_form() would not be thread safe if used to store the state... So what would be the best way?
In your case, I feel that maybe writing your own view is a better choice than hacking django's admin site.
But if you insist, you can override changelist_view and record the request.
class CustomAdmin(admin.ModelAdmin):
list_display = [ 'name', 'user_specific', ]
def changelist_view(self, request, extra_context=None):
self.request = request
return super(admin.ModelAdmin, self).changelist_view(self, request, extra_context)
def user_specific(self, obj):
return obj.func1(self.request)
output.short_description = 'UserSpecific'
I get an error in my class AuthorCreateForm when I submit my form.
NameError
self is not defined
How do I use a CreateForm?
I have created a class in my Author.py file
from django.views.generic import TemplateView, ListView, CreateView
from books.models import Author, Publisher, Book
from books.forms import AuthorForm
class AuthorCreateView(CreateView):
objAuthorForm = AuthorForm(self.request.POST)
if(objAuthorForm.save()):
success = "Form saved!"
else:
error = "There was an error!"
and I have a html template which submits to /Author/Create
and I have the following line in my urls.py
('^authors/create/$', Author.AuthorCreateView.as_view()),
I render the form at this URL
('^authors/new/$', TemplateView.as_view(template_name="author_new.html")),
I find the class based views confusing, does anyone have a good tutorial on how to use it for CRUD operations?
Thanks
What you have is a python error -- self is not defined. self is generally what refers to the class instance itself on class methods.
Anyways, I agree, it's brand spanking new and not as documented. I think looking at the source is absolutely key at this point.
To get comfortable with class based views, I'd start by subclassing django.views.generic.base.View, which implements only a few methods, namely attempting to call a function on the class based on the request method (post, get, head, - look at source).
For example, here's the first step to replace view functions with the new view classes:
class MyClassBasedView(View):
def get(self, request):
# behave exactly like old style views
# except this is called only on get request
return http.HttpResponse("Get")
def post(self, request):
return http.HttpResponse("Post")
(r'^foobar/$', MyClassBasedView.as_view())
Back to your specific question:
All TemplateView.as_view() does is render the template - CreateView is a combination of several other classes that handle ModelForms and template rendering (TemplateView).
So, for a very basic example, look to the docs for what class mixins are used by CreateView.
We see it implements TemplateResponseMixin, ModelFormMixin, and ProcessFormView, each containing a list of methods for those classes.
The most basic CreateView
At the most basic level, provide CreateView's ModelFormMixin with the model or custom ModelForm class as documented here.
Your CreateView class would look something like the following
class AuthorCreateView(CreateView):
form_class = AuthorForm
template_name = 'author_new.html'
success_url = 'success'
With those 3 core attributes set, call it in your URLs.
('^authors/create/$', Author.AuthorCreateView.as_view()),
Render the page and you'll see your ModelForm passed to the template as form, handling the form validation step (passing in request.POST / re-render if invalid), as well as calling form.save() and redirecting to the success_url.
Start overriding the class methods
To customize behavior, start overriding the methods documented for the mixins.
Remember that you simply need to return an HttpResponse from one of these methods just like any regular view function.
Example overriding form_invalid documented in ModelFormMixin:
class AuthorCreateView(CreateView):
form_class = AuthorForm
template_name = 'author_new.html'
success_url = 'success'
def form_invalid(self, form):
return http.HttpResponse("form is invalid.. this is just an HttpResponse object")
This per-method overriding starts becoming extremely useful as your forms grow more advanced and ultimately lets you build huge forms with a handful of lines of code, overriding only what is necessary.
Say you want to pass your form custom parameters such as the request object (very common if you need access to the user in the form): you merely need to override get_form_kwargs.
class MyFormView(FormView):
def get_form_kwargs(self):
# pass "user" keyword argument with the current user to your form
kwargs = super(MyFormView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Class based views are a shining example of smart class usage. It gave me a great intro towards building my own mixins for views and python classes in general. It is saving countless hours.
Wow this got long. To think it started as a mere URL to the docs comment :)