class form(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
u = User.objects.get(user=user)
super(form, self).__init__(*args, **kwargs)
self.fields['phone'].queryset = Phone.objects.filter(user=u)
class Meta:
model = MyModel
fields = ["name", "description", , "phone"]
This form pre-populates the field phones with phones that belong to the currently logged in user. I got the template to display using {{ form.as_p }}, but I dont know how to manually display the fields with the pre-populated phone names to be chosen, in an html template.
I tried the snippet below as part of the bigger form, but it did not work.
<select multiple="multiple" id="id_phone" name="phone" required>
{% for p in phone %}
<option value="{{ p.id }}">{{ p.name }}</option>
{% endfor %}
</select>
Also, how can I allow a user to choose multiple phones at once with this pre-population method. I tried to use ModelMultipleChoiceField but it did not work.
There are two things that you need in this situation
Set an initial value for the field
Set the field widget to be able to select multiple values
Luckily, Django already has a widget for that!
The following code will achieve what you want:
class form(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
u = User.objects.get(user=user)
kwargs["initial"].update({
"phone": Phone.objects.filter(user=u)
})
super(form, self).__init__(*args, **kwargs)
class Meta:
model = MyModel
widgets = {
"phone": SelectMultiple
}
fields = ["name", "description", "phone"]
With these two pieces in place, the template becomes easy!
{{ form.phone.errors }} {# shows on page any errors during clean #}
{{ form.phone }} {# renders the widget #}
If you do not want to use the built in widget that Django provides, look into creating your own
For other ways to set initial values checkout this post
Related
I have a Django form in which for some fields I pull choices from my database, shown below.
I have another field in which the user has to manually type in the data. Is it possible to give that field choices that are present from the database and an option to enter the data if the choice isn't there?
Example: The field name is player and I would like to have all the players already in database as options to choose from and if it is a new player, the option to enter it.
Is that what a TypedChoiceField is? I have been trying to find examples but no luck.
thanks!
forms
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
team_names = TeamName.objects.filter(league__name='MLB').order_by('name')
teams = [(team.id, team.name)for team in team_names]
self.fields['team'].choices = teams
TypedChoiceField is not what you think it is. It simply starts on a default value instead of a "------" blank value.
You could not define choices= on your model. But instead define a list of default choices outside of the model.
my_choices = (
"foo",
"bar",
"pop",
)
class MyModel(models.Model):
my_field = models.CharField(max_length=100)
Then in your view you'd want to import that tuple and pass it to you template:
from my_app.models import my_choices
def my_view(request, *a, **kw):
# view logic
return render(request, "path/to/my/template", choices=my_choices)
Then in your template you can have a select box with the default choices and string values. And also have an optional input type=text that will save to that field if populated.
Something like:
<select name="my_field">
<option value="" selected="selected">-----</option>
{% for choice in choices %}
<option value="{{ choice }}">{{ choice }}</option>
{% endfor %}
</select>
Will give you default choices. Then add an input with the same name, this will act as an optional new choice.
<input type="text" name="my_field"/>
Optionally you could write javascript logic that will ensure only the selectbox or the textfield gets submitted.
I'm looking for a crispy forms version of this:
Django form with choices but also with freetext option?
Models.py
class Animals(models.Model):
animal_id = models.AutoField(primary_key=True)
animal_name = models.CharField(max_length=100, verbose_name='Animal Name')
forms.py
from django import forms
from django.forms import ModelForm
from .models import Animals
class CustomSelection(Field):
template = 'custom_selectbox.html'
class AnimalsForm(ModelForm):
class Meta:
model = Animals
fields = [
'animal_name',
]
def __init__(self, *args, **kwargs):
super(AnimalsForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = True
self.helper.layout = Layout(
Div(
Fieldset(CustomSelection('animal_name'))
FormActions(
Button('submit','Submit')
)
)
)
So if I have:
custom_selectbox.html
{% load crispy_forms_field %}
<div class="form-group">
{{ field.label_tag }}
{% crispy_field field 'class' 'custom-select' %}
</div>
this renders a box OK, which is the first thing I want.
Next, I would like to know if it's possible to somehow inject all of the existing animal_names,
something like this:
{% load crispy_forms_field %}
<input list="options" name="test-field" required="" class="form-control" id="test-field-add">
<datalist id="options">
{% for option in field.subwidgets %}
<option value="{{ option.choice_label }}"/>
{% endfor %}
</datalist>
but with support for crispy-forms. Essentially I want the user to be presented with a list of existing CharField entries - but if the thing they're looking for isn't around, then I want them to be able to add it. Note: it doesn't need to be a charfield - I could do an independent model and use a Foreign Key but if I do that I'm not sure if that means I need to make a separate form to add new entries to every model (the example is animal_name - in reality the form I need to build has lots of equivalent fields). I would accept the reduced control of a charfield over a foreignkey if it is easier for users to add new entries.
I think the datalist approach would work if I'm making the form directly (forgive me, I'm very new to django) but it feels like I'm (hopefully) missing a more django-esque solution. Ideally one that is compatible with my existing crispy-forms. These forms are very large, so I'm using the Layout() helper heavily, so I would really like to avoid re-creating all of this manually.
I have seen this: Django crispy forms work with custom widgets?
but it's very old and I don't understand django well enough to know if the answer still applies.
I have also seen: https://leaverou.github.io/awesomplete/ - this looks equivalent to the datalist approach.
Then finally, https://pypi.org/project/django-awesomplete/ - the only example is for the admin panel so I'm not sure if I can do the same thing with crispy-forms.
I did it using django-floppyforms. I found the solution here: Django form with choices but also with freetext option?
My code:
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('sexo', 'data_nascimento', 'foto', 'sobre_mim', 'telefone', 'paroquia','cidade','estado', 'cep', 'possui_filhos', 'facebook', 'instagram')
widgets = {
'cidade': floppyforms.widgets.Input(datalist=CIDADES, attrs={'autocomplete': 'off'}),
}
Note:
CIDADES is my list containing cities.
atrrs is optional.
I have a forms.ModelForm 'CreateUserForm'.
I want to set a property for each form field to be later used in the template.
In this case, I want to set a icon name to specify which icon name should be used for each field.
class CreateUserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
icon_names = ['person', 'email', 'enhanced_encryption']
class Meta:
model = User
fields = ['username', 'email', 'password']
I've had trouble iterating over both the field AND the field's property 'icon_names'. I can't really zip() without losing functionality.
Currently I've hacked together iteration by using the 'forloop.parentloop.counter'
{% for field in form %}
<div class="form-group">
<div class="input-field">
<i class="icons">
{% for icon in form.icon_names %}
{% if forloop.parentloop.counter == forloop.counter %}
{{ icon }}
{% endif %}
{% endfor %}
</i>
<input type="text" id="autocomplete-input" class="autocomplete">
<label class="control-label" for="autocomplete-input">{{ field.label_tag }}</label>
</div>
</div>
{% endfor %}
Which produces the intended result, but it seems redundant, especially if I wanted to add another field property in the future.
What's the proper way to do this?
One idea would be to pass the zipped list in the context, such as:
context = {'fields_with_icons': zip(form.icon_names, [field for field in form])}
and then
{% for field, icon in fields %}
{{ field }}
{{ icon }}
{% endfor %}
There are two ways i could do this both involving adding an extra html attribute on the fields widget
see age field below, i would use the self.fields to get the field widget and add the extra icon attribute on its attrs dictionary...for this to work you should ensure it comes after the call to super().__init__(*args, **kwargs) others the self.fields will not have been populated....i'd use this when i dont have anything else i need to adjust on the widget class.
https://docs.djangoproject.com/en/2.0/ref/forms/widgets/#styling-widget-instances
see name field below, You could do this on the Meta class https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#overriding-the-default-fields
The form
class PersonForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['age'].widget.attrs['icon'] = 'age'
class Meta:
model = models.Person
fields = ('name', 'age')
widgets = {
'name': forms.TextInput(attrs={'icon': 'email'})
}
And on the template when looping over the fields id get it like this
{% for field in form %}
{{ field.field.widget.attrs.icon }}
{% endfor %}
I need form, where user can select tours based on selected tag. I have implemented it via view, custom html form and some AJAX stuff:
HTML form:
<form id="add-destination" method="post">
{% csrf_token %}
<select name="tag_id" id="tag_id">
<option value=''>---</option>
{% for tag in tags %}
<option value={{ tag.id }}>{{ tag.name }}</option>
{% endfor %}
</select></br>
<select multiple="multiple" name="tours" id="tours">
</select></br>
<button type="submit">Submit</button>
</form>
View to get tours by tag
def get_tours_by_tag(request):
tag_id = Tag.objects.get(id=request.GET.get('tag_id', None))
tours = Inbound.objects.filter(tags=tag_id)
return JsonResponse({"status": "ok", "tours": serializers.serialize("json", list(tours))})
JS:
$(function(){
$('#tag_id').change(function(e){
$('#tours').html('');
e.preventDefault();
$.ajax({
type: 'GET',
data: $(e.currentTarget).serialize(),
url: "/ru/tour/get_tours/",
success: function(data){
var tour_data = JSON.parse(data.tours);
if(data.status=='ok'){
$.each(tour_data, function(i, item) {
$('#tours').append('<option value='+item['pk']+'>'+item['fields']['title']+'</option>');
});
}else{
console.log("Not okay!")
}
}
});
})
});
This way works fine, but I know that it's not right way to solve such problem. And I want to do it by using power of django forms.
So I have model Tours:
class Tour(models.Model):
title = models.CharField(max_length=255)
tags = models.ManyToManyField(Tag)
And form:
class FiveDestinationForm(forms.Form):
tags = forms.ModelChoiceField(queryset=Tag.objects.all())
tours = forms.ModelMultipleChoiceField(queryset=????)
fields = ('tag', 'tours')
I found some close enough questions and tried to solve my problem by overriding __init__ method in form, as it was suggested:
class MyForm(forms.Form):
tags = forms.ModelChoiceField(queryset=Tag.objects.all())
tours = forms.ModelMultipleChoiceField(queryset=Tour.objects.none())
def __init__(self, *args, **kwargs):
qs = kwargs.pop('tours', None)
super(MyForm, self).__init__(*args, **kwargs)
self.fields['tours'].queryset = qs
fields = ('tag', 'tours')
But I get following error:
'NoneType' object has no attribute 'iterator'
So my question is how to properly set queryset for ModelMultipleChoiceField based on what user chosed in ModelChoiceField ?
The error you're seeing is most probably because the Form does not receive a 'tours' argument (it will receive a dictionary named data instead, containing the fields' data in an unvalidated form) and thus the qs variable will be None.
You should probably do something like this:
class MyForm(forms.Form):
tag = forms.ModelChoiceField(queryset=Tag.objects.all())
tours = forms.ModelMultipleChoiceField(queryset=Tour.objects.none())
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
if kwargs['data']:
tag_id = kwargs['data'].get('tag', None)
if tag_id:
self.fields['tours'].queryset = Tour.objects.filter(tag_id=tag_id)
Note that we bypass django's form mechanics a bit here and need to parse the tag_id ourselves. It's not a big deal, though.
From a UX vantage point, this is a rather hard problem to get right without the use of ajax. So I wouldn't completely dismiss your solution either.
In Django admin , does anyone know how can i get the chosen values from FilteredSelectMultiple widget in, when the form is saved?
class ControllerForm(forms.ModelForm):
terminal = forms.ModelMultipleChoiceField(queryset=[])
def __init__(self, *args, **kwargs):
super(ControllerForm, self).__init__(*args, **kwargs)
self.fields['terminal'].widget = widgets.FilteredSelectMultiple('terminals', False)
self.fields['terminal'].help_text = "Select the terminals which are to be added to the group."
self.fields['terminal'].required = False
self.fields['terminal'].label = "Select terminal(s)"
self.fields['terminal'].choices = [(t.id, str(t)) for t in Terminal.objects.filter(associated=False)]
class Meta:
model = Controller
class ControllerAdmin(admin.ModelAdmin):
"""
Controller admin form customization.
"""
list_display = ('name', 'group',)
form = ControllerForm
admin.site.register(Controller, ControllerAdmin)
EDIT:
I think i can access the values in the save_model method. (https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model)
I've reached a solution. Using the save_model method available in the ModelAdmin one can access the chosen objects in the form.cleaned_data dictionary.
def save_model(self, request, obj, form, change):
for terminal in form.cleaned_data['terminal']:
...
obj.save()
Checkout https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model and https://docs.djangoproject.com/en/dev/topics/forms/?from=olddocs for more details on this method.
Thanks
forms.py
class SampleWidget(forms.Form):
date=forms.CharField(widget=AdminDateWidget,max_length=100)
users = forms.ModelMultipleChoiceField(queryset=User.objects.all(),widget=FilteredSelectMultiple(("Users"), False))
mytemple.html
<form action="." method="POST">
{{ form.as_p }}
{{ form.media }}
{% csrf_token %}
<p><input type="submit" value="Submit"></p>
</form>
The widget should post the correct values selected without issue if you have your templates and forms setup like so.
Refer to this one:
This is only example using filteredselectmultiplte widget
http://jayapal-d.blogspot.com/2009/08/reuse-django-admin-filteredselectmultip.html