Django autocomplete_light and cities_light - invalid choice - python

I have a model with a location field that is mapped to cities_light.city and I'm using an autocomplete field that allows users to type in their city and have it autocompleted to the correct/valid location model instance.
class Profile(models.Model):
location = models.ForeignKey(City, blank=True, null=True)
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ('location')
widgets = {
'location': autocomplete_light.TextWidget(CityAutocomplete, autocomplete_js_attributes={'placeholder':'City, Country', 'minimum_characters':4})
}
The form field works exactly as advertised and a list of autocomplete options are shown. However when I save the form/model I get a validation error which seems to be caused by the field not being translated into the primary key of the City model instance.
Select a valid choice. That choice is not one of the available choices.
I'm guessing I need to extend the AutocompleteModelBase like the CityAutocomplete implemented below but I'm not sure how and I've been unable to find a working example.
class CityAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ('search_names',)
https://github.com/yourlabs/django-cities-light/blob/master/cities_light/contrib/autocompletes.py
Thank for any assistance and I'm sorry if my question is poorly formatted.

Your problem is not specific to django-autocomplete-light. What you're doing has no chance to work and here's why:
the form field for a ForeignKey like location is a ModelChoiceField by default,
ModelChoiceField accepts values which are pks of models in ModelChoiceField.queryset, which is TheModel.objects.all() by default,
the TextWidget widget is a TextInput,
a TextInput widget is just an <input type="text" />,
the value of a <input type="text" /> is directly sent to the server on form submission.
As a result, selecting a couple of cities like "Lisboa" and "Madrid" with a text widget will look like::
<input type="text" value="Lisboa, Madrid" name="location" />
Which means that the form will post {'location': 'Lisboa, Madrid'}. While this is good for a CharField, it won't work for a ModelMultipleChoiceField which would expect something like {'location': [3,5]} where 3 would be the pk of Lisboa and 5 the pk of Madrid.
In the same fashion, a ModelChoiceField would expect {'location': 3} which autocomplete_light.ChoiceWidget is able to do.
To fix this, use a ChoiceWidget instead of a TextWidget. I have clarified this in the tutorial I hope it is better now.

Related

How to get WTForms to require entry into a RadioButton field?

I'm creating a web form using Flask and Flask-WTF. The form is dynamic, with the fields determined by the database. Some questions in the database specify a radio button field, while others specify a single word.
I need to require that the radio button fields have a selection. Using wtforms.validators.DataRequired works perfectly for the string fields, but not the radio fields.
...
if question.category == 'word':
field = StringField(question.question, validators=[DataRequired()])
elif question.category == 'likert':
choices = [('1', 'Strongly Agree'), ('2', ...
field = RadioField(question.question, choices=choices,
validators=[DataRequired()])
setattr(FlaskForm, str(question.id), field)
stator(FlaskForm, 'submit', SubmitField('Submit))
form = FlaskForm()
....
Leaving any string field blank results in an error message when submitted. But nothing happens if a radio field is left without a selection.
How can I correct this?
I still don't know why DataRequired (and InputRequired) don't properly work with radio fields, but here's how I solved it.
I subclassed FlaskForm as follows:
class SurveyForm(FlaskForm):
class Meta:
def render_field(self, field, render_kw):
render_kw.setdefault('required', True)
return super().render_field(field, render_kw)
Using adding the dynamic fields to an instance of this subclass then made even the radio fields require an input.

Django form validation in template

I have a Django project with many standard HTML forms.
And now I want to make another form to upload documents. I found solution using django-forms. So for me it is just a simple class:
class DocumentForm(forms.Form):
docfile = forms.FileField(
label='',
)
description = forms.CharField(
label=u'Description:',
widget=forms.Textarea(attrs={'rows':4, 'cols':50}),
)
I want to add emptyness check to Textarea field. There is a possibility to add "required" parameter in CharField constructor. But in this case page will allow POST and I should write extra code after "form.is_valid()" check and use "form.description.errors" to display my error message.
And I want behaviour similar to my other forms. Something like
<textarea name="action" required rows="4" cols="50"></textarea>
In this case I can't even do POST without non-empty textarea. And more important I have error display as I like.
Is there a possibility to make such form with django-forms? Also is there a possibility to make similar behaviour to FileField?
You can add the required attribute just like you have added rows or cols:
class DocumentForm(forms.Form):
docfile = forms.FileField(
label='',
required=True, # just in case someone doesn't submit using form
widget=forms.ClearableFileInput(attrs={'required': 'required'}),
)
description = forms.CharField(
label=u'Description:',
required=True, # just in case someone doesn't submit using form
widget=forms.Textarea(attrs={'rows': 4, 'cols': 50, 'required': 'required'}),
)
Please note the use of forms.ClearableFileInput widget for FileField because that is what's defined as the default widget for FileField.

Django ModelChoiceField allow objects creation

Django's ModelChoiceField is the default form field used for foreign keys when deriving a form from a model using ModelForm. Upon validation, the field will check that selected value does exist in the corresponding related table, and raise a ValidationError if it is not the case.
I'm creating a form for a Document model that has a type field, a foreign key to a Type model which does only contain a name attribute. Here is the code of models.py for clarity
class Type(models.Model):
name = models.CharField(max_length=32)
class Document(models.Model):
title = models.CharField(max_length=256)
type = models.ForeignKey(Type, related_name='related_documents')
Instead of a standard select control, I'm using selectize.js to provide auto-completion to the users. Moreover, selectize provides a "create" option, that allows to enter a value that does not exist yet into the select.
I would like to extend the ModelChoiceField in order to create a new Type object when the selected value does not exist (the new value will be assigned to name field, this should be an option of the field for reusability). If possible, I would like the object to not be inserted into DB until save() is called on the validated form (to prevent that multiple failed validation create multiple rows in db). What would be a good way to do so in Django? I tried to look into the documentation and the source code, tried to override ModelChoiceField and tried to build this behavior basted on a TextField but I'm not sure if there isn't a simpler way to do it.
I looked into the following questions but couldn't find the answer.
Django ModelChoiceField has no plus button
Django: override RelatedFieldWidgetWrapper
How to set initial value in a dynamic Django ModelChoiceField
I would like to keep the process of adding new types as simple as possible - i.e.: do not use a pop-up attached to a '+' button. User should be able to type the value, and the value gets created if it doesn't exist.
Thanks
This seems like it'd be easier without using a ModelForm. You could create a list of all of your Type objects then pass that to the template as a context variable. Then, you could use that list to construct a <select> element. Then use jquery and selectize to add the necessary attributes to the form.
#views.py
...
types = Type.objects.all()
...
#template.html
...
<form action="" method="POST">{% csrf_token %}
<input type='text' name='title'>
<select name='type' id='id_type'>
{% for type in types %}
<option value="{{type.id}}">{{type.name}}</option>
{% endfor %}
</select>
<input type='submit'>
</form>
<script type='text/javascript'>
$('#id_type').selectize({
create: true
});
</script>
...
Then when you get a form submission, you can process it in a simple view function:
if request.method == POST:
title_post = request.POST.get('title','')
type_post = request.POST.get('type',None)
if type_post is not None:
try:
#If an existing Type was selected, we'll have an ID to lookup
type = Type.objects.get(id=type_post)
except:
#If that lookup failed, we've got a user-inputted name to use
type = Type.objects.create(name=type_post)
new_doc = Document.objects.create(title=title_post, type=type)

Custom form fields in Django

I know how to create forms in forms.py and views.py using models.py, but to want to create a birthdate field. As everyone knows we can do this birthdate = models.DateTimeField(). However, the issue is the user would have to input their birthdate by typing YYYY-MM-DD-HH-MM-SS.
So does anyone here have an elegant way for users to input their birthday?
Thanks
I believe you are looking for a nicer DateTimeWidget, not the field itself. Secondly, unless it's really necesary, you would more likely use a DateField rather than DateTimeField to record user's birthdate ;)
here's just a one example of custom datetime input widget, but there are more:
https://github.com/asaglimbeni/django-datetime-widget
in the forms, you would do:
birthdate = forms.DateTimeField(widget= ...)
cheers
Of course. Tie a date-picker to the birthday input.
See jQuery UI's datepicker widget.
The approach is:
User picks a date from datepicker widget.
The brithday input get's automatically populated as per the selected date.
After form submission, the value in the input goes to the backend.
UPDATE:
Example:
In your template:
<form action=".">
...
{{ form.birthdate }}
...
</form>
<script>
$(function() {
$( "#id_birthdate" ).datepicker({
changeYear: true,
changeMonth: true,
});
});
</script>
The id of the birthdate input is definitely going to be id_birthdate, because that is how Django assigns ids to form fields, so that is what you need to pass in the script's function.
Also, include jqueryui.js in your project.
You could use models.DateField and custom DATE_INPUT_FORMATS if needed:
https://docs.djangoproject.com/en/dev/ref/settings/#date-input-formats

Django add single fields to Model form using AJAX call

I have a Model form. The form contains a button "Add more Field". Clicking this button sends an AJAX call which should add a textfield to the form. Any number of "textfields" can be added.
I am not sure if Django-Formsets is the correct way to do it as I need to store the data of "extra added fields" in the same Model Form's Table in the database.
How can I achieve this ?
I did something similar to this recently, and my solution was to subclass the form dynamically, providing only the field needed, and rendering just that field:
from forms import SomeModelForm
from models import SomeModel
def view_name(request,pk,field):
SomeModelFieldForm(SomeModelForm):
class Meta(SomeModelForm.Meta):
fields = (field,)
inst = SomeModel.objects.get(pk=pk)
form = SomeModelFieldForm(instance=inst)
#the rest of your view goes here...
#...send only the form's field:
ctx = {'field': form[field]}
return render_to_response("template",ctx)
This takes advantage of your original form's specifics - i.e., if you have specially defined widgets, or other restrictions, or something. It then restricts the entire form to a single field. This is to allow the validation of a single field on an existing model.
However, you don't want to send the entire form, you only want to send the single field. form[field] is a django.forms.forms.BoundField that represents the specific field you pass in.
For example, if you were working with the User model from django.contrib.auth.models, and you created a form for a specifi user, in idle calling form["username"] would return:
<django.forms.forms.BoundField object at 0x01354750>
and calling print form["username"] would print:
<input id="id_username" type="text" name="username" value="name_of_user" maxlength="30" />

Categories