Processing dynamic MultipleChoiceField in django - django

All the answers I've seen to this so far have confused me.
I've made a form that gets built dynamically depending on a parameter passed in, and questions stored in the database. This all works fine (note: it's not a ModelForm, just a Form).
Now I'm trying to save the user's responses. How can I iterate over their submitted data so I can save it?
The MultipleChoiceFields are confusing me especially. I'm defining them as:
self.fields['question_' + str(question.id)] = forms.MultipleChoiceField(
label=mark_safe(required_tag +
question.label + "<br/>Choose any of the following answers"),
help_text=question.description,
required=question.required,
choices=choices,
widget=widgets.CheckboxSelectMultiple())
When I select several options, the actual posted data is something like:
question_1=5&question_1=6
Will django automatically realise that these are both options on the same form and let me access an iterable somewhere? I was going to do something like:
for field in self.cleaned_data:
print field # save the user's response somehow
but this doesn't work since this will only return question_1 once, even though there were two submitted values.
Answer: The for loop now works as expected if I loop through self.fields instead of self.cleaned_data:
for field in self.fields:
print self.cleaned_data[field]

... this doesn't work ...
Are you sure? Have you tested it? Normally the cleaned_data value for a MultipleChoiceField is a list of the values chosen on the form.
So yes, it only returns question_1 once, but that returned value itself contains multiple values.

Related

Flask-SQLAlchemy : Apply filter only if value to compare is not empty

I'm working on a small web-app that includes a filter with around 10/15 different fields,
since the user doesn't have to fill all the fields in order to be able to post the form, I implemented the below logic in order to avoid filtering the database with empty strings or values and obtain 0 results.
if not form.Price_from.data == "" and not form.Price_from.data == None:
Posts_filtered = Posts_filtered.filter(Post.Price >= form.Price_from.datas)
By doing this, I filter the column Post. Price only if the field form.Price_from field contains a value.
Since I have 10-15 fields, you can imagine that I have a lot of lines doing the same thing and I really do not like it. Since I'd like to add more fields in the future, my question is if there is a more efficient way to perform this action?
Do you think it would be faster to implement it with js? By doing that I should not need to send a post request every time and I'd save time.
Thank you!
you can use a helper function and loop through every field:
def is_filled(raw_data):
try:
value = raw_data[0]
if value == '':
return False
except (IndexError, TypeError):
return False
return True
(use raw_data from the field instead of data for this function)
you can more info here: https://stackoverflow.com/a/47450458/11699898

Django: Add arbitrary additional data to a queryset

I am trying to display a map of my data based on a search. The easiest way to handle the map display would be to serialized the queryset generated by the search, and indeed this works just fine using . However, I'd really like to allow for multiple searches, with the displayed points being shown in a user chosen color. The user chosen color, obviously cannot come from the database, since it is not a property of these objects, so none of the aggregators make sense here.
I have tried simply making a utility class, since what I really need is a somewhat complex join between two model classes that then gets serialized into geojson. However, once I created that utility class, it became evident that I lost a lot of the benefits of having a queryset, especially the ability to easily serialize the data with django-geojson (or natively once I can get 1.8 to run smoothly).
Basically, I want to be able to do something like:
querySet = datumClass.objects.filter(...user submitted search parameters...).annotate(color='blue')
Is this possible at all? It seems like this would be more elegant and would work better than my current solution of a non-model utility class which has some serious serialization issues when I try to use python-geojson to serialize.
The problem is that extra comes with all sorts of warning about usefulness or deprecation... But this works:
.extra(select={'color': "'blue'"})
Notice the double quotes wrapping the string value.
This translates to:
SELECT ('blue') AS "color"
Not quite sure what you are trying to achieve, but you can add extra attributes to your objects iterating over the queryset in the view. These can be accessed from the template.
for object in queryset :
if object.contition = 'a'
object.color = 'blue'
else:
object.color = 'green'
if you have a dictionary that maps fields to values, you can do things like
filter_dictionary = {
'date__lte' : '2014-03-01'
}
qs = DatumClass.objects.filter(**filter_dictionary)
And qs would have all dates less than that date (if it has a date field). So, as a user, I could submit any key, value pairs that you could place in your dictionary.

Django formset - show extra fields only when no initial data set?

I have a page that displays multiple Formsets, each of which has a prefix. The formsets are created using formset_factory the default options, including extra=1. Rows can be added or deleted with JavaScript.
If the user is adding new data, one blank row shows up. Perfect.
If the user has added data but form validation failed, in which case the formset is populated with POST data using MyFormset(data, prefix='o1-formsetname') etc., only the data that they have entered shows up. Again, perfect. (the o1 etc. are dynamically generated, each o corresponds to an "option", and each "option" may have multiple formsets).
However if the user is editing existing data, in which case the view populates the formset using MyFormset(initial=somedata, prefix='o1-formsetname') where somedata is a list of dicts of data that came from a model in the database, an extra blank row is inserted after this data. I don't want a blank row to appear unless the user explicitly adds one using the JavaScript.
Is there any simple way to prevent the formset from showing an extra row if the initial data is set? The reason I'm using initial in the third example is that if I just passed the data in using MyFormset(somedata, prefix='o1-formsetname') I'd have to do an extra step of reformatting all the data into a POSTdata style dict including prefixes for each field, for example o1-formsetname-1-price: x etc., as well as calculating the management form data, which adds a whole load of complication.
One solution could be to intercept the formset before it's sent to the template and manually remove the row, but the extra_forms attribute doesn't seem to be writeable and setting extra to 0 doesn't make any difference. I could also have the JavaScript detect this case and remove the row. However I can't help but think I'm missing something obvious since the behaviour I want is what would seem to be sensible expected behaviour to me.
Thanks.
Use the max_num keyword argument to formset_factory:
MyFormset = formset_factory([...], extra=1, max_num=1)
For more details, check out limiting the maximum number of forms.
One hitch: presumably you want to be able to process more than one blank form. This isn't too hard; just make sure that you don't use the max_num keyword argument in the POST processing side.
I've come up with a solution that works with Django 1.1. I created a subclass of BaseFormSet that overrides the total_form_count method such that, if initial forms exist, the total does not include extra forms. Bit of a hack perhaps, and maybe there's a better solution that I couldn't find, but it works.
class SensibleFormset(BaseFormSet):
def total_form_count(self):
"""Returns the total number of forms in this FormSet."""
if self.data or self.files:
return self.management_form.cleaned_data[TOTAL_FORM_COUNT]
else:
if self.initial_form_count() > 0:
total_forms = self.initial_form_count()
else:
total_forms = self.initial_form_count() + self.extra
if total_forms > self.max_num > 0:
total_forms = self.max_num
return total_forms

Django regex field - unique and without blank spaces

I have a ModelForm, in which I'm having a CharField, which is declared as unique in the Model.
But I have 2 problems:
If I fill in the form with a field having the same name I don't get an error message.
I'd like this field not to contain white spaces.
Is it possible to do that using a ModelForm?
You can do something close to this:
class MyModelForm(forms.ModelForm):
# your field definitions go here
def clean_myuniquefield(self):
# strip all spaces
data = str(self.cleaned_data['myuniquefield']).replace(' ', '')
model = self._meta.model
# check if entry already exists
try:
obj = model.objects.get(myuniquefield=data)
except model.DoesNotExist:
return data
raise forms.ValidationError("Value already exists!")
To get rid of spaces, make a clean_fieldname function to strip the spaces.
http://docs.djangoproject.com/en/dev/ref/forms/validation/#ref-forms-validation
As for uniqueness, also note about the meta-field unique_together. I don't know if you need it, but I didn't know about it until I dug around.
If you really need to do uniqueness checking before trying to add and failing, you can also do that in the clean_* function. However, it might be better to assume that the database will take care of it and fail in a standard way, and just set up your error messages properly. That way, if you change constraints later, it will flow through more easily. And if others have to maintain your code, it will be more standard.
Hope this helps.

How do I change the value of submitted form data using form object and redisplay it?

Essentially I want to sanitize some data a user submits in a form when I redisplay it if there is an error. This is easy to do if I am extracting the data from a form object. I can override the clean() method and manipulate the data. I can also set the .initial value for the first time it is displayed. However, I cannot find a way of manipulating the form data that is going to redisplayed on error. For example, say a user submits a phone number of "123 456 test test 7890”, I want to be able to strip out the non-alphanumeric characters(that is easy) and show them just the numbers “1234567890” in the form field.
If the data is coming from a request (which is the case if you are using a view) the form.data dictionary will be a QueryDict which is supposed to be immutable. Thankfully you can hack your way into changing it by copying it first:
self.data = self.data.copy()
self.data['phone_number'] = 1234567890
If you are changing directly a form instance that is not from a view's request, you can change the form.data dictionary (it's a simple dictionary object this way) directly like so:
# Don't need to copy `data` first
self.data['phone_numer'] = 123456789