Django: ModelChoiceField as MultipleHiddenInput - django

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

Related

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

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 }}

refer to ModelField property in template

There is a possibility in Django templates to refer to attributes of the field directly in a template. For example to .help_text or .label. Like that:
{% for field in form %}
{{field.label|safe}}
{{field}}
{% endfor %}
What is the right way to refer to a custom defined field property?
For example:
{{field.myproperty}}
I use ModelForm and in models.py I use my own ModelField. Everything works perfectly but any attempts to add my own property fail. In a sense that everything is ok but if I refer to this property in a template it just doesn't get it :-(
Models.py:
class MyFormField(forms.IntegerField):
MyProperty = 'whatever'
def __init__(self,active1='default',*args, **kwargs):
self.MyProperty = 'whatever'
super(MyFormField, self).__init__(*args, **kwargs)
class MyOwnField(models.IntegerField):
def __init__(self, active1='asdf',*args, **kwargs):
super(MyOwnField, self).__init__(*args, **kwargs)
def formfield(self, **kwargs):
defaults = {'form_class': MyFormField}
defaults.update(kwargs)
return super().formfield(**defaults)
class MyModel(Model):
ns6_1 = MyOwnField()
Firstly, case is significant; field.myproperty is not the same as field.MyProperty.
Secondly, when you iterate through a form you don't directly get the form fields, you get BoundField instances. Each of these has a field property which is the field itself. So:
{{ field.field.MyProperty }}

Making Django forms return forms.instance in templates

I have complex user-defined permissions for my users. But to make thinks simpler, let's imagine there's only read-only or write permissions for each user.
I am using Django forms to edit and save model objects. And my goal is to render <input> in the Django HTML template for those users who have the permission to edit a given model instance, and a hard-coded data (without <input> tag) if the user has only read-only permission.
Currently, I have the following code in my Django template to achieve this:
{%if user.has_permission_to_edit %}
{{my_form.my_field}}
{% else %}
{{my_form.instance.my_field}}
{% endif %}
And here's my_form:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control input-sm'
if field.required == True:
field.widget.attrs['required'] = ''
class Meta:
model = MyModel
fields = ('my_field',)
The problem with the code in the template is that I have to use multiple {% if %}{% else %} blocks. I am relatively new to Django, and I know that there is plethora of advanced tools making Django code potentially super DRY, so I want to asked you guys, what is the most DRY method to organize what I described in the template. Specifically, is there any way to make Django forms return instance values based on some condition specified inside the form definition? Or do I have to some used-defined tag ? Or maybe some totally different architecture is used to achieve such goals?
For what I understood form your question, you want to pass the instance of the data fetched from your data-source.
from .forms import MyForm
from django.shortcuts import render
assuming you have created a forms.py file at the views.py level.
Fetching data from data-source(Detail is the model in below example)
detail_instance = Detail.objects.get(user=request.user.id)
reg_form = MyForm(instance=detail_instance or None)
# In case of edit scenario you can pass in the post params to the form as well
reg_form = MyForm(request.POST, instance=detail_instance)
# Or form with uploads
reg_form = MyForm(request.POST, request.FILES, instance=detail_instance)
Now once we have data inside our reg_form parameter we can pass it in the template
return render(request, 'applicant/register.html', { 'my_form' : reg_form})
Do whatever you wish, with you my_form variable in the template.
BASED ON THE UPDATED QUESTION
You can pass parameter to the init function of a form
reg_form = MyForm(exist = exist, some_param = param_value, instance=detail_instance or None)
After passing the param, the param can be fetched and processed in the init function of the form
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
exist = kwargs.pop('exist', None)
pk_reg = kwargs.pop('param', None)
super(MyForm, self).__init__(*args, **kwargs)
#do some stuff with you custom params here
if exist == True or pk_reg:
self.fields['username'].widget.attrs['readonly'] = True
The above approach has an alternative as well for having separate forms for separate permission and calling the appropriate form based on the user permissions.

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