I would like to be able to have a drop-down list of choices from the database that are links (URLs) and also have a text input box to create a new URL.
I've seen the answers to Django form with choices but also with freetext option?
It seems like the non-chosen solution by the author of that question is closest to what I need but I have a few differences:
- The choices are from the database.
- The choices should be hyperlink-able to URLs.
Is this doable with Django?
I would like to have a pull-down of the links only. The text box will show the selected link -- this link will also be in the pull-down. If you enter a new link in the text box, that will be added to the database and to the pull down.
I would also like to delete entries from the pull-down -- which will delete them from the database. Maybe have an "x" next to each link in the pull-down so that it will be deleted from the the database and the pull-down when clicked. If this last part is too cumbersome, it can be another way.
Here is the code that I have so far. All that is showing is "--------" in the pull-down box and "fields.UrlChoiceField object at 0x7f869910af10" in the text-box:
forms.py:
class Form(ModelForm):
detail_urls = common_fields.UrlChoiceField(
label='Detail URLs', required=False,
queryset=common_models.DetailUrl.objects.none(),
help_text='List of Detail URLs from which to pick. Can be null.')
def __init__(self, *args, **kwargs):
super(Form, self).__init__(*args, **kwargs)
if self.instance.pk:
if 'detail_urls' in self.fields:
queryset = self.instance.detail_urls.all()
self.initial.update({
'detail_urls': common_fields.UrlChoiceField(queryset=queryset)})
fields.py:
class OptionalChoiceWidget(forms.MultiWidget):
def __init__(self, *args, **kwargs):
queryset = kwargs.pop('queryset')
widgets = (
forms.Select(choices=queryset),
forms.TextInput())
super(OptionalChoiceWidget, self).__init__(*args, **kwargs)
def decompress(self, value):
if value:
if value in [x[0] for x in self.widgets[0].choices]:
return [value, '']
return ['', value]
return ['', '']
class UrlChoiceField(forms.MultiValueField):
def __init__(self, queryset, *args, **kwargs):
fields = (
forms.ModelChoiceField(queryset=queryset, required=False),
forms.CharField(required=False))
self.widget = OptionalChoiceWidget(
queryset=queryset, widgets=[f.widget for f in fields])
super(UrlChoiceField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list):
if not data_list:
raise exceptions.ValidationError(
'Need to select choice or enter text for this field')
return data_list[0] or data_list[1]
class URLWidget(forms.TextInput):
"""Widget for displaying an URLField input with clickable link."""
def render(self, name, value, attrs=None):
"""Defines HTML representation for this widget."""
cls = ''
attrs = attrs or {}
if 'class' in attrs:
cls = ' ' + attrs['class']
attrs['class'] = 'autolink' + cls
html = super(URLWidget, self).render(name, value, attrs)
return safestring.mark_safe(html)
class URLField(models.CharField):
"""A CharField with milder validation of URLs than Django's URLField."""
default_validators = [validators.ValidateUrl]
def __init__(self, *args, **kwargs):
if 'max_length' not in kwargs:
kwargs['max_length'] = 512
super(URLField, self).__init__(*args, **kwargs)
def formfield(self, **kwargs):
kwargs['widget'] = URLWidget
return super(URLField, self).formfield(**kwargs)
class DetailUrlField(URLField):
"""A URLField that becomes auto-linked in list view."""
def ListDisplay(self, value=None, unused_instance=None):
if not value:
value = ''
else:
value = html_utils.escape(value)
value = filters.AutoLink(value)
return value
ListDisplay.allow_tags = True
models.py:
class MyModel(Model):
detail_urls = ct_fields.GenericRelation(
'common.DetailUrl', object_id_field='object_pk')
class DetailUrl(models.Model):
content_type = models.ForeignKey(ct_models.ContentType)
object_pk = models.PositiveIntegerField(db_index=True)
content_object = ct_fields.GenericForeignKey(fk_field='object_pk')
detail_url = common_fields.DetailUrlField(
'Detail URL', blank=True, help_text='URL to relevant information.',
max_length=255)
class Meta(object):
ordering = ('detail_url',)
unique_together = ('content_type', 'object_pk', 'detail_url')
def __unicode__(self):
return u'%s: %s' % (self.content_object, self.detail_url)
def ListDisplay(self, unused_value=None, unused_instance=None):
"""Provides formatting of detail_url in list view."""
content = self.detail_url
content = filters.AutoLink(content)
return content
ListDisplay.allow_tags = True
def ExportDisplay(self):
return self.detail_url