Django: Switch a form in template depending on choice field - django

I am looking for a way to switch and display a form in template, depending on choice in CharField in another form.
My app has the following models:
class Damage(models.Model):
damage_place = models.CharField()
damage_date = models.DateField()
class DamageType1(models.Model):
damage = models.ForeignKey(Damage)
...
class DamageType2(models.Model):
damage = models.ForeignKey(Damage)
...
class DamageType3(models.Model):
damage = models.ForeignKey(Damage)
...
Damage model has a DamageForm(forms.ModelForm) that is filled in by another user.
Each DamageType model has different fields (marked as ... for simplicity) and own DamageTypeForm1(forms.ModelForm), DamageTypeForm2(forms.ModelForm) and DamageTypeForm3(forms.ModelForm).
There is also a form in a template specifying which DamageType to choose:
class DamageSpecify(forms.Form):
DAMAGE_CHOICES = [
('DamageType1', 'DamageType1'),
('DamageType2', 'DamageType2'),
('DamageType3', 'DamageType3'),
]
damage_type = forms.CharField(choices=DAMAGE_CHOICES )
And now, after choose specific damage_type in DamageSpecify I would like to display the filled DamageForm and empty DamageType(1, 2 or 3) in template to fill and save both forms.
How could this be done?

How I'd tackle it. Write 4 forms. One, with a single ChoiceField. The other 3, to get the appropriate inputs for damage types 1 to 3 as chosen in the first. Display all the forms in your template:
<form> {% csrf_token %}
{{damage_type_form.as_p}}
<div id="type1">
{{damage_form_1.as_p}}
</div>
<div id="type2">
{{damage_form_2.as_p}}
</div>
<div id="type3">
{{damage_form_3.as_p}}
</div>
<input type="submit" ...>
</form>
(obviously, pass the four forms in your context, as you'd normally pass just one)
In your view POST processing, you will use the damage_type to determine which form is to be validated and used. Bind all four forms (to request.POST), then use the choice to decide which one gets validated and used. Ignore the other two.
(This may be the first time I've actually thought that a Function-based view will probably be easier than using the Class-Based Views)
Finally, write some JS that will hide the <div>s that are not selected in the damage_type pulldown/ ChoiceField.

Related

Django template: using forloop.counter as an index to a list

In a django template, I need to use forloop.counter0 to access an element in a list. For instance:
{% for foo in bar %}
<p>{{data[{{forloop.counter0}}]}}</p>
{%endfor%}
That notation doesn't work - I guet "cannot parse the remainder: [{{forloop.....]"
So... how do I use the variable forloop.counter0 as an index for another variable (data) which is a list in a django template?
EDIT: why I think i need to do that.... Short story is that I have an inlineformset. The formset (let's call it "Discounts") is simple - a description (just a label to show users) and a % discount associated. The user only needs to enter the %. That formset is used within a Customer form. Using inlineformset_factory(Customer, Discounts, ....). There are also 2 foreign keys that must be set in that discount form (to the Customer instance being created, and to the MetaGroup, see below). But those are hidden fields and it's easy to do.
However, the number of forms I need in that formset is determined by the instances in another table (let's call it "MetaGroup"). E.g. metagroupe contains entries for stuff like say Chairs, Tables, Sofa. My "Discounts" inlineformset provides users a way to set the discount % on each of those MetaGroup.
Thus in the template I do:
...
<tbody>
<managmeent forms, error stuff here>
{% for form in formset %}
<td>{{form.field.pourcent}}</td>
That's the forloop in which I need to access the data[fooloop.counter0]. data is provided by the inclusion I use to render that table/snippet.....
EDIT: the accepted answer is solution to the narrow question - which is that I probably shouldn't do that. For the wider issue (e.g. as to why I thought I needed to do this), I ended up using what is detailed here.
Please don't. Django templates are deliberately restricted, not to do this since business logic belongs in the view, not the template.
In the view, you can zip bar and data, so then the view looks like:
def my_view(request):
bar = …
data = …
context = {
'bar_data': zip(bar, data)
}
return render(request, 'some-template.html', context)
and render this with:
{% for foo, datum in bar_data %}
<p>{{ datum }}</p>
{%endfor%}

How to set the default option for a select field using Jinja templates

I need to add default option value to a select field using Jinja templates.
form.py
class TeamForm(Form):
user = SelectField(u'Team Member')
views.py
class myview(request,id):
form = TeamForm(request.form)
members =db.session.query(teams).filter_by(t_id = id).all()
return render_template("members.html",form=form,members=members)
member.html
<table>
{% for member in members%}
<tr>
<td>{{ form.user(class_="form-control",value=user.id) }}</td>
</tr>
{% endfor %}
</table>
The assigned option value is not shown in the output.
I have to loop the select fields depending upon the members in team.
For example, if a team has 3 members, I will display the three select fields and auto select those three members.
You have two questions in play here:
To implement default values in your form, just use the default=(value, label) option for the SelectField class in form.py.
Assume that you want to default to Johnny as your team member and the numerical value of that option is 0. Then you can do something like this using default=(value, label):
class TeamForm(Form):
user = SelectField(u'Team Member', default=(0, "Johnny"))
Just pass in a tuple in the form (value,label). Your values can be int's, strings, whatever. (You're also missing a Submit button, which I assume is accidental.)
I'm not sure what you're doing in your views. You say, "Here I have to loop the select fields depending upon the members in team. For example if team having 3 members, I will display the three select field with auto selected by those three members." Are you saying that you just need to show these to the user in a drop-down field when you render the form, or are you having trouble actually displaying query results to the user when the template is rendered?
Because you have a form in play, I assume you will be submitting data to a server via a POST. A typical view pattern creates a blank form object and passes it to the user when the template is rendered via a GET request. When the user enters data and submits it, then it is sent via a POST request. I also noticed that you used a class for your views. I suggest using a decorator instead for your routing and have it call a function.
If, say, you have three options in play in your form and you want to show them to your user in a drop-down, then you'll want to implement something like this:
form.py:
# Sample Form class; choices can be overridden later.
class TeamForm(Form):
user = SelectField(u'Team Member', default=(0, "Johnny"), choices=[(0, "Johnny"), (1, "Sarah"), (2, "Bill")])
submit= SubmitField('Submit')
view.py:
#app.route('/team', methods=['GET','POST'])
def team_view(id):
user_selection = None
form = TeamForm()
# This code block will only execute on POST
if request.method == 'POST':
if form.validate_on_submit():
user_selection = form.user.data
form.user.data = ''
return redirect(url_for(some-other-page.html))
members =db.session.query(teams).filter_by(t_id = id).all()
# This next line will override the default choices in your form.
form.user.choices = [(member.id, member.name) for member in members]
return render_template("members.html",form=form)
member.html:
Option 1:
<!-- Assume you're using Flask-Bootstrap & WTForms -->
{% import "bootstrap/wtf.html" as wtf %}
<!-- Render your Team form; this is where you call to your form object -->
{{ wtf.quick_form(form) }}
Option 2:
<!-- Manually construct your template -->
<form method="POST" action="/team">
<div>{{ form.user }}</div>
<div>{{ form.submit() }}</div>
</form>
I found a solution that worked perfectly for me.
If you look inside the form object of type SelectField, you will see that Jinja will render as selected the field that is by default.Ex:
type = SelectField('Company Type', choices=type_choices, default=1)
In the example above Jinja will render a select whose option will have a selected value where value=1.
Checking the form object instantiated in my views, I verified that there are three fields to fulfill this condition:
form.type.data = "1" # ---> string
form.type.default = 1 # ---> int
form.type.object_data = 1 # ---> int
So, replacing these three values with data brought from database, inside views, I was able to create a dynamic default select in Jinja, without having to make any changes to my HTML or forms:
form = EnterpriseForm()
result = "query to database"
form.type.data = str(result.id_enterprise_type)
form.type.default = result.id_enterprise_type
form.type.object_data = result.id_enterprise_type
I hope this information helps others.

Can I create multiple model instances with CreateView?

I have a form whose purpose is to let a user add, edit, subtract and reorder songs. JavaScript DOM manipulation lets users add, subtract and reorder songs fields. The reordering is via jQuery UI's sortable interaction. The order of songs is crucial.
HTML field name attribute values are duplicated. I'm not using Django to generate the form.
Assuming there are two songs on submission, Firebug shows the form DOM looking something like this (csrf omitted):
<form method="post" action="/save/">
<ul id="sortable_songs" class="ui-sortable">
<li>
<input type="text" name="title" id="song_txt_1">
<textarea id="more_info_txtarea_1" name="more_info"></textarea>
</li>
<li>
<input type="text" name="title" id="song_txt_2">
<textarea id="more_info_txtarea_2" name="more_info"></textarea>
</li>
</ul>
<button type="submit">save</button>
</form>
Example query string:
title=FOO&more_info=FOO+INFO&title=BAR&more_info=BAR+INFO
The model:
class Song(models.Model):
title = models.CharField(max_length=65)
more_info = models.CharField(max_length=255)
#todo: foreignkey to User
Probably not much data is involved, both in each record and with regard to the number of records per user. Hence I'm assuming it makes sense that, for a given user, when the form is submitted I'll delete all of their song instances in the Song table and create a bunch of new ones according to the form. (As opposed to having to edit existing records and having a db field which indicates song order).
It seems like I shouldn't write my own view so I'm trying Django's generic CreateView, but perhaps unsurprisingly, with the above user input only a model instance with "BAR" and "BAR INFO" is created -no "FOO" instance is made.
Is there a simple way around this?
You should use Formsets, in this way you can manage multiple instances of an object in a simple way, however you need to control some extra variables and format your query string. But all is covered in the documentation.
You will need a order field in your model:
class Song(models.Model):
title = models.CharField(max_length=65)
more_info = models.CharField(max_length=255)
#todo: foreignkey to User
order = models.PositiveIntegerField()
class Meta:
order_by = ['order']
# to ensure database integrity
unique_thogeter = [(user, order)]
you may also need to update your create view,
class SongListCreate(CreateView):
model = Song
def get_context_data(self, *args, **kwargs):
context = super(SongListCreate, self).get_context_data(*args, **kwargs)
# add a song formset to the context
# or a bound formset from validation
return context
def form_valid(self, form):
formset = SongFormset(#post data)
objects = formset.save(commit = False)
objects.update(#user instance,
#and other data you might need like the new orders)
objects.save()
return super(SongListCreate, self).form_valid(form)
This is roughly what you may need to do, working with formsets is quite a bit hard while you get used to.
You may also want to use a custom library to manage the order of the songs easily, like moving up or down, something like django-positions.
Regards.

How to write view from ModelChoiceField response?

I'm new to Django forms and am getting hung up on something that seems like it should be very simple.
I want to create a dropdown selector that directs users to detail pages, one for each year.
In models.py I have:
class Season(models.Model):
year = models.IntegerField(unique = True, max_length=4, verbose_name = "Season (year)")
…
class season_choice(forms.Form):
choice = forms.ModelChoiceField(queryset=Season.objects.all().order_by('year'), empty_label="Season")
class Meta:
model = Season
In my template:
<form action="/season_detail/{{ choice.year }}" method="get">
{{ season_choice.as_p }}
<input type="submit" value="Go" />
</form>
The dropdown selector shows up fine, producing choices formatted like so:
<select id="id_choice" name="choice">
<option selected="selected" value="">Season</option>
<option value="1">1981</option>
<option value="2">1982</option>
<option value="3">1983</option>
…
Choosing and submitting a year, for instance 1983, now takes me to /season_detail/?choice=3 when what I what is something like /season_detail/?choice=1983
I assume I need to write that into views.py, but after reading through the Django docs and searching through the forum here and trying several approaches I'm more confused than ever.
It looks like you're mixing forms.Form and forms.ModelForm in class season_choice based on your use of forms.Form but also declaring a Meta class.
If you need a different form widget than the model default, you can over ride it in the Meta class if using a ModelForm. When using ModelForms it's best practice to explicitly list the fields to be displayed so that future fields (potentially sensitive ones) are not added by default.
class SeasonForm(forms.ModelForm):
class Meta:
model = Season
fields = ['year']
widgets = {
'year': forms.widgets.Select(),
}
Django models also have a Meta class that will allow you to provide a default ordering:
class Season(models.Model):
year = ...
class Meta:
ordering = ['-year']
If you don't want the entire Model class to have that ordering, you can either change this in your view or create a proxy model, then in your form use model = SeasonYearOrdering.
class SeasonYearOrdering(Season):
class Meta:
ordering = ['-year']
proxy = True
Another item of interest is the hard coded url in your template. You can give the urls in your urls.py names. Then in your template, you can reference these names so that if your urls.py path ever changes, your templates refer to the name and not the hard coded path.
So:
<form action="/season_detail/{{ choice.year }}" method="get">
Becomes (season_detail is the name from urls.py):
<form action="{% url "season_detail" choice.year %}" method="get">
You may change the value of option by adding to_field_name='year' to the choice ModelChoicefield in the form.
So you'll get
<option value="1981">1981</option>
<option value="1982">1982</option>
<option value="1983">1983</option>

How to pass a value to Django-Modelform in InputHidden IntegerField without even disturbing visible field in template

I created a Django model with three fields in which one of them is hidden and rest of them are visible
1.CharField
2.IntegerField (Hidden Field)
3.DateTimeField
I made this hidden by using following code :
class HostCreateForm(forms.ModelForm):
class Meta:
model = Host
widgets = {
'Zone_Vale': forms.HiddenInput()
}
To view this form, i am using following code in my temlate:
<form action="" method="POST">
{{ form.as_p }}
<input type="submit" value="{% trans "Add host" %}" />
Everything is fine but i want to pass a variable in this input hidden layer also. I a not getting any idea to do this. I tried to do by customizing the template (refer this link:) https://docs.djangoproject.com/en/dev/topics/forms/ `
For example {{ form.as_p}} will return a form containing two visible field and the data into them is :(what user had entered it) and a hidden field. So, I want to pass a variable to that hidden field from template.html file
It doesn't work also. Need your help
If you feel like being dirty, a fast solution is to populate this field using javascript. The right way should be using the "initial" parameter when instantiating the form object at view.py
Reference: https://docs.djangoproject.com/en/1.3/ref/forms/api/#django.forms.Form.initial
Even being very dirty, some javascript solution like this shoud work too:
(using jQuery. Adapt for your needs)
<script type="text/javascript">
$(document).ready(function(){
$('form input[name$="Zone_Vale"]').val(DESIRED_HIDDEN_VALUE)
})
</script>
But don't be proud of being doing that.
You won't be able to pass a variable to the field at the template level, given how you're rendering the template.
If you really, absolutely must pass a variable to the form at the template level, I would suggest creating a template tag that will:
Resolve your variable from the request context
Instantiate the form class using the variable as part of the initial data for the form
Add the form to the context to be rendered