placeholder on dependent dropdown filter - django

I have a django filter with a dependent drop down to filter car manufactures and models. The models use a charfield and pulls the cars from a db entry.
I would like a place holder to say manufacture and model on their respected fields.
I cant find much online about doing this. The only post I can find relates to using the choice field on the model which wont work for me.
filter
class CarFilterForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['model'].queryset = Post.objects.none()
if 'model_manufacture_id' in self.data:
try:
model_manufacture_id = int(self.data.get('model_manufacture_id'))
self.fields['model_id'].queryset = CarModels.objects.filter(model_manufacture_id=model_manufacture_id)
except (ValueError, TypeError):
pass
class carFilter(django_filters.FilterSet):
class Meta:
model = Post
fields = 'manufacture', 'model'
form = CarFilterForm
html
<form method="get" id="AllCarsForm" data-cars-url="{% url 'ajax-allcars' %}">
{% render_field myFilter.form.manufacture class="cars-filter-widget" %}
{% render_field myFilter.form.model class="cars-filter-widget" %}
<button class="btn btn-primary" type="submit">Search</button>
</form>
models
class Manufactures(models.Model):
manufacture_id = models.AutoField(primary_key=True)
manufacture = models.CharField(max_length=55, default="OEM")
class CarModels(models.Model):
model_id = models.AutoField(primary_key=True)
model = models.CharField(max_length=55)
model_manufacture = models.ForeignKey(Manufactures, on_delete=models.CASCADE)

Try to set the empty_label for the fields:
self.fields['your_field'].empty_label = 'My custom empty label'

The simplest method of doing this is to set the model field default to one that corresponds to your fields.
Example:
class Model(models.Model):
field = models.CharField(max_length=25, choices=CHOICES,
default=DEFAULT, blank=True)
You can also do this in forms:
self.fields['field'].choices = [('', 'Placeholder Text')] + list(
self.fields['field'].choices[1:])

Related

How to use ModelChoiceField in DRF?

I am trying to convert my form that was written earlier to django rest serializer but it does't work. Could you help me to solve this problem please?
this is my form:
class TripSearchForm(forms.Form):
departure = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
destination = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
How to built proper serializer?
class SearchSerializer(serializers.Serializer):
departure = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='departure')
destination = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='destination')
Assuming you have model Country
class Country(models.Model):
name = models.CharField(max_length=60, blank=True, default='')
You could write a serializers based on that
class CountryField(serializers.PrimaryKeyRelatedField):
def display_value(self, instance):
return instance.name
class CountrySerializer(serializers.ModelSerializer):
country = CountryField(queryset=Country.objects.all())
class Meta:
model = Country
fields = ('name', )
class DesiredSerializer(serializers.Serializer):
country = ColorSerializer()
Now you should be able to use your desired serialized to render choices from model either as html form for instance or whatever logic fits you better
if you want it as form
#views.py
def get(self, request):
serializer = DesiredSerializer()
return Response({ 'serializer': serializer }, template_name='my_model_choices_form.html')
<!-- my_model_choices_form.html -->
{% load rest_framework %}
<form action="..." method="POST">
{% csrf_token %}
{% render_form serializer %}
</form>
Now if you'll create instance of Country with some name it will be shown in select dropdown, display_value function can be used to customize the option output.
Hope that helps

Django Form based on Form

I am writing my code based on the principle don't repeat yourself. I keep violating that rule because I am new to Django but this one should be straight forward.
The code below is no problem for ModelAForm:
model.py
class ModelA(models.Model):
id = model.AutoField(primary_key=True)
name1 = models.CharField(max_length=100)
name2 = models.CharField(max_length=100)
...
right = models.BooleanField(default=True)
class ModelB(models.Model):
id = model.AutoField(primary_key=True)
mod = model.ForeignKey(ModelA, on_delete=models.CASCADE)
above30 = models.BooleanField(default=True)
forms.py
class ModelAForm(forms.ModelForm):
class Meta:
model = ModelA
exclude = ['id']
class ModelBForm(forms.ModelForm):
class Meta:
model = ModelB
exclude = ['id']
But this way I don't see the other fields of modelA in the ModelBForm. How can I do this?
Thanks!
Well, it should not. Because its FK relation between ModelB and ModelA. So in ModelB form, the ModelA entries should appear as a choice field. If you want to show fields of ModelA then try like this:
First, remove the FK reference field from ModelBForm:
class ModelBForm(forms.ModelForm):
class Meta:
model = ModelB
exclude = ['id', 'mod']
Now Use both those forms in your view:
def some_view(request):
if request.method == 'GET':
return render(request, 'template.html', {'form_a': ModelAForm(), 'form_b': ModelBForm()})
if request.method == 'POST':
form_a = ModelAForm(request.POST)
form_a = ModelBForm(request.POST)
if form_a.is_valid() and form_a.is_valid():
instance_a = form_a.save() # get model a instance
instance_b = form_b.save(commit=False)
instance_b.mod = instance_a # set model a instance as FK
instance_b.save()
Render the Form Like this:
<form action="." method="post">
{% csrf_token %}
{{ form_a.as_p }}
{{ form_b.as_p }}
<input type="submit" value="Submit">
</form>

Django: How to add a foreignkey to a dependent form, to a multi-form submit?

Forgive the logic of the table structure below example. It only meant as a simple example of the situation that I am.
So the situation is that I want to make an employee form page, where the department and line manager might or might not exist already. So I replace the drop-down box with the form field for the foreign key, so they can be created if need all in one step for the user. However, with this kind of dependency, I am not doing the right thing in view to make it work.
If you need more detail please do ask.
If you can make the Title more precise please do.
Thank you for your time.
Model
class Employee(models.Model):
name = models.CharField()
age = models.CharField()
line_manager = models.ForeignKey(LineManager)
department = models.ForeignKey(Department)
class LineManager(models.Model):
manager_name = models.CharField()
department = models.ForeignKey(Department)
class Department(models.Model):
department_name = models.CharField()
Form
class EmployeeForm(ModelForm):
class Meta:
model = Employee
fields = ['name',
'age'
#'line_manager' Not needed
#'department' Not needed]
exclude = ('line_manager', 'department')
class LineManagerForm(ModelForm):
class Meta:
model = LineManager
fields = ['manager_name']
exclude = ('department')
# There is a basic form for Department, as well.
View
class AddEmployeeView(View):
forms = {'department': DepartmentForm(self.request.POST or None),
'line_manager': LineManagerForm(self.request.POST or None),
'employee': EmployeeForm(self.request.POST or None)]}
def get(self, request, *args, **kwargs):
form_list = [form for _,form in forms]
return render (request, 'app/temp.html', {'forms': form_list}
def post(self, request, *args, **kwargs):
if all([form.is_valid() for _,form in forms]):
department_data = forms['department'].cleaned_data
department_obj, _ = Department.objects.get_or_create(department_data)
line_manager_instance = forms['line_manager'].instance
line_manager_instance.department = department_obj
line_manager_data = forms['line_manager'].cleaned_data
line_manager_obj, _ = LineManager.objects.get_or_create(line_manager_data)
employee_instance = forms['employee'].save(commit=False)
employee_instance.department = department_obj
employee_instance.line_manager = line_manager_obj
employee_instance.save()
html
<form method="POST">
{% csrf_token %}
{% form in forms %}
{{ form.as_p }}
{% endform %}
<input type='submit'/>
</form>

Django passing object ID in hiddeninput by populating

I have an form which allows a user to edit an object description.
How can I populate an object ID in a form's hidden input value.
What I done so far is I added an field called hidden_field in forms.py but it only show the hidden_field . How can I link the hidden_field with the object ID
models.py
class School(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=55)
description = models.CharField(max_length=300,blank=True)
forms.py
class SchoolDescriptionForm(forms.ModelForm):
description = forms.CharField(widget=forms.Textarea,max_length=300)
hidden_field = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = School
fields = ()
views.py
def SchoolEditor(request,school_id):
school = School.objects.get(pk=school_id,user=request.user)
form = SchoolDescriptionForm(instance=school) # I want to populate the object ID
return render(request,'schooleditor.html',{'school':school,'form':form})
template
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type = "submit" value= "save" />
{{ form.field.as_hidden }}
</form>
Change hidden_field to id and tell Django to include the School's id.
class SchoolDescriptionForm(forms.ModelForm):
description = forms.CharField(widget=forms.Textarea,max_length=300)
id = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = School
fields = ('id', 'name', 'description')
EDIT:
If you want to conserve hidden_field as name you should then add a custom init method:
def __init__(self, *args, **kwargs):
super(SchoolDescriptionForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['hidden_field'].initial = instance.id
Just pass the object id in the form initial:
def SchoolEditor(request,school_id):
initial = {}
school = School.objects.get(pk=school_id,user=request.user)
if school:
initial.update({'hidden_field': school.id})
form = SchoolDescriptionForm(instance=school, initial=initial) # I want to populate the object ID
return render(request,'schooleditor.html',{'school':school,'form':form})

Django - Rendering a relation between two models as a radio button

I have the following models:
class Profile(models.Model):
verified = models.BooleanField(default=False)
primary_phone = models.OneToOneField('Phone', related_name='is_primary', null=True, blank=True)
class Phone(models.Model):
profile = models.ForeignKey(Profile)
type = models.CharField(choices=PHONE_TYPES, max_length=16)
number = models.CharField(max_length=32)
#property
def is_primary(self):
return profile.primary_phone == self
And the following forms:
class PhoneForm(ModelForm):
class Meta:
from accounts.models import Phone
model = Phone
fields = ('type', 'number', )
which is being used in a modelformset_factory.
I'm rendering the formset like this:
<div class="span-13 last">
{{ formset.management_form }}
{% for form in Phones %}
<div class="span-2">{{ form.type|add_class:'dropdown' }}</div>
<div class="span-11 last">{{ form.number|add_class:'phone-number' }}</div>
<div class="clearfix"></div>
{% endfor %}
</div>
Now what I want to do is to render a radio button in the template to reflect the is_primary property of Phone model. There are two ways to determine this relationship, through Phone model itself or through Profile.primary_phone. But then I'm rendering Phone model as a formset, hence looping over its instances, so I tried to include 'is_primary' in PhoneForm fields, but it did not work since it's a property.
Any idea how to do this?
UPDATE #1:
I have used jpic approach and tried to render primary as radio buttons:
class PhoneForm(ModelForm):
primary = forms.BooleanField(widget=forms.RadioSelect( choices=((0, 'False'), (1, 'True')) ))
class Meta:
from accounts.models import Phone
model = Phone
fields = ('primary', 'type', 'number', )
However, it shows two radio buttons for each instance of Phone while I need it to show only one radio button per instance. I'm going to play around with it for a while and see if I can get it to show correctly.
Instead of:
class Profile(models.Model):
verified = models.BooleanField(default=False)
primary_phone = models.OneToOneField('Phone', related_name='is_primary', null=True, blank=True)
class Phone(models.Model):
profile = models.ForeignKey(Profile)
type = models.CharField(max_length=16)
number = models.CharField(max_length=32)
You should have:
class Profile(models.Model):
verified = models.BooleanField(default=False)
def primary_phone(self):
return self.phone_set.get(primary=True)
class Phone(models.Model):
profile = models.ForeignKey(Profile)
type = models.CharField(max_length=16)
number = models.CharField(max_length=32)
primary = models.BooleanField(default=False)
def save(self, force_insert=False, force_update=False, using=None):
if self.primary:
# clear the primary attribute of other phones of the related profile
self.profile.phone_set.update(primary=False)
self.save(force_insert, force_update, using)
That would make your life easier.
If you cannot make this change: a Phone formset is actually a wrapper around many Phone forms. But the field you're after allows to edit Profile.primary_phone.
So one way of doing it is to do it manually as such:
{% for form in Phones %}
<input type="radio" name="primary_phone" checked="{% if form.instance == profile.primary_phone %}checked{% endif %}" value="{{ form.instance.pk }}" />
<!-- snip ... ->
But the problem is that the radio won't have a value for empty Phone forms, as the value is {{ form.instance.pk }}.
Another way of doing it is to add a checkbox to PhoneForm:
from django import forms
from accounts.models import Phone
class PhoneForm(forms.ModelForm):
primary = forms.BooleanField(required=False, default=False)
class Meta:
model = Phone
fields = ('type', 'number', )
We're using a BooleanField here because for each Phone form, primary is to be set or not. But still, you'll have to render it yourself:
{% for form in Phones %}
<input type="checkbox" name="{{ form.prefix }}-primary" checked="{% if form.instance == profile.primary_phone %}checked{% endif %}" value="true" />
<!-- snip ... ->
But then, you need javascript to ensure only one radio is checked at the time, e.g. with jQuery:
$('input[type=checkbox]').change(function() {
$('input[type=checkbox][checked=checked]').attr('checked', '');
$(this).attr('checked', 'checked');
});
Of course, you should update the selectors in this example above to ensure only "primary phone" checkboxes are affected.
Finally, to connect the checkbox, something like this might work:
class PhoneForm(forms.ModelForm):
primary = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super(PhoneForm, self).__init__(*args, **kwargs)
if self.instance.is_primary:
self.data['primary'] = True
def save(self, *args, **kwargs):
super(PhoneForm, self).save(*args, **kwargs)
if self.cleaned_data['primary']:
self.profile.primary_phone = self
self.profile.save()