I need to make a selector with genders (male and female) with the respectives counts of each type.
For example, if I have 4 males and 8 females in my DB, I need a select tags with options
"Select gender"
"Male (4)"
"Female (8)"
In my Form class I have
gender = forms.ModelChoiceField(
queryset=Gender.objects.all().annotate(counter=Count('user')),
required=False,
label="Gender"
)
And I don't know how to render this select element. If I use {{ form.gender }} I can't even display the options. I get Error binding parameter 0 - probably unsupported type. It could be an SQLite issue? I will use PostgreSQL as my DB anyway.
Any suggestions?
EDIT: I need something like this, with almost 10 form fields and a variable number of options in each case
I would do it this way. I tried this and it works fine. Modify it for your needs and it should work fine as well. I am just modifying the widget for gender field.
class testform(forms.ModelForm):
class Meta:
model = names
fields = ['name', 'gender']
male = 'male'+str(names.objects.filter(gender='m').count())
female = 'female'+str(names.objects.filter(gender='f').count())
choices = ((male, male),(female,female))
gender = forms.CharField(widget=forms.Select(choices=choices))
You can override label_from_instance method of ModelChoiceField:
class GenderModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.name + ' ({})'.format(obj.counter)
And use this custom field in your form:
gender = GenderModelChoiceField(
queryset=Gender.objects.all().annotate(counter=Count('user')),
required=False,
label="Gender"
)
Related
I am working on some backend django work that requires me to grab an Employee by filtering, but I also need to grab the EmployeeAddress object that is associated to the Employee. I was wondering if this was possible within a single query. I need the employees address, and employee info to be in a combined single dictionary, to access on the front end side with JS.
I have models as such,
Class Employee(models.model):
first_name
last_name
email
Class EmployeeAddress(models.model):
employee = models.ForeignKey(Employee):
street
city
state
I have a view, that kinda does the job, but having trouble merging merging the two separate QuerySets into a single listed dictionary with all values.
I was hoping there was just a way to get the EmployeeAddress without even writing the second query, and just grabbing that associated data in the first employee_list query?
def employee_ajax_list(request):
email = request.GET.get('email', None)
employee_list = Employee.objects.filter(email=email)
employee_address = EmployeeAddress.objects.filter(employee_id__in=employee_list).values(
'street', 'city', 'state',)
# this chain kinda works, but splits them into 2 separate dictionaries?
employee_info = list(chain(employee_list.values(), employee_address))
data = {
'employee_list': employee_info
}
return JsonResponse(data)
Just looking on some advice to make this work a little smoother!
Change this line
employee = models.ForeignKey(Employee)
to
employee = models.ForeignKey(Employee, related_name="address")
That will let you access an employee's address by doing employee.address in regular code or employee__address in queries.
For example:
Employee.objects
.filter(email=email)
.exclude(address=None)
.values(
'email',
'name',
'address__street',
'address__city',
'address__state'
)
(The .exclude() clause is in case someone doesn't have an address set.)
That should output something like:
<QuerySet [{'email': 'johnsmith#example.com',
'name': 'John Smith', 'address__street':
'123 Maple St', 'address__city': 'Springfield',
'address__state': 'MO'}]>
Maybe something like this should be a bit better and in a single query:
employee_info = EmployeeAddress.objects.filter(employee__email=email).values("employee__email", "employee__<another_field>", "street", "city", "state")
data = {
'employee_list': employee_info
}
A best way of doing it should be with DRF serializers:
class EmployeeAddressSerializer(serializers.ModelSerializer):
class Meta:
fields = "__all__"
model = Employee
class EmployeeSerializer(serializers.ModelSerializer):
# you should add related_name="addresses" in the
# foreignkey if you want this works.
addresses = EmployeeAddressSerializer(many=True)
class Meta:
fields = "__all__"
model = Employee
Then to use:
employee = Employee.objects.filter(email=email).last()
return JsonResponse(EmployeeSerializer(employee).data)
I have a form in a formset where I would like to display multiple drop down menus under a single field 'tests'. I have achieved this in the form of having a single dropdown menu within 'optgroup' tags (see image below).
I guess this way you can only choose a single value.
However, is it possible to 'nest' these drop downs? I.e have them all under one field 'tests', but be able to have several dropdowns with 'tags' and choose results for each tag? Or do I need a field for each 'tag'?
My forms.py:
class ReportForm(forms.ModelForm):
summary = forms.CharField(
widget=forms.Textarea(attrs={'rows':3, 'cols':70}),
label='',
required=False)
tests = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = ClinicallyReportedSample
fields = ('id', 'summary', 'tests', 'hilis_reported')
def __init__(self, *args, **kwargs):
json_data = kwargs.pop('json_data', None)
super(ReportForm, self).__init__(*args, **kwargs)
crs_obj = self.instance
for j in json_data:
if j['lab_no'] == str(crs_obj):
json = j
summary = json['summary']
self.fields['summary'].initial = summary
self.fields['reported'].label = crs_obj
tests = json.get('tests', None)
if tests:
test_choices = (
('mutated', 'mutated'),
('mutated - see comments', 'mutated - see comments'),
('awaiting confirmation', 'awaiting confirmation'),
)
self.fields['tests'] = forms.ChoiceField(
required=True,
label='Current or repeat samples?',
choices=((k, test_choices) for k in tests),
)
What I get now:
I would instead want a dropdown for each gene, and those choices. Do I need to make a field for each gene? The problem I have with doing this is that each result can have 0-10 genes, and this would be incredibly difficult to render in a HTML table.
Thanks
You probably want to implement something template/client-side to handle that, such as Chosen or Selectize.js (see the option groups examples).
Then on your form class implement a clean and/or clean_[field_name] method if you need to get your selected data in the format you want.
In my project I am using Django-Select2 heavily, particularly its ModelSelect2Widget as my users frequently need to select from lists of 2,000-6,000 items. In all my uses of it up 'til now, the queryset for the widget has always been called as ".all()" instances of a model, for the user to select from, and there's been no issues.
Now, however, I have implementations in different parts of the project for which filtering the queryset of options for the widget is necessary. In all of these cases, however, any modification of the queryset seems to have no effect, and I'm wondering if there is a problem with the widget itself.
In the primary case, the items in the database are boolean-flagged as active/inactive (about 65% inactive), and I need to only have active items available for the end-user to select.
I'm able to filter the queries correctly via the shell.
In the form definitiion, any filtering (".filter(flag_active=True)", or even setting the queryset to ".none()" has no effect – there is no apparent change in the options in the dropdown/autocomplete. Being that it is a select2 input, I can only view a small number of items at a time, but both the initial retrieved population and the winnowed-down selection as I type indicate that the filters are not been followed.
MODEL:
class Inventory_product_base(models.Model):
id = models.UUIDField(primary_key=True,default=uuid.uuid4,null=False)
upc = models.CharField(max_length=96,null=True,blank=True)
name = models.CharField('Item name',max_length=96,null=False)
flag_active = models.BooleanField("Active item",default=True)
price = models.DecimalField(max_digits=8,decimal_places=3,null=True,blank=True)
unit_of_measure = models.CharField('UOM',max_length=24, choices=UNITS_OF_MEASURE,default='EACH')
spec = models.CharField(max_length=36,null=True,blank=True)
category = models.ForeignKey(Inventory_category,on_delete=models.CASCADE,related_name='cat_products')
subcategory = models.ForeignKey(Inventory_subcategory,on_delete=models.CASCADE,related_name='subcat_products')
note = models.CharField(max_length=275,null=True,blank=True)
def __str__(self):
return str(self.name)
FORM:
class InventoryCatalogUpdateProductsForm(forms.ModelForm):
parent_product_base = forms.ModelChoiceField(
queryset=Inventory_product_base.objects.filter(flag_active=True),
label=u"",
widget=ModelSelect2Widget(
model=Inventory_product_base,
search_fields=['name__icontains'],
attrs={'data-placeholder': 'Select product...', 'data-width': '100%'},),)
class Meta():
model = Inventory_unit_catalog
fields = ('parent_product_base',)
class InventoryCatalogUpdateAllProductsForm(forms.ModelForm):
parent_product_base = forms.ModelChoiceField(
queryset=Inventory_product_base.objects.all(),
label=u"",
widget=ModelSelect2Widget(
model=Inventory_product_base,
search_fields=['name__icontains'],
attrs={'data-placeholder': 'Select product...', 'data-width': '100%'},),)
class Meta():
model = Inventory_unit_catalog
fields = ('parent_product_base',)
InventoryCatalogUpdateProductsFormset = modelformset_factory(model=Inventory_unit_catalog,form=InventoryCatalogUpdateProductsForm,extra=10,can_delete=True)
InventoryCatalogUpdateAllProductsFormset = modelformset_factory(model=Inventory_unit_catalog,form=InventoryCatalogUpdateAllProductsForm,extra=10,can_delete=True)
VIEW:
if product_flag == 'active':
formset = InventoryCatalogUpdateProductsFormset(queryset=parent_unit_catalog.products.filter(flag_active=True))
else:
formset = InventoryCatalogUpdateAllProductsFormset(queryset=parent_unit_catalog.products.all())
As noted, if I change the above queryset to .none() (or anything else, either in the widget or in the view) there is no difference in the rendered choices in the select2 field.
I've tried separate, parallel forms and formsets. Originally I tried for a more sophisticated approach, to pass a parameter and have the different querysets selected within a single form, by adding the following:
def __init__(self, *args, **kwargs):
self.product_flag = kwargs.pop('product_flag')
super(InventoryCatalogAddToForm, self).__init__(*args, **kwargs)
print("__init__ has product_flag: ",self.product_flag)
if self.product_flag == 'active':
self.fields['parent_product_base'].queryset = Inventory_product_base.objects.filter(flag_active=True)
print("Screened for flag_active=True")
else:
self.fields['parent_product_base'].queryset = Inventory_product_base.objects.all()
print("Screened for flag_active=False")
and I was able to verify by the debug prints that the correct filter choices were executing, but without any effect. So I moved back to a simpler, more direct approach of separate forms, and still nothing.
Any advice would be welcome. My project is several months in and Django-Select2 is one of the foundations across it, I would hate to learn that it cannot filter the select2 input and I would need to find a replacement.
self.fields['parent_product_base'].queryset sets the queryset for the formfield (i.e. allowed choices for validation).
Use self.fields['parent_product_base'].widget.queryset to set the widget's choices.
I made a model something like this:
class Enduser(models.Model):
user_type = models.CharField(max_length = 10)
Now I want user_type to have only one of the given values, say any one from ['master', 'experienced', 'noob']
Can I do this with Django?
Also, how can I display a list of radio buttons or drop-down list/select menu to chose one of these values?
You can take advantage of the choices attribute for CharField:
class Enduser(models.Model):
CHOICES = (
(u'1',u'master'),
(u'2',u'experienced'),
(u'3',u'noob'),
)
user_type = models.CharField(max_length = 2, choices=CHOICES)
This will save values 1,2 or 3 in the db and when retrieved the object, it will map it to master, experienced or noob. Take a look at the docs for more info.
Hope this helps!
Use model field choices:
CHOICES = (
('foo', 'Do bar?'),
...
)
class Enduser(models.Model):
user_type = models.CharField(max_length = 10, choices=CHOICES)
I want to show the human-readable name for the type selected but I
keep getting the stored value.
TYPE_CHOICES = (
('0', 'Basic'),
('1', 'Full'),
('2', 'Intermediate'),
)
class ServiceType(models.Model):
type = models.IntegerField(max_length=1, choices=TYPE_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def __unicode__(self):
return '%s' % (self.get_type_display())
It seems that you have your answer, but as another link, I'd just like to point out James Bennett's thoughts on this:
Handle choices the right way
I think it is a pretty convenient way to do things, and removes the 'magic number' aspect of things. Worth a read IMO, even if you go for a different option.
From his article (quoted in case it disappears):
class Entry(models.Model):
LIVE_STATUS = 1
DRAFT_STATUS = 2
HIDDEN_STATUS = 3
STATUS_CHOICES = (
(LIVE_STATUS, 'Live'),
(DRAFT_STATUS, 'Draft'),
(HIDDEN_STATUS, 'Hidden'),
)
# ...some other fields here...
status = models.IntegerField(choices=STATUS_CHOICES, default=LIVE_STATUS)
Now we can just import the Entry model and query like so:
live_entries = Entry.objects.filter(status=Entry.LIVE_STATUS)
draft_entries = Entry.objects.filter(status=Entry.DRAFT_STATUS)
You probably want to use ChoiceField instead of IntegerField in your model. It sounds like you are seeing an input tag with type=text in your admin but want a select tag. The default widget associated with a IntegerField is TextInput which would explain what you are seeing.
Another option is to write your own admin and explicitly call out that you want type to be a ChoiceField in the admin. Something like this:
class ServiceTypeAdmin(admin.ModelAdmin):
# ...
type = fields.ChoiceField(choices=TYPE_CHOICES)
admin.site.register(ServiceType, ServiceTypeAdmin)
I would personally start by switching the IntegerField to a ChoiceField. Way less work involved.
I had that same problem, and couldn't figure out why it works, but if you change the field type to CharField the get_type_display should work fine.
TYPE_CHOICES = (
('B', 'Basic'),
('F', 'Full'),
('I', 'Intermediate'),
)
class ServiceType(models.Model):
type = models.CharField(max_length=1, choices=TYPE_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)
Rookie mistake, I've changed the tuple values from ('0', 'Basic) to (0, 'Basic') and it worked. I didn't realize that I was saving a char value as an integer value.
Thanks for your help.
Trick
use TypedChoiceField()
The answer of your questoin lies in using TypedChoiceField, not ChoiceField.
you are getting type field from a django form, using the cleaned_data from a ChoiceField. The problem with this is that the output from a ChoiceField is a string, not an integer.
if you use get_type_display() right after saving the form, u would probably get the value, but when u have try to retrieve the value from the DB, you would get integer instead of string(because your are saving type as Integer field), here you wont be able to get value with get_type_display.
Having now looked into this, I see that you should have used the TypedChoiceField, to ensure that the output from cleaned_data is always an integer or string.
first of all change IntergerField to Char field or SmallIntergetField.
Hope this helps.
Code
type = models.SmallIntegerField(choices=TYPE_CHOICES)
in forms.py
type = TypedChoiceField(coerce=int, required=False, empty_value=0, choices=TYPE_CHOICES)
another possibility is that you could use MODELFORM and provide the widgets for the field.
Forms.py
class abc(forms.Modelform)
class Meta:
model = FOO
widgets = {
'type': forms.TypedChoiceField(coerce=int, required=False, empty_value=0, choices=TYPE_CHOICES),
type shadows with an in-built bulletin, that's why get_type_display() has no effects. Avoid using column names as type instead use service_type or something else and use get_service_type_display() i.e get_<column_name>_display().