How do I wrap a Django Form Wizard in a view? I need to do this so I can access request.
Does anyone have some example code for this?
I probably should be just commenting on Manoj's answer, but sounds you need code
urls.py
from django.conf.urls.defaults import *
from MyApp import views
urlpatterns = patterns(
'',
(r'^wizard/$', views.MyWizardView ),
)
views.py
#login_required
def MyWizardView (request):
cw = MyWizard([WizardName, WizardQuestions, WizardProvider, WizardGoomber])
return cw(request)
The as_view function converts a class based view into a callable view:
from django import forms
from django.contrib.formtools.wizard.views import SessionWizardView
class Form1(forms.Form):
a = forms.CharField()
class Form2(forms.Form):
b = forms.CharField()
class MyWizard(SessionWizardView):
pass
wizard_view = MyWizard.as_view([Form1, Form2])
def view(request):
# do something fancy with the request object here
return wizard_view(request)
This is basicly the same answer as in How to wrap a Django Form Wizard in a View?
This Django snippet may prove useful.
From the title: "FormWizard inside view with proper context handling and site templating support, without having to use urls.py"
Related
is there anyway to improve this in the django admin panel?
As you can see its a simple Many2Many field.
I would like to access that record with a simple mouse click.
You can add custom HTML into the admin as a field if you define the content in a method:
from django.utils.html import format_html
from django.utils.safestring import mark_safe
#admin.register(MyModel)
class MyModelAdmin(ModelAdmin):
fields = ['related_thing_links', ...]
readonly_fields = ['related_thing_links']
def related_thing_links(self, obj):
items = []
for thing in obj.related_things.all():
url = reverse(
'admin:myapp_relatedthing_change',
kwargs={'pk': thing.pk}
)
items.append(format_html(
'<li>{thing}</li>',
url=url,
thing=thing,
))
return mark_safe('<ul>' + ' '.join(items) + '</ul>')
related_thing_links.short_description = 'related things'
I've not managed to test this exactly, but it's based on working code.
I'm new to Django and I've been trying to make so small app after reading the tutorial but I can't figure out what's wrong with my code.
What I'm trying to do is listing all the database entries of a model called project using a ListView with a form below it (using the same view) that the user can use to filter the entries to be shown by means of submitting data in text fields.
I managed to make that work, and it looks like this:
However, once the user clicks the "Filter results" button providing some filtering pattern on the textfields (let's say, filter by name = "PROJECT3", leaving the other fields blank), instead of rendering a page with the filtered data and the form below it, which is my intention, it just returns a white page.
Can anyone please explain me what is wrong with my code?
Here are the relevant parts:
forms.py
class FilterForm(forms.Form):
pjt_name = forms.CharField(label='Name', max_length=200, widget=forms.TextInput(attrs={'size':'20'}))
pjt_status = forms.CharField(label='Status', max_length=20, widget=forms.TextInput(attrs={'size':'20'}) )
pjt_priority = forms.CharField(label='Priority', max_length=20, widget=forms.TextInput(attrs={'size':'20'}))
pjt_internal_sponsor = forms.CharField(label='Int Sponsor', max_length=20, widget=forms.TextInput(attrs={'size':'20'}))
pjt_external_sponsor = forms.CharField(label='Ext Sponsor', max_length=20, widget=forms.TextInput(attrs={'size':'20'}))
views.py
from App.models import Project
from django.views.generic import ListView
from django.shortcuts import render
from django.template import RequestContext
from App.forms import FilterForm
class ProjectListView(ListView):
context_object_name = 'project_list'
template_name='App/index.html'
def get_context_data(self, **kwargs):
context = super(ProjectListView, self).get_context_data(**kwargs)
if 'filter_form' not in context:
context['filter_form'] = FilterForm()
return context
def get_queryset(self):
form = FilterForm(self.request.GET)
if form.is_valid():
name = form.cleaned_data['pjt_name']
i_sp = form.cleaned_data['pjt_internal_sponsor']
e_sp = form.cleaned_data['pjt_external_sponsor']
status = form.cleaned_data['pjt_status']
pri = form.cleaned_data['pjt_priority']
return send_filtered_results(name, i_sp, e_sp, status, pri)
else:
return Project.objects.order_by('-project_creation_date')[:5]
def send_filtered_results(name, i_sp, e_sp, status, pri):
return Project.objects.filter(project_name__in=name,internal_sponsor_name__in=i_sp, external_sponsor_name__in=e_sp, project_status__in=status, project_priority__in=pri).exclude(alias__isnull=True).exclude(alias__exact='')
urls.py
from django.conf.urls import patterns, url
from App.views import ProjectListView
from django.views.generic import DetailView
from App.models import Project, Task
urlpatterns = patterns('',
url(r'^$',
ProjectListView.as_view())
The answer is in your response/status code:
After doing that I get a white page and runserver returns [23/Jan/2015 00:21:09] "POST /App/ HTTP/1.1" 405 0
You're POSTing to a view that has no POST handler. You should be getting an error saying so, but the 405 means method not allowed.
Add a post method to your CBV. Django class based views map request method to functions, so a GET is handled via CBV.get, POST via CBV.post
For demonstration purposes, add:
# essentially, mirror get behavior exactly on POST
def post(self, *args, **kwargs):
return self.get(*args, **kwargs)
And change your form handler to pull from request.POST not request.GET.
form = FilterForm(self.request.POST)
I suggest you start using print()s or log to start seeing what's happening. request.GET should be empty, as.. you're not using GET parameters. That data will be in request.POST.
Your code is messy and your Project.objects.filter(...) is far to aggressive. It just doesn't return any objects.
Don't use name__in=name but name__contains=name.
I created a very simple example code using Django, but cannot get model value to be displayed on my page:
----------------------------- home/models.py
from django.db import models
class Home(models.Model):
msg = models.CharField(max_length=100)
#classmethod
def create(cls, msg):
home = cls(msg=msg)
# do something with the book
return home
home = Home.create("Hello World!")
------------------------------------home/views.py
from django.views.generic import TemplateView
from project.models import Home
class IndexView(TemplateView):
model = Home
template_name = 'home/index.html'
------------------------------------------ templates/home/index.html
{{ home.msg }}
this is a test page. I am expecting to see this....
------------------------------------------- urls.py
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView
admin.autodiscover()
urlpatterns = patterns('',
# Home pagetentacl.urls
url(r'^$', TemplateView.as_view(template_name='home/index.html')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)
-------------------------------------- result page on browser:
this is a test page. I am expecting to see this....
I don't want to have DB access for my example. I want my model returns "hello world" string. home.msg on index.html doesn't return anything. What is missing here?
You're not giving the template an instance of Home. You need to create one and pass it to the template as a Context, in the form {'msg': msg}.
EDIT: Adding some code
First of all, you should create your instance of home in your view. I've never used TemplateViews, so I'm going to use a regular view method instead.
def IndexView(request):
home=Home.create("Hello World!")
return render(request, 'index.html', {'home': home},)
As #Daniel rightly points out, you're not giving your template an instance of Home to work with.
If you want to use class-based views, subclass TemplateView and override get_context_data():
class IndexView(TemplateView):
template_name = "home/index.html"
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context["home"] = Home.create("Hello World!")
return context
And make sure your urls.py is using IndexView.as_view() - your version above is just referencing the generic TemplateView.
The fact that you added a model field to your subclass of TemplateView makes me think you're confusing it with DetailView. See the documentation for the difference.
I know this is really simple, but I'm missing something. And because I can't ever remember this, I'm hoping this can document a solution here.
All I want to do is pass a PK for the object in the URL and get the detail view back.
Url:
url(regex=r'^(?P<pk>\d+)/$',
view=AdventureDetail.as_view(),
name='adventure_detail',
),
View:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
template_name = "adventure/AdventureDetail.html"
def get_object(self):
return get_object_or_404(Page)
But I'm getting a "multiple objects returned error"
MultipleObjectsReturned at /1/
get() returned more than one Page -- it returned 5! Lookup parameters were {}
This feels really silly. It should "just work" but I'm missing something obvious.
Thanks for the help.
In DetailView it is more simpler: you can just specify the model:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
model = Page
template_name = "adventure/AdventureDetail.html"
And that's all. DetailView will do the rest of work.
Another way is to specify queryset:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
queryset = Page.objects.all()
template_name = "adventure/AdventureDetail.html"
This will have the same result.
And the last way is to override the get_object method.
Look here for details
You're not passing any other parameters to get_object_or_404, other than the Page class. So now you're basically querying for all pages. So you'd need to do:
return get_object_or_404(Page, pk=self.kwargs.get('pk', None))
Also, why are you overriding get_object? The DetailView already contains this functionality so all you need to do is have a URL with pk in it.
In views.py
from django.views.generic import DetailView
# Import your model that you want to use in details view for example
from .models import Post
class PostDetailView(DetailView):
model = Post
Then create a template with the following name conversion
<appname>/<model_viewtype>.html
In urls.py
First import the class view that you created. In our case it is
from .views import PostDetailView
Then
path("post/<int:pk>/", views.PostDetailView.as_view(), name="PostDetailView")
I am trying to change to class-based views in Django after an upgrade and I have two questions regarding this. This is my code, simplified:
# urls.py
urlpatterns += patterns('project.app.views',
('^$', 'index'), # Old style
url(r'^test/$', SearchView.as_view()), # New style
)
# views.py
class SearchView(TemplateView):
template_name = 'search.html'
def get_context_data(self, **kwargs):
messages.success(request, 'test')
return {'search_string': 'Test'}
When I run this I first get the error name 'SearchView' is not defined. Does anyone know why?
Trying to skip that I add from project.app.views import SearchView which is ugly and not the way I want it to work, but hey, I try to see if I can get the rest working. Then I get global name 'request' is not defined because of the messages. This makes sense but how do I get the request object here?
So I'd like to know: how do I get the views to work as intended and how to use messages in get_context_data()?
You are seeing name 'SearchView' is not defined because you have not imported SearchView into your urls.py. If you think this is ugly then you can do search = SearchView.as_view() in your views.py and use the old style to reference the view as search instead. The request can be accessed on self.request for adding the messages. The updated source example is given below.
# views.py
class SearchView(TemplateView):
template_name = 'search.html'
def get_context_data(self, **kwargs):
messages.success(self.request, 'test')
return {'search_string': 'Test'}
search = SearchView.as_view()
# urls.py
urlpatterns += patterns('project.app.views',
url('^$', 'index'), # Old style
url(r'^test/$', 'search'), # New style
)
Please ask a single question at a time (a StackOverflow guideline).
Anyway:
This is the way class-based views are intended to work. There's no auto-importing magic, you just import the class and use its as_view() method.
You can access the request object in your view class through self.request.