I am trying to work out the best way to implement a form in django that has two choice fields on it, one of which affects the choices available in the other. An example - form field one is a radio button (can choose only one option) called 'cuisine', and the second is a multichoice field called 'menu'. If you choose 'french' from 'cuisine' then you get french dishes in the menu list, but if you choose 'chinese' you get a different selection.
How do I work this server-side in the form validation process. How do I 'bind' the two controls so that only dishes related to the cuisine option are accepted?
And how do I render this - should I pass in a ModelForm for each type of cuisine, or have a single menu ModelForm that has everything in it, and just show/hide stuff on the client-side?
All of the menu options are stored in the db and loaded in as fixtures, and the cuisines are hard-coded into the app:
CUISINE = ((0,'French'),(1,'Chinese'),(2,'Italian'))
class MenuItem(models.Model):
description = models.CharField(max_length=200)
cuisine = models.IntegerField('Cuisine', choices=CUISINE)
For rendering, you can use django-selectable or django-autocomplete-light,
For server side validation, django has it completely documented.
Related
New to using WTForms, and I can't figure out how to do expose additional form fields as shown here: https://css-tricks.com/exposing-form-fields-radio-button-css/
Because radio fields have the choices parameter where a list of fields is provided, is there any way to customize each of those choices? Like have something different be exposed when different choices are selected?
This is going to be a mix of python and javascript code.
There are 2 common solutions:
Everything is already in the webpages and the unused fields are hidden until there is an action from the user (on the radio button).
Make API calls to get dynamic custom choices.
You can create a route:
#app.route("/api/choices", methods=["GET"])
def my route:
custom_choices = get_custom_choices()
return jsonify(custom_choices)
Then in your javascript code, when the user clicks on a button, you make a request to the this route to get the custom choices.
Then when you check/validate the sent form, populate the choices of the field with the custom choices (in the example, get_custom_choice) as described in the WTForms documentation here:
Note that the choices keyword is only evaluated once, so if you want
to make a dynamic drop-down list, you’ll want to assign the choices
list to the field after instantiation. Any submitted choices which are
not in the given choices list will cause validation on the field to
fail. If this option cannot be applied to your problem you may wish to
skip choice validation (see below).
Select fields with dynamic choice values:
class UserDetails(Form):
group_id = SelectField('Group', coerce=int)
def edit_user(request, id):
user = User.query.get(id)
form = UserDetails(request.POST, obj=user)
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
Note we didn’t pass a choices to the SelectField constructor, but
rather created the list in the view function. Also, the coerce keyword
arg to SelectField says that we use int() to coerce form data. The
default coerce is str().
Source
The second solution requires more work, but it's lighter depending on how many different possibilities ou have for the choice. (And in my opinion: cleaner).
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.
I have model Tech, with name(Charfield) and firm(ForeignKey to model Firm), because one Tech(for example, smartphone) can have many firms(for example Samsung, apple, etc.)
How can I create filter in admin panel for when I creating model, If I choose 'smartphone' in tech field, it show me in firm field only smartphone firms? Coz if I have more than one value in firm field (for example Apple, Samsung, IBM), it show me all of it. But IBM must show only if in tech field I choose 'computer'. How release it?
class MyModelName(admin.ModelAdmin):
list_filter = (field1,field3,....)
refer:-
https://docs.djangoproject.com/en/2.1/ref/contrib/admin/
You can define the choices of the input with the attribute 'choices' of the widget. When you create the admin form of the model, you could define manually the fields, and also you can define the widget for each input. In the widget you could define with a tuple the choices and the initial values.
I am using materializecss to give my django site some material elements. I have put together a form (the 'old' way using html) but now realised I need to use a django form instead. The problem is, these forms don't play well with materialises built in column system (they use classes to determine rows and column spacing). Here is an example of the layout I set up so far. However when defining the form through form.py, it spits out one input per layer.
My question is: what can I do to either a) get django to work with the html-defined form or b) make a 'form template' to give the input fields the appropriate classes?
If you want to see the code I can post some but I'm quite a new coder so it's messy.
Thanks!
There are three ways I can think of off the top of my head.
If you want full control over the HTML form, in a Django template or HTML form, simply map the names of your fields to match the underlying field names in the Django form. This way, when POSTed back to your view, Django will automatically link up the POSTed fields with the Django form fields.
For example, if you have a field username in your Django form (or Django model if using ModelForm), you could have an element <input type="text" name="username" maxlength="40"> (that you can style any way you need) on your HTML form that Django will happily parse into your Django form field, assuming your view is plumbed correctly. There is an example of this method in the Django documentation.
Another way is to customize the Django form field widgets in your Django form definition. The Django documentation talks a little bit about how to do this. This is great for one offs, but is probably not the best approach if you expect to reuse widgets.
The final approach would be to subclass Django form field widgets to automatically provide whatever attributes you need. For example, we use Bootstrap and have subclassed nearly all of the widgets we use to take advantage of Bootstrap classes.
class BootstrapTextInput(forms.TextInput):
def __init__(self, attrs=None):
final_attrs = {'class': 'form-control'}
if attrs is not None:
final_attrs.update(attrs)
super().__init__(attrs=final_attrs)
Then it's simply a matter of letting the Django form know which widget to use for your form field.
class UsernameForm(forms.ModelForm):
class Meta:
model = auth.get_user_model()
fields = ['username']
widgets = {'username': BootstrapTextInput()}
Hope this helps. Cheers!
I'd like to create a confirmation page for selected objects before a change is made to them (outside the admin). The objects can be of different models (but only one model a time).
This is much like what is done in administration before deletion. But the admin code is complex and I haven't grasped how it is done there.
First I have severall forms that filter the objects differently and then I pass the queryset to the action / confirmation page. I have created a form factory so that I can define different querysets depending on model (as seen in another similiar question here at Stackoverflow):
def action_factory(queryset):
''' Form factory that returns a form that allows user to change status on commissions (sale, lead or click)
'''
class _ActionForm(forms.Form):
items = forms.ModelMultipleChoiceField(queryset = queryset, widget=forms.HiddenInput())
actions = forms.ChoiceField(choices=(('A', 'Approve'), ('D' ,'Deny'), ('W' ,'Under review'), ('C' ,'Closed')))
return _ActionForm
Which I use in my view:
context['form']=action_factory(queryset)()
The problem is that the items field wont be displayed at all in the html-code when it is hidden. When I remove the HiddenInput widget it displays the form correctly.
I don't want to display the choice field since there can be thousands of objects. All I want to have is something like "Do you want to change the status of 1000 objects" and a popdown and a submit button. A simple enough problem it seems, but I can't get it to work.
If someone has a solution to my current attempt I would be glad to hear how they have done it. Even better would be if there is a cleaner and better solution.
I used the wrong widget. It should be MultipleHiddenInput not HiddenInput.