Why {{ form }} represents a form instance in template file? - django

According to django official doc,
All you need to do to get your form into a template is to place the form instance into the template context. So if your form is called form in the context, {{ form }} will render its <label> and <input> elements appropriately.
Where is the form instance named/defined as form for template context in django source code? and How can I change the name to something else {{ my_form }} for example ?

As you aptly say in your comment:
Context means the repository of data used for rendering via HTML
template files.
As to how form is passed to the context if you use a function based view, this is completely upto you with what key you pass the form, for example below code will pass it as my_form:
return render(request, 'template.html', {'my_form': my_form})
In case of a class based view most of them inherit from ContextMixin which declares the method get_context_data that is used to pass variables into the context. All class based views that deal with forms also inherit from FormMixin, looking at its source code it overrides get_context_data to pass the form into the context:
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
return super().get_context_data(**kwargs)
Of course you can decide to again override get_context_data yourself and pass the form with some other name, but doing so wouldn't be very useful:
class YourView(CreateView):
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['my_form'] = context['form']
return context

When rendering your view in Views.py you can do the following to change the name of the form that you render:
Views.py
from .forms import MyForm1
def MyForm(request):
my_form = Myform1
return render(request , 'MyForm.html' , {'my_form' : my_form})
Then when calling your form on the HTML page you can call it as
{{ my_form }}

Related

Where does the value 'form' in 'form.as_p' in Django template come from?

I know this question was asked before, but the accepted answer does not really answer the question:
where `form.as_p`in django templates come from?
In Django doc:
Example myapp/views.py:
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
Example myapp/author_form.html:
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
The question is, where does the template get the 'form' context from, since we did not explicitly define a render() function inside the AuthorCreate class? Thanks.
I found the answer.
CreateView inherits the get method from ProcessFormView class and the get_context_data method from the FormMixin class.
As you can see in the code, the get method returns an expression that calls get_context_data.
class ProcessFormView(View):
"""Render a form on GET and processes it on POST."""
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
return self.render_to_response(self.get_context_data())
In turn, get_context_data adds a keyword argument with key form if it is not already present in **kwargs:
class FormMixin(ContextMixin):
...
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
return super().get_context_data(**kwargs)
So CreateView renders a Response with a context that has a 'form' key and a value ponting to the ModelForm.

Django templates not updating after database changes via admin

I am using Django v2.2 admin to change the information on my database but after I change it and refresh the page, the new data is not there, only the old data.
A fix for this if I restart the server, the templates can now fetch the new data that I input.
views.py
# template with context
class Home(TemplateView):
template = 'home.html'
context = { 'bar': Baby.objects.all() }
def get(self, request):
return render(request, self.template, self.context)
home.html
{% for foo in bar %}
{{ foo.name }}
{{ foo.cost }}
{% endfor %}
How I can get the new data by refreshing the page and not restarting the server?
As others mentioned, use get_context_data() method is good idea, because ContextMixin is parent class (not base class, but part of TemplateView's __mro__ Method Resolution Order) of TemplateView which is responsible to pass data from view to template. But, if you want to render template manually using get() method, You should hit on database on every GET request (in your case).
class Home(TemplateView):
template = 'home.html'
def get(self, request):
self.context = {'bar': Baby.objects.all()}
return render(request, self.template, self.context)
Your code does not work, because static variables are initialized only once. In your case context was static variable.
Hope, it helps you.
Can you please try this?
class Home(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bar'] = Baby.objects.all()
return context

Django: ModelChoiceField as MultipleHiddenInput

I am trying to render a ModelChoiceFiel using the MultipleHiddenInput widget but the template does not generate any inputs at all.
Here is what I am trying:
class PresetSelectForm(forms.Form):
presets = forms.ModelChoiceField(queryset=Preset.objects.none(), widget=forms.MultipleHiddenInput())
def __init__(self, presets, *args, **kwargs):
super(PresetSelectForm, self).__init__(*args, **kwargs)
self.fields['presets'].queryset = presets
And in the template I am using the following:
{% csrf_token %}
{{ form }}
The csrf token input is generated, but nothing is for {{ form }}.
What am I missing?
Edit: I am including the essential and relevant code I am using in my view. It's just a FormView, so the form object is automatically created.
class PresetSelectView(FormView):
form_class = PresetSelectForm
The overriden methods are: get_form_kwargs, get_context_data, form_valid, form_invalid and dispatch.
I guess it's worth mentioning that I am not using the form instance anywhere in these methods except in form_valid, where I am getting form.cleaned_data['presets'] but not modifying form at all.
Here is the overriden get_context_data method:
def get_context_data(self, **kwargs):
request = self.request
context = super(PresetSelectView, self).get_context_data(**kwargs)
# now some lines retrieving models
# then we attach some additional attributes to the instances of some of these models (these attributes will be used in the template)
context.update({...})
return context

Mutiple forms with Class Based View. How to show errors at the same page?

views.py
from forms.py import PersonCreateForm
class PersonCreateView(CreateView):
model = Person
form_class = PersonCreateForm
template_name = "my_app/create_person.html"
def form_valid(self, form):
self.object = form.save()
return redirect('/homepage/')
class PeopleListView(ListView):
[...]
context.update({
'task_form': TaskCreateForm(),
return context
In my template I just add in action url which handle PersonCreateView.
<form action="{% url 'people_create' %}" method="post">{% csrf_token %}
When Form is valid then all data are saved without problems and it redirects me to '/homepage/.
But when my form is invalid then it redirects me to to {% url 'people_create' %} and shows errors at /homepage/people_create/
How can I avoid that? I want all errors show at same page without redirect.
Handle the form on the same view you build it, otherwise the page will change. You may mix django.views.generic.edit.ModelFormMixin into your PeopleListView so it has most of the features you need.
class PeopleListView(ModelFormMixin, ListView):
success_url = '/homepage/' # should use reverse() here
def get_context_data(self, **kwargs):
# only add the form if it is not already given to us
if not 'task_form' in kwargs:
kwargs['task_form'] = self.get_form()
return super(PeopleListView, self).get_context_data(**kwargs)
def post(self, request, *args, **kwargs):
# ListView won't have a post method, we define one
form = self.get_form()
if form.is_valid():
return self.form_valid(form) # default behavior will save and redirect
else:
return self.form_invalid(form) # default behavior has to be overridden (see below)
def form_invalid(self, form):
# Whatever you wanna do. This example simply reloads the list
self.object_list = self.get_queryset()
context = self.get_context_data(task_form=form)
return self.render_to_response(context)
There, you have three code paths:
Initial display will load the listview as usual, only an empty form will be added to the context.
On submitting valid input, the form_valid method is invoked, which will redirect to /homepage/.
On submitting invalid input, our overridden form_invalid method is invoked, which will render the page normally, except the form will contain the validation errors.
You may make the whole thing a bit more staightforward using a cached property for the form, but then you'd start working against Django's shipped views instead of with it, and might as well just use the basic View class and implement all logic by yourself. I'd stick with Django's views, but ymmv.

Captured URL parameters in form

I am using Userena and I am trying to capture URL parameters and get them to my form but I'm lost how to do this.
What I would like to do in my template is:
Free Plan<br/>
Pro Plan<br/>
Enterprise Plan<br/>
And then in my urls.py
url(r'^accounts/signup/(?P<planslug>.*)/$','userena.views.signup',{'signup_form':SignupFormExtra}),
Then, ideally, I'd like to use that planslug in my forms.py to set the user plan in the profile.
I'm lost how to get the captured URL parameter into the custom form. Can I use the extra_context, do I have to override the Userena signup view?
If you use class based views, you can overwrite the def get_form_kwargs() method of the FormMixin class. Here you can pass any parameters you need to your form class.
in urls.py:
url(r'^create/something/(?P<foo>.*)/$', MyCreateView.as_view(), name='my_create_view'),
in views.py:
class MyCreateView(CreateView):
form_class = MyForm
model = MyModel
def get_form_kwargs(self):
kwargs = super( MyCreateView, self).get_form_kwargs()
# update the kwargs for the form init method with yours
kwargs.update(self.kwargs) # self.kwargs contains all url conf params
return kwargs
in forms.py:
class MyForm(forms.ModelForm):
def __init__(self, foo=None, *args, **kwargs)
# we explicit define the foo keyword argument, cause otherwise kwargs will
# contain it and passes it on to the super class, who fails cause it's not
# aware of a foo keyword argument.
super(MyForm, self).__init__(*args, **kwargs)
print foo # prints the value of the foo url conf param
hope this helps :-)
You can access the url in your template using -
{% request.get_full_path %}
(see the docs for more info).
However if you just want to get the planslug variable then pass it from the view to the template and access it in the template (it's available in the view because it's a named parameter in the url) -
def signup(request, planslug=None):
#
render(request, 'your_template.html', {'planslug':planslug}
and then in your template you get it with -
{% planslug %}
If you're using class based views then you'll need to override get_context_data to add the planslug variable to your context before you pass it to the template-
def get_context_data(self, *args, **kwargs):
context = super(get_context_data, self).get_context_data(*args, **kwargs)
context['planslug'] = self.kwargs['planslug']
return context