0

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:

  1. The choices are from the database.
  2. 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
Jim Remedios
  • 41
  • 1
  • 5
  • 1
    Yes, it's do-able. – rnevius Apr 05 '16 at 00:53
  • You can use ModelChoiceField in your form and specify the "queryset" to pull the values from the database into dropdown, but I can't visualise a form dropdown with clickable links in it. Dropdown is a – somecallitblues Apr 05 '16 at 00:59
  • I've added some text and code fragments to answer your questions. Thank you. – Jim Remedios Apr 05 '16 at 14:24

0 Answers0