Django form validation model choices - django

I am having a problem with validating a forms.ChoiceField. I have two tables/models
class City(models.Model):
...
name = models.CharField(max_length=64)
...
class ShoppingCenter(models.Model):
...
name = models.CharField(max_length=64)
city = models.ForeignKey(City)
People can register their activity in a city and in a shopping center. But because there are lots of cities and even more shopping centers, I want to limit the shopping center choices to the selected city. I have created a form with a CharField for the city name and an empty ChoiceField.
After the user has entered a city in the CharField, using ajax/jquery autocomplete, I use ajax/jquery to fill in the values for for the ShoppingCenter ChoiceField.
All that seems to work. I enter a valid city, and the javascript fills in the options for the ShoppingCenter selectfield.
However when I want to validate this form it fails. I am getting:
form_errors
{'shopping_centre_id': [u'Select a valid choice. 1 is not one of the available choices.']}
although 1 is a valid shopping center id, it was selected from a the database.
I have another MultipleChoiceField is the same form and that works fine. The only difference between the two is that the MultipleChoiceField is generated using standard Django and the ChoiceField is created using javascript (ajax + django).
For now I have found a workaround by defining my own clean method in which I remove the error from self._errors and add the shopping_centre_id value to cleaned_data.
Does anyone know what might cause the validation error?

Fill in choices for ChoiceField on form's __init__. You can take a city name from provided data, retrieve a City, filter shopping centers and put them in choices.
But better to use ModelChoiceField for shopping centers, so you will not have to construct choices manually, but just pass a queryset to that field. If there is no data - it will be ShoppingCenter.objects.none(), otherwise - filtered by city.

Related

How to query the database in Django forms?

I have a model:
class Registration(models.Model):
student_name = models.CharField(max_length=50)
season = models.ForeignKey(Season, on_delete=models.CASCADE)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
address = models.TextField()
class Meta:
unique_together = (('student_name', 'selected_season', 'selected_subject'),)
I want to create a form for this model where the user will be given the option to select the subject based on the season that they selected. I have models for them as well:
class Subject(models.Model):
subject_name = models.CharField(max_length=50)
...
class Season(models.Model):
season = models.CharField(max_length=2, primary_key=True)
subject = models.ManyToManyField(Subject)
...
I dont know how to query the Form. Should It be a ModelForm or a regular Form? How to query the database in the form?
You can only know which season was selected when the form is submitted, so there's no simple direct way to implement this (note that this is a HTTP limitation, not a django one). IOW you'll need either a "wizard" process or front-end scripting.
The "wizard" solution is: one first form where the user selects the season, user submits the form, your code selects the related subjects and displays a second form with subjects choices, user selects subjects and submits for final validation (nb: this is usually done within a single view, using a form hidden field to keep track of the current step and which season was selected in first step). This is garanteed to work (if correctly implemented of course xD), but not really user friendly.
Second solution is to use front-end scripting.
In it's simplest form, when the user selects the season, you use js to hide other seasons subjects (or you first hide all subjects and only display relevant ones when the season is selected). This can be done rather simply by grouping all subjects for a given season in a same fieldset (or whatever other container tag) with an id matching the season's one, or by having a distinct html "select" (with same name but different ids) per season. Of course you can also have all subjects in one single html select (or whatever), keep a front-side (js) mapping of seasons=>subjects, and update your select or whatever from this mapping.
This solution can be made to work (in "degraded" mode) without JS (you just have to make sure the subjects selector(s) are visible and active by default). You'll have to implement a custom validation backend-side (cf django's forms doc for this) to make sure the subject matches the season anyway (never trust front-side validation, it's only user-friendly sugar), so in the worst case the form will just not validate.
Now if you have a LOT of seasons and subjects, you may want to prefer doing an ajax query when the user selects the season and populate the subjects selector from this query's result (to avoid initially rendering a huge list of options), but then you can't make it work without JS anymore (whether this is an issue or not depends on your requirements).
EDIT
If i do follow the form wizard option, I need to create 2 forms, the first consisting of just the Season.
Well, you could do it with one single form (passing an argument to specify what should be done) but using two forms is much simpler indeed.
The 2nd form will consist of the rest of the options (except for seasons), am I right? So should the 2nd form be a modelform?
Well, that's the point of modelforms, isn't it ?
How do I put a queryset in the ModelChoiceField in modelform? I googled but could't find anything –
In your form's __init__, you update self.fields["subject"].queryset with your filtered queryset.
IMPORTANT: do NOT try to touch self.fieldname - this is the class-level field definition, so changing it would totally unexpected results in production (been here, done that, and that was quite some fun to debug xD).
Now this being said, for your use case, I reckon you'd be better with the simple js filtering solution - it's the easiest to set up - on the backend it's totally transparent, and the front-end part is rather simple using jQuery -, it's much more user-friendly, and it's garanteed to work.

A Select Widget with fields as checkboxes with multiple options possible

I have the following model:
class Owner(models.Model)
country = models.CharField(max_length=255, choices=COUNTRIES, default=COUNTRIES_DEFAULT)
COUNTRIES is compose of tuples:
COUNTRIES = (
('afg', 'Afghanistan'),
('ala', 'Aland Islands'),
('alb', 'Albania'),
('dza', 'Algeria'),
('asm', 'American Samoa'),
....... )
For FrontEnd, I need to show a Widget, and for each country to have a checkbox.
A Person/User can select multiple Countries.
I presume I need to use a custom widget, but I don't know where to start inherit for Field/Widget and make the query in the database.
--- Why is not a duplicate of Django Multiple Field question ----
I don't need a new Model field, or to store it in the database and is not a many to many relation, so something like the package django-multiselectfield is not useful.
The ModelField is storing just one value, but in the form will appear values from the tuple.I added just to see the correspondence.
Instead I need to be just a Form Field, to get the values, and query the database. Like get all owners that resides in USA and UK.
Also is not looking like Select2, I need to respect design. As functionality is like in the image:
In your Form you must define a MultipleChoiceField with the CheckboxSelectMultiple widget:
countries = forms.MultipleChoiceField(choices=COUNTRIES, widget=forms.CheckboxSelectMultiple)
This will give you a list of multiple choice checkboxes. You can style that yourself to appear with a scrollbar if you don't want to show a long list.
Here is an example from the Django documentation: https://docs.djangoproject.com/en/2.1/ref/forms/widgets/#setting-arguments-for-widgets

django - dynamic query on form field

Lets say I have a form with some fields. I was wondering if it is possible to do dynamic query where I could do a string match while the user is typing into a field. Like when typing into google it returns a list of choices when you type into the search bar. Can someone give me an example on how and where to implement it?
If what you're looking to achieve is fields for a ForeignKey then you can provide a Queryset on the form field then use an app like django_select2 which can then provide a widget which will allow a user to search for items in the Queryset for the field. e.g.
city = forms.ModelChoiceField(
queryset=City.objects.all(),
label=u"City",
widget=ModelSelect2Widget(
model=City,
search_fields=['name__icontains'],
dependent_fields={'country': 'country'},
max_results=500,
)
)
Then in the form, as you started to type into the city field it'd start to search on City.name using an icontains lookup. The docs for this are here.
If that's not what you're looking for then you could just write some Javascript which detected input to a field, then sent an ajax request to a view with the contents of your field. That could then return a response which displayed content in the page related to the input.

How to render a ForeignKey field as CharField in Django forms

My Model
class Collaborator(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
My Form:
class CollaboratorForm(forms.ModelForm):
user = forms.CharField(max_length=30, required=False)
class Meta:
model = Collaborator
fields = ('user',)
The template render the user input text as an autocomplete field loaded from User model.
The page shows the user input text field correctly, however when I go to edit a Collaborator the user input text field shows the user Id and I want to show the username instead.
You want to figure out first what the exact behavior you want is.
Let's say I'm user with id=1, username="Joel". When you produce your CollaboratorForm and it shows "Joel" (my username) on the form and you edit it to read "Jane", are you:
trying to edit my name to "Jane", so that id=1 now has a username=Jane ?
or trying to change the associated user to the user matching that username?
Once you know that, you can proceed.
I'd change the user = forms.CharField(..) to username = forms.CharField(..); that's what your really showing, after all.
In your view, when you create your form, pass it the initial values of {'username':username-of-user-that-is-currently-related}.
In your view, when you process the form, before you save it, use the cleaned_data['username'] to either (a) update the username of that user (if the first case above) or (b) get the ID of the new user and save that to the .user field (if the second case above).
Of course, if what you want is really at a higher-level "let people pick the user based on the username", you might be able to solve this differently and more easily--by not using a CharField at all, and using a ChoiceField instead, so you could show a drop down menu of users with the key=ID and value=name. Depending on how many users you have, though, that might not scale well.

Which Django model field goes with a CheckboxSelectMultiple form field?

I have a django form that allows a user to select multiple options:
CARDS = (
("visa", "Visa"),
("mastercard", "MasterCard"),
)
class PaymentForm(forms.ModelForm):
credit_cards = forms.MultipleChoiceField(choices=CARDS, widget=forms.CheckboxSelectMultiple)
# ... etc.
I have the form's associated model setup as:
class Payment(models.Model):
user = models.OneToOneField(User)
credit_cards = models.CharField(choices=CARDS, max_length=100)
# ... etc.
But I'm thinking that a CharField with the choices parameter can only accept a single choice because my form never validates and I get an error like:
Value u"[u'visa']" is not a valid choice.
And it sure looks like a valid choice.
I've seen that some people get this working with a ManyToManyField on the model side (which I'd expect) but building a model just for a static list of credit card types seems overkill.
So: is there a specific model field type or different form configuration I should be using to support multiple selections from a pre-defined list of options?
Thanks.
http://djangosnippets.org/snippets/1200/