I have a Django app which I want to be able to use in multiple instances. One model (Listing) can have a variable number of fields (for the different instances) but will then always have exactly those extra fields for the instance. I want these extra fields added through admin so I've created models like this:
class BespokeField (models.Model):
name = models.CharField(
max_length = 20,
verbose_name = "Field Title"
)
def __unicode__(self):
return self.name
class Listing (models.Model):
name = models.CharField (
verbose_name = 'Listing',
max_length = 30
)
slug = models.SlugField (
verbose_name = "Slug",
allow_unicode = True,
unique=True,
blank=True,
null=True
)
class ListingBespokeField (models.Model):
bespoke_field = models.ForeignKey(BespokeField)
listing = models.ForeignKey(Listing)
value = models.CharField (
max_length = 60
)
def __unicode__(self):
return u'%s | %s' % (self.listing.name, self.bespoke_field.name)
The theory is admin specifies the bespoke fields and these are then displayed to the user in forms. Within admin this is relatively simple as I can assume a modicum on intelligence from the users so my admin.py looks like:
class ListingBespokeFieldInline(admin.TabularInline):
model = ListingBespokeField
extra = len(BespokeField.objects.all())
max_num = len(BespokeField.objects.all())
class ListingAdmin(admin.ModelAdmin):
inlines = [ListingBespokeFieldInline]
It does mean the admin user has to select one of each BespokeField from the dropdown but I'm not uncomfortable with that because by using unique_together ensure there is only one of each.
What I cannot work out how to do is present this to the non-admin user in a friendly way. What I want is the BespokeField.name
to display on the form as a label for ListingBespokeField.value
.
This is what I have in forms.py
(for ListingBespokeField).
class ListingBespokeFieldInline(forms.ModelForm):
class Meta:
model = ListingBespokeField
exclude =['id']
widgets = {
'bespoke_field' : forms.HiddenInput(),
'value' : forms.TextInput(attrs={'class' : 'form-control'})
}
class ListingBespokeFieldForm(forms.ModelForm):
class Meta:
model = ListingBespokeField
exclude = ()
BESPOKE_FIELD_COUNT = len(BespokeField.objects.all())
ListingBespokeFieldInlineFormSet = forms.inlineformset_factory (
Listing,
ListingBespokeField,
form=ListingBespokeFieldInline,
extra = BESPOKE_FIELD_COUNT,
max_num = BESPOKE_FIELD_COUNT,
exclude = ['id'],
can_delete=False,
can_order=False
)
I'm then trying to present it through a template as follows:
<table class="table">
{{ bespokefields.management_form }}
{% for form in bespokefields.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="formset_row bespokefield">
<td>
{{ form.listing }}{{ form.id }}{{ form.bespoke_field }}
{{ form.bespoke_field.label }}
</td>
<td>{{ form.value }}</td>
</tr>
{% endfor %}
</table>
This doesn't work. I could use some insight please.
This was my solution:
<table class="table">
{{ bespokefields.management_form }}
{% for form in bespokefields.forms %}
<tr class="formset_row bespokefield">
<td>
{{ form.listing }}{{ form.id }}
<select id="id_listingbespokefield_set-{{ forloop.counter0 }}-bespoke_field" name="listingbespokefield_set-{{ forloop.counter0 }}-bespoke_field" class="form-control">
{% with forloop.counter as counter %}
{% for x,y in form.fields.bespoke_field.choices %}
{% if counter == forloop.counter0 %}
<option value="{{x}}" selected>{{y}}</option>
{% endif %}
{% endfor %}
{% endwith %}
</select>
</td>
<td>{{ form.value }}</td>
</tr>
{% endfor %}
</table>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With