DJANGO: Add "select all" choice option to ModelMultipleChoiceField? - python

Is there a way to add additional choices to a ModelMultipleChoiceField? Specifically, I want to add a "Select All" option instead of an End-User having to select all of the choices individually.
Current Model Form:
class QueryForm(forms.Form):
book = forms.ModelMultipleChoiceField(queryset=Book.objects.all())
I want something like this:
class QueryForm(forms.Form):
book = forms.ModelMultipleChoiceField(choices=(('Select All', 'Select All'),(Book.objects.all()))
How can I add the Select All option to Model Multiple Choice Field?

Is this in the Django admin or in your own form? If it's the admin, the easiest solution is to simply add the field to ModelAdmin.filter_horizontal or ModelAdmin.filter_vertical. The special field that the Django admin will use has a "Choose All" option builtin.
If it's your own form, JS is your best bet as #AdamKG recommends. Given the following:
<div class="field">
<select multiple="multiple" name="my_field">
<option value="something">Something</option>
...
</select>
Select All
</div>
This bit of jQuery should do the trick:
$('.select-all').click(function(){
$('option', $(this).parent()).attr('selected', 'selected');
});

You will need to convert your ModelMultipleChoiceField to a MultipleChoiceField that gets its choices from the related model in __init__(). Add a "Select All" option with a sentinel (e.g. "all") instead of the PK that you will get from the actual models. Then just detect this in the POST view and set things appropriately.

Related

How to display a dropdown list in Django form?

I have an issue with the display of a dropdown list, which is a field part of a Django form. Instead of a normal dropdown list, it appears like a kind of multiple select choice box (at least, it's high as this kind of object, as you will see in below screenshots), with the feature of a dropdown (the small arrow that opens the choice list).
I do not understand why it looks like this and how to solve this.
Edit
I pushed my current version into production, for tests and demo purposes, and surprisingly, it works, the dropdown displays properly (and still ugly in local environment)
If anyone has an explanation/solution for that, he is welcome
As far as possible I use standard objects (I'm not very comfortable with CSS) and, in this case, I did not manage to update anything (event setting height had no impact, maybe there is something wrong in this part too)
And I can understand my question is not perfect, but please explain me what's wrong to allow me to add missing information (and I still do not understand why we cannot thanks in advance people who will read and try to solve problems here, but it's another question... that will probably be edited without any explanation or comment)
Related model field is defined like this:
class Company(models.Model):
"""
Company informations
- Detailed information for display purposes in the application
but also used in documents built and sent by the application
- Mail information to be able to send emails
"""
company_name = models.CharField("nom", max_length=200)
comp_slug = models.SlugField("slug")
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")] # Default management rule
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
The form has no dedicated rules, even if tried to add some (kept as comment in the code below):
class CompanyForm(forms.ModelForm):
company_name = forms.CharField(label="Société", disabled=True)
# rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
# rule = forms.ChoiceField(label="Mode de scrutin", choices=rules)
class Meta:
model = Company
exclude = []
Here is the view:
#user_passes_test(lambda u: u.is_superuser or u.usercomp.is_admin)
def adm_options(request, comp_slug):
'''
Manage Company options
'''
company = Company.get_company(comp_slug)
comp_form = CompanyForm(request.POST or None, instance=company)
if request.method == "POST":
if comp_form.is_valid():
comp_form.save()
return render(request, "polls/adm_options.html", locals())
And the part of HTML code:
<div class="row border mt-4">
<div class="col-sm-12">
<h5>Préférences de l'application</h5>
<div class="row">
<div class="col-sm-5 mt-2">
{{comp_form.use_groups}} <label for="{{comp_form.use_groups.label}}">{{comp_form.use_groups.label}}
</div>
<div class="col-sm-7 mt-2">
<p><label for="{{comp_form.rule.label}}">{{comp_form.rule.label}}</label> : {{comp_form.rule}}</p>
<p>{{comp_form.upd_rule}} <label for="{{comp_form.use_groups.label}}">{{comp_form.upd_rule.label}}</p>
</div>
</div>
</div>
</div>
The concern is the format of the field:
When the user click on the arrow, here is the display (there are only 2 options):
What did I wrong?
How can I change this (in views or HTML/CSS)?
In your Company model you have only used two choices:
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
When rendering this model form in HTML there should be only two options. If you want may options you should modify your model.

Add editable choices in django

I am trying to add a editable choicefield in django like the picture, already do some research on this. Unfortunately, the solution like django-autocomplete doesn't quite fulfill my needs. The autocomplete looks fine, but if do so, I need create a django modal to generate the choices through a url view request, but in my case, it doesn't necessary to do that, I just need put these 3 choices for ip address in the dropdown list, and choose one of them then edit it or submit.
The solutions I found, but they are not very well fit my need:
Django admin: Change selected box of related fields to autocomplete
Django editable dropdown field
If you want to use just those three entries the quickest way is to use a datalist, it just needs to be added into your HTML somewhere.
<div id="page-wrapper">
<label for="ip">Network address</label>
<input type="text" id="ip" list="ip-datalist">
<datalist id="ip-datalist">
<option>192.168.0.0/24</option>
<option>10.0.0.0/24</option>
<option>172.16.0.0/24</option>
</datalist>
</div>
If your options are likely to never change, you could hardcode them into your html like the answer above. If you're generating your dropdown using a form however it would be better to do something like this:
class MyForm(forms.Form):
ip_list = ((1, '192.168.0.0/24'), (2, '10.0.0.0/24'),
(3, '172.16.0.0/24'), )
network_address = forms.ChoiceField(label='Network Address',
choices=ip_list)
...
Once you render the form object in your template it ends up producing html like this:
<label for="id_network_address">Network Address:</label>
<select id="id_network_address" name="network_address">
<option value="1">192.168.0.0/24</option>
<option value="2">10.0.0.0/24</option>
<option value="3">172.16.0.0/24</option>
</select>
This way it is easier to change the ip_list in future if you need to, plus its keeps all your code in one place. This is explained in the docs here.

dynamic forms in django with ajax/dajax

I have this simple form with a queryset to display job objects and allow the user to select one. Now, i would like to have a group of two radio buttons that would allow the user to select a 'filtering option'. If the user selects a group filter with the radio buttons, i would a drop down to appear that allows them to select which group they would like to filter on. I want to do this without having to reload the page, so i'm attempting to use dajax.
I was able to find an example on the dajax website that does something similiar, however, i was not able to get it to work. The radio buttons appear and the drop down appears(empty). When i click on the "Group" radio button, the drop-down should populate with all my Group objects, but it doesnt.
I've been stuck on this for awhile, so any help would be greatly appreciated.
forms.py
filters = (('0', 'Group'),
('1', 'Host'),
)
class JobSelectForm(forms.Form):
def __init__(self, *args, **kwargs):
super(JobSelectForm, self).__init__(*args, **kwargs)
self.fields['jobs'].widget.attrs["size"] = 20
jobs = forms.ModelChoiceField(queryset=Job.objects.all().order_by('name'), empty_label=None,)
filter = forms.ChoiceField(choices=filters,
widget=forms.RadioSelect(attrs={'onchange': "Dajaxice.tdportal.updatefilter(Dajax.process,{'option':this.value})", 'name':'combo1', 'id':'combo1', },
renderer=HorizRadioRenderer),
ajax.py
def updatefilter(request, option):
dajax = Dajax()
options = [Group.objects.all(),
Host.objects.all(),
]
out = ""
for o in options[int(option)]:
out = "%s<option value='#'>%s" % (out,o,)
dajax.assign('#combo2','innerHTML',out)
return dajax.json()
dajaxice_functions.register(updatefilter)
template
{{selectForm.filter.label}}: {{selectForm.filter}}
<br>
<select name="combo2" id="combo2" onchange="" size="1"></select>
<br><br>
<form method="post" action="/tdportal/jobs/">{% csrf_token %}
{{selectForm.jobs}}
<br><br>
<input type="submit" value="Edit" /> <input type="button" name="new" value="New" />
</form>
You need to add your dajaxice function to the select onchange to reference your function in the template.
Something like:
<select name="combo2" id="combo2" onchange="Dajaxice.your_project.your_appname.updatefilter(Dajax.process,{'option':this.value}" size="1"></select>
(Replace your_project with your project name, your_app with your appname).
Also make sure in your template you have all the necessary headers (and that they actually are there; e.g., if you view the source that you can click to them).
Be sure to debug using google chrome inspector (Ctrl-Shift-I) or firebug (in firefox).
EDIT: I now noticed that you have something similar that's in your form. How does it render in the HTML when you view the source? Is it improperly escaped?
I think you forgot a % sign (value='#') and a closing </option> in here:
out = "%s<option value='%s'>%s</option>" % (out,o,o)

How to get Django form field from model field?

I'd like to create a form that includes fields from two separate models, along with some other regular (non-model) fields. The form will create an instance of each model. I don't think I can use inline formsets for this, since I don't want to include all the fields from both models.
I'd like to create the form field without hard-coding the type of the model fields.
I know I can get a form field from a model field using model_field.formfield(). But how can I get the specific model field?
My first solution:
def get_fields(model_class):
fields = {}
for f in model_class._meta.fields:
fields[f.name] = f
class MyForm(forms.Form):
foo_name = get_fields(Foo)['name'].formfield()
bar_name = get_fields(Bar)['name'].formfield()
other_field = ...
Is there an equivalent of get_fields already? Is this a bad idea? I'm uncomfortable relying on the model _meta attribute. Or am I going about this the completely wrong way?
You also can take a look at django.forms.models.fields_for_model.
That should give you a dictionary of fields, and then you can add the fields of the form
You should never have to build the fields yourself unless you want some special behavior.
This should be as simple as using two ModelForms and an extra Form inside one <form> tag in your template with one submit button.
in forms.py:
class Model1Form(forms.ModelForm):
class Meta:
model = Model1
fields = ('fields', 'you', 'want')
class Model2Form(forms.ModelForm):
class Meta:
model = Model2
fields = ('fields', 'you', 'want')
class ExtraFieldsForm(forms.Form):
extra_field = form.TextField() # or whatever field you're looking for
in views.py:
form1 = Model1Form(request.POST or None)
form2 = Model2Form(request.POST or None)
form3 = ExtraFieldsForm(request.POST or None)
if form1.is_valid() and form2.is_valid() and form3.is_valid():
form1.save()
form2.save()
form3.save()
...do other stuff like a redirect...
and in the template:
<form method="POST" action="">{% csrf_token %}
<fieldset>
{{ form1|as_uni_form }}
{{ form2|as_uni_form }}
{{ form3|as_uni_form }}
<div class="form_block">
<input type="submit" value="Save both models"/>
</div>
</fieldset>
</form>
I'm used to using django-uni-form, but you can render the form fields however you like. Good luck with your site.
There is now a documented API for getting the model field from a model class:
my_model_field = MyModel._meta.get_field('my_model_field_name')
Although it's not officially documented until Django 1.8, this should work with earlier Django versions too.
Once you have this, you can get the form field like so:
form_field = my_model_field.formfield()
Another solution can be to create one 'uber'-form that aggregates the concrete modelforms. The form supports the methods that a form normally provides and it forward them to all the child forms. Some will be simple, other complicated. The big advantage of that approach is that no code beyond the form is affected (client validation code and alike). The concept isn't really revolutionary but i guess complicated to add afterwards.
Paul
It's still undocumented as far as I know, but since Django 3.0 you can make a form field from a single model field called field_name with:
MyModel.field_name.field.formfield()
If your model is a parler.models.TranslatableModel, and the field_name is inside translations, you can use:
MyModel.translations.field.model.field_name.field.formfield()
Or, if you prefer a bit more verbose, but better documented ways, you can use:
MyModel._meta.get_field("field_name").formfield()
and
MyModel._parler_meta.get_model_by_related_name("translations")._meta.get_field("field_name").formfield()
See:
Django get_field
django-parler get_model_by_related_name

How do I add data to an existing model in Django?

Currently, I am writing up a bit of a product-based CMS as my first project.
Here is my question. How can I add additional data (products) to my Product model?
I have added '/admin/products/add' to my urls.py, but I don't really know where to go from there. How would i build both my view and my template? Please keep in mind that I don't really know all that much Python, and i am very new to Django
How can I do this all without using this existing django admin interface.
You will want to wire your URL to the Django create_object generic view, and pass it either "model" (the model you want to create) or "form_class" (a customized ModelForm class). There are a number of other arguments you can also pass to override default behaviors.
Sample URLconf for the simplest case:
from django.conf.urls.defaults import *
from django.views.generic.create_update import create_object
from my_products_app.models import Product
urlpatterns = patterns('',
url(r'^admin/products/add/$', create_object, {'model': Product}))
Your template will get the context variable "form", which you just need to wrap in a <form> tag and add a submit button. The simplest working template (by default should go in "my_products_app/product_form.html"):
<form action="." method="POST">
{{ form }}
<input type="submit" name="submit" value="add">
</form>
Note that your Product model must have a get_absolute_url method, or else you must pass in the post_save_redirect parameter to the view. Otherwise it won't know where to redirect to after save.
This topic is covered in Django tutorials.
Follow the Django tutorial for setting up the "admin" part of an application. This will allow you to modify your database.
Django Admin Setup
Alternatively, you can just connect directly to the database using the standard tools for whatever database type you are using.

Categories

Resources