I am back with more django questions on CBVs. This is about context_object_name. I have the following:
#method_decorator(verified_email_required, name='dispatch')
class Create(CreateView):
model = Profile
context_object_name = 'profileForm'
template_name = 'Members/template_includes/profile/form.html'
form_class = ProfileForm
success_url = '/Members'
form_title = "New Login Profile Information"
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {
'profileTitle': self.form_title,
})
I am using PyCharm and can put a breakpoint in the template_name form and see what the environment knows about. I expect to see a dict named profileForm with all the form members in it plus profileTitle. Instead I see profileTitle as a standalone member. I do not see anything named profileForm or object_list and the expected form members are not being painted in the template.
I suppose that I understand that the extra content in the return render will pass a "naked" profileTitle but I did expect that the default get behaviour would pull in the form info.
Have I missed the point?
You've overridden the get method in your CreateView-subclass and in doing so, you've bypassed the included functionality that a CreateView does to fill your context. If you take a look here you can see that a CreateView would otherwise call return self.render_to_response(self.get_context_data()) (because it inherits from ProcessFormView) and it's within get_context_data() (ref) that those included context variables are set up.
Related
So, i have a rather usual "update item" page that is a class-based view which inherits UpdateView. (in views.py it looks like "class ItemUpdateView(UpdateView) and it has method get_success_url(self) defined which contains the redirect url where user will be taken after clicking "Update" button.
My problem is that in my application, there are two different pages that could lead me to this "Update item" page, and depending on the page that user comes from - i want to take the user back to either pageA or pageB upon the successful update of the item.
I wasn't able to find the best-practices of how to handle this anywhere on the web, so - would really appreciate the help.
My guess is that I need to create an additional parameter that will be a part of the url and will contain A or B depending on the pageA or pageB that user came from, i.e. the url itself would be something like '/itemUpdate/int:pk/sourcepage' => '/itemUpdate/45/A'. Does that sound like a correct aproach or is there a better way?
There is a better way that you can check Meta dictionary in request:
write in your views file:
class ItemUpdateView(UpdateView):
previous_url = ''
form_class = UpdateItem
def get(self, request, *args, **kwargs):
self.previous_url = request.META.get('HTTP_REFERER')
print(self.previous_url)
return super().get(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial['success_url'] = self.previous_url
return initial
def form_valid(self, form):
self.success_url = form.cleaned_data['success_url']
print(self.success_url)
return super().form_valid(form)
# also you can use get_success_url instead of form_valid()
# def get_success_url(self):
# return super().get_form().cleaned_data['success_url']
and then write a hidden field in your form and name it success_url
class UpdateItem(forms.ModelForm):
success_url = forms.URLField(widget=forms.HiddenInput)
class Meta:
model=Item
fields=['itemName','quantity']
Note you can not use instance in order to get success_url field, because this field belong to form nor your model instance !
refer to documentions
I've been learning Django and one source of confusion I have is with class based views and when to override the get method. I've looked through the documentation and it explains what get does but it doesn't explain when I should override get.
I originally created a view this way:
class ExampleView(generic.ListView):
template_name = 'ppm/ppm.html'
paginate_by = 5
def get(self, request):
profiles_set = EmployeeProfile.objects.all()
context = {
'profiles_set': profiles_set,
'title': 'Employee Profiles'
}
return render(request, self.template_name, context)
But I was recently told that my code was simple of enough for the default implementation, and that all I needed was this:
class ExampleView(generic.ListView):
model = EmployeeProfile
template_name = 'ppm/ppm.html'
So my Question is this: In what scenario/circumstance should I override the get method?
If you are using the builtin generic views, then you should rarely have to override get(). You'll end up either duplicating lots of functionality, or break features of the view.
For example, the paginate_by option will no longer work in your view, because you are not slicing the queryset in your get() method.
If you are using a generic class based view like ListView, you should try to override specific attributes or methods where possible, rather than overriding get().
The advantage of your view which overrides get() is that it's very clear what it does. You can see that the view fetches a queryset, includes it in a context, then renders the templates. You don't need to know about ListView to understand the view.
If you like the explicitness of overriding get() subclass View instead. You aren't using any of the features of ListView, so it doesn't make sense to subclass it.
from django.views.generic import View
class ExampleView(View):
template_name = 'ppm/ppm.html'
def get(self, request):
...
You should override the get method when you specifically want to do something other than the default view does. In this case, your code isn't doing anything other than rendering the template with the list of all EmployeeProfile objects, which is exactly what the generic ListView would do.
You might override it if you want to do something more complicated. For example, maybe you want to filter based on a URL parameter:
class ExampleView(generic.ListView):
template_name = 'ppm/ppm.html'
def get(self, request):
manager = request.GET.get('manager', None)
if manager:
profiles_set = EmployeeProfile.objects.filter(manager=manager)
else:
profiles_set = EmployeeProfile.objects.all()
context = {
'profiles_set': profiles_set,
'title': 'Employee Profiles'
}
return render(request, self.template_name, context)
I've overridden a detail view so I can render form elements with render_field. However, when rendered they do not show the saved values. I can't set the value in the template because I can't put {{}} within {% %} syntax. How can I access and display the previously saved model instance values? (these are sliders that I want to keep as sliders, and they also have a lot of data attributes that I want to keep consistent, so I can't just write the inputs manually in the template)
In views.py:
class MyDetailEditMixin(SingleObjectMixin):
"""
Hybrid mixin to edit a detail
"""
model = MyModel
form_class = forms.MyForm
raise_execption = True
def get_context_data(self, **kwargs):
"""
expose the form
"""
kwargs.setdefault('form', forms.MyForm)
return super(MyDetailEditMixin, self).get_context_data(**kwargs)
class MyDetailView(MyDetailEditMixin, DetailView):
"""
Shows the details
"""
I feel like I need to explicitly mention the instance somehow? I'm not sure. The inputs render correctly with all their specific data attributes, just no set values.
I tried many different things, but in the end I ditched the mixin and edited MyDetailView with a get_form method (and within that I could tell it to use the instance):
class MyDetailView(DetailView):
"""
Shows the details
"""
form_class = forms.MyForm
def get_form(self):
form = self.form_class(instance=self.object)
return form
def get_context_data(self, **kwargs):
context = super(MyDetailView, self).get_context_data(**kwargs)
context.update({
'form': self.form_class(instance=self.get_object()),
})
return context
The render_field fields have their values filled with the saved instance values and all the attributes they need are there.
I have a created a view class "class Demo", which has 3 functions
def update_time()
def get_context()
def before_response()
urls.py : url(r'^demo/$', Demo.as_view(),name='demo_class'),
When i'll enter url /demo/ how will it determine which function to call from "class Demo" ?
Because Django’s URL resolver expects to send the request and associated arguments to a callable function, not a class, class-based views have an as_view() class method which serves as the callable entry point to your class. The as_view entry point creates an instance of your class and calls its dispatch() method. dispatch looks at the request to determine whether it is a GET, POST, etc, and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed if not.
just read the docs
Basically class based views are recommended when you need to handle both get and post requests at one point. For example in get method of class Register, you can render the registration form and in its post method, you can handle the form submission. If its a get request it will automatically invoke the get() method in the class, same for post request. Also you can write any common code in the dispatch() method which will be invoked for every request.eg:
class Register(View):
def dispatch(self, *args, **kwargs):
'''
common code here
'''
return super(Register, self).dispatch(*args, **kwargs)
def get(self, request):
registration_form = RegistrationForm()
return render(request, 'new.html', { 'form': registration_form })
def post(self, request):
registration_form = RegistrationForm(request.POST or None)
if registration_form.is_valid():
#save form
return HttpResponseRedirect(reverse('success-show'))
return render(request,new.html', { 'form': registration_form })
For references you can check this website.
You need to subclass a class based views, and depending on that it will have one or another method.
For example TemplateView renders a template you pass in the template_name attribute.
All class based views care about is that the attributes needed to work properly are setted. That is done via different methods. You can check the django's documentation for specifics.
For example, if you want to render a form in your template view, you will need to pass the form in the context, so you can override get_context_data() like:
def get_context_data(self, **kwargs):
context = super(DemoClass, self).get_context_data(**kwargs)
context['form'] = MyForm()
return context
There are different methods to handle different things, like querysets, objects, etc.
They are not complicated, but they are specific. If a class based view does not fit what you need, it may be easier to implement the functionality from a more general view (View, TemplateView) than forcing a more specific one to do things it is not intended for.
slightly change the url
add numbers one to three in url and put the condition in your view.
Ex.
url(r'^abc/(?P<newid>.*)$', 'class_demo'),
so your url will be like abc/1 or abc/2 or abc/3
view
def class_demo(requests, newid):
if newid==1:
update_time()
elif newid==2:
get_context()
I am trying to process two forms in a Django class based view. The site contains a form called form (based on GET) for narrowing the list results of the ListView and the second form status_form (based on POST).
Both forms are required since the ListView returns a list of items. Form lets the user restrict the choices and status_forms lets the user flag incorrect items via a modal form (therefore it needs to be in the same template).
My trouble is that ListView does not come with the method post, however FormView does. My class List inherits from both classes, but when I execute the class I get the error message:
Attribute Error: 'List' object has no attribute 'status_form'
How should I change my implementation to allow the second form been processed via the post method?
class List(PaginationMixin, ListView, FormMixin):
model = ListModel
context_object_name = 'list_objects'
template_name = 'pages/list.html'
paginate_by = 10 #how may items per page
def get(self, request, *args, **kwargs):
self.form = ListSearchForm(self.request.GET or None,)
return super(List, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.status_form = StatusForm(self.request.POST or None)
if self.status_form.is_valid():
...
else:
return super(List, self).post(request, *args, **kwargs)
def get_queryset(self):
# define the queryset
...
# when done, pass to object_list
return object_list
def get_context_data(self, **kwargs):
context = super(List, self).get_context_data(**kwargs)
context.update(**kwargs)
context['form'] = self.form
context['status_form'] = self.status_form # Django is complaining that status_form is not existing, result since the post method is not executed
return context
# Django is complaining that status_form does not exist,
# result since the post method is not executed
context['status_form'] = self.status_form
Because you didn't define self.status_from in the first place.
You have defined it in get_context_data, and it's accessible from there.
You can access you object from get_context_data in your post method;
context = self.get_context_data(**kwargs)
status_form = context['status_form']
Also consider that you can define your status_form directly in post method itself without getting it from self or get_context_data.
Redesign you views to separate each Form processing in separate Views then tight them with each-other.
Views redesign:
In nutshell, let each view to do one job. You can create a View just for processing your status_form and name it like StatusFormProcessView then on your List view return it on its post method
class List(ListView);
def post(self, request, *args, **kwargs):
return StatusFormView.as_view()(request) # What ever you need be pass to you form processing view
This is just an example of it, need more work to be real.
For another example; On my website index page I have a search form. when user POST or GET the search form, The processing of searching doesn't exist in my IndexView, instead I handle the whole form stuff in separate view, If form should process on GET method, I'll override get() method, If form should process on POST, I'll override post() method to send search_form data to the view that is responsible for handling of processing the search_form.
Comments response
status_form = context['status_form']
shouldn't it be
context['status_form'] = status_form
after I created it ?
You want to get status_form from context, So you need to
status_form = context['status_form']
Anyway, your form data are available on self.request.POST