Пример #1
0
    def test_valid_db_id(self):
        """
        Make sure ID values are properly stored and serialized.
        """
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)

        page = PageModel.objects.create(slug='foo')
        v = AnyUrlValue(urltype.prefix, page.id, reg)

        # Database state
        self.assertTrue(page.id)
        self.assertEqual(urltype.prefix,
                         'any_urlfield.pagemodel')  # app_label.modelname
        self.assertEqual(v.type_prefix, urltype.prefix)
        self.assertEqual(v.type_value, page.id)
        self.assertEqual(v.to_db_value(), 'any_urlfield.pagemodel://1')

        # Frontend
        self.assertEqual(
            unicode(v),
            "/foo/")  # fetches model and returns get_absolute_url()

        # Programmer API's
        self.assertIs(v.get_model(), PageModel)
        self.assertEqual(v.get_object(), page)
        self.assertTrue(v.exists())
Пример #2
0
    def test_pickle_registry(self):
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)
        page = PageModel.objects.create(slug='foo')

        # See if custom registries can be pickled
        v1 = AnyUrlValue(urltype.prefix, page.id, reg)
        out = StringIO()
        pickle.dump(v1, out)

        # Unpickle.
        out.seek(0)
        v2 = pickle.load(out)
        self.assertEqual(v1, v2)  # Note that __eq__ is overridden for AnyUrlValue!
Пример #3
0
 def test_registry(self):
     """
     Test the basic registry setup.
     """
     reg = UrlTypeRegistry()
     self.assertIsNotNone(reg['http'])
     self.assertIsNotNone(reg['https'])
Пример #4
0
    def test_from_dbvalue_ftps(self):
        reg = UrlTypeRegistry()

        v = AnyUrlValue.from_db_value("ftps://www.example.com/", reg)
        self.assertEqual(v.type_prefix, 'http')   # http is the constant for external URL types
        self.assertEqual(v.type_value, "ftps://www.example.com/")
        self.assertEqual(unicode(v), "ftps://www.example.com/")
Пример #5
0
    def test_from_dbvalue(self):
        reg = UrlTypeRegistry()

        v = AnyUrlValue.from_db_value("http://www.example.com/", reg)
        self.assertEqual(v.type_prefix, 'http')
        self.assertEqual(v.type_value, "http://www.example.com/")
        self.assertEqual(unicode(v), "http://www.example.com/")
Пример #6
0
    def test_invalid_db_id(self):
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)
        v = AnyUrlValue(urltype.prefix, 999999, reg)

        # Database state
        self.assertEqual(v.type_value, 999999)
        self.assertEqual(v.to_db_value(), 'any_urlfield.pagemodel://999999')

        # Frontend
        self.assertEqual(unicode(v), "#DoesNotExist")  # Avoids frontend errors

        # Programmer API's
        self.assertIs(v.get_model(), PageModel)
        self.assertRaises(PageModel.DoesNotExist, lambda: v.get_object())
        self.assertFalse(v.exists())
Пример #7
0
    def test_invalid_db_id(self):
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)
        v = AnyUrlValue(urltype.prefix, 999999, reg)

        # Database state
        self.assertEqual(v.type_value, 999999)
        self.assertEqual(v.to_db_value(), 'any_urlfield.pagemodel://999999')

        # Frontend
        self.assertEqual(unicode(v), "#DoesNotExist")       # Avoids frontend errors

        # Programmer API's
        self.assertIs(v.get_model(), PageModel)
        self.assertRaises(PageModel.DoesNotExist, lambda: v.get_object())
        self.assertFalse(v.exists())
Пример #8
0
    def test_render_widget(self):
        """
        See if widget rendering is consistent between Django versions
        """
        reg = UrlTypeRegistry()
        reg.register(PageModel)

        class ExampleForm(forms.Form):
            field = any_urlfield.forms.AnyUrlField(url_type_registry=reg)

        def _normalize_html(html):
            # Avoid some differences in Django versions
            html = html.replace(' checked="checked"', '')
            html = html.replace(' checked', '')
            html = html.replace(' selected="selected"', ' selected')
            html = html.replace(' required', '')
            return html

        html = Template('{{ form.field }}').render(Context({'form': ExampleForm()}))
        self.assertHTMLEqual(_normalize_html(html), _normalize_html("""
    <div class="any-urlfield-wrapper related-widget-wrapper">
      <ul class="any_urlfield-url_type radiolist inline" id="id_field_0">
        <li>
          <label for="id_field_0_0">
            <input type="radio" name="field_0" value="http"
                   class="any_urlfield-url_type radiolist inline" id="id_field_0_0"/>
            External URL</label>
        </li>
        <li>
          <label for="id_field_0_1">
            <input type="radio" name="field_0" value="any_urlfield.pagemodel"
                   class="any_urlfield-url_type radiolist inline" id="id_field_0_1"/>
            page model</label>
        </li>
      </ul>

      <p class="any_urlfield-url-http" style="clear:left">
        <input type="text" name="field_1" class="vTextField" id="id_field_1"/>
      </p>

      <p class="any_urlfield-url-any_urlfieldpagemodel" style="clear:left">
        <select name="field_2" id="id_field_2">
          <option value="" selected>---------</option>
        </select>
      </p>
    </div>
    """))
Пример #9
0
    def test_invalid_db_id(self):
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)
        v = AnyUrlValue(urltype.prefix, 999999, reg)

        # Database state
        self.assertEqual(v.type_value, 999999)
        self.assertEqual(v.to_db_value(), 'any_urlfield.pagemodel://999999')

        # Frontend
        from any_urlfield.models.values import logger
        logger.warning("NOTE: The following statement will cause a log to output")
        self.assertEqual(unicode(v), "#DoesNotExist")       # Avoids frontend errors

        # Programmer API's
        self.assertIs(v.get_model(), PageModel)
        self.assertRaises(PageModel.DoesNotExist, lambda: v.get_object())
        self.assertFalse(v.exists())
Пример #10
0
    def test_from_db_value_ftps(self):
        """
        Make sure other URLs are properly serialized.
        """
        reg = UrlTypeRegistry()

        v = AnyUrlValue.from_db_value("ftps://www.example.com/", reg)
        self.assertEqual(v.type_prefix,
                         'http')  # http is the constant for external URL types
        self.assertEqual(v.type_value, "ftps://www.example.com/")
        self.assertEqual(unicode(v), "ftps://www.example.com/")
Пример #11
0
    def test_from_db_value_mailto(self):
        """
        Test constructing the value from ID.
        """
        reg = UrlTypeRegistry()

        v = AnyUrlValue.from_db_value("mailto://[email protected]", reg)
        self.assertEqual(v.type_prefix,
                         'http')  # http is the constant for external URL types
        self.assertEqual(v.type_value, "mailto://[email protected]")
        self.assertEqual(unicode(v), "mailto://[email protected]")
Пример #12
0
    def test_pickle_registry(self):
        """
        Test pickle when the ``AnyUrlValue`` has a custom registry.
        """
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)
        page = PageModel.objects.create(slug='foo')

        # See if custom registries can be pickled
        v1 = AnyUrlValue(urltype.prefix, page.id, reg)
        out = StringIO()
        pickle.dump(v1, out)

        # Unpickle.
        out.seek(0)
        v2 = pickle.load(out)
        self.assertEqual(v1, v2)  # Note that __eq__ is overridden for AnyUrlValue!

        # See that the object still works properly
        self.assertEqual(v2.get_object(), page)
        self.assertEqual(str(v2), '/foo/')
Пример #13
0
    def test_from_db_value_id(self):
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)

        page = PageModel.objects.create(slug='foo')
        v = AnyUrlValue(urltype.prefix, page.id, reg)

        # Database state
        self.assertTrue(page.id)
        self.assertEqual(urltype.prefix, 'any_urlfield.pagemodel')   # app_label.modelname
        self.assertEqual(v.type_prefix, urltype.prefix)
        self.assertEqual(v.type_value, page.id)
        self.assertEqual(v.to_db_value(), 'any_urlfield.pagemodel://1')

        # Frontend
        self.assertEqual(unicode(v), "/foo/")          # fetches model and returns get_absolute_url()

        # Programmer API's
        self.assertIs(v.get_model(), PageModel)
        self.assertEqual(v.get_object(), page)
        self.assertTrue(v.exists())
Пример #14
0
    def test_form_clean(self):
        """
        Basic test of form validation.
        """
        reg = UrlTypeRegistry()
        reg.register(PageModel)

        class ExampleForm(forms.Form):
            url = any_urlfield.forms.AnyUrlField(url_type_registry=reg)

        # Test 1: basic URL
        form = ExampleForm(data={
            'url_0': 'http',
            'url_1': 'http://examle.org/',
        })
        self.assertTrue(form.is_valid())
        self.assertEqual(form.cleaned_data['url'], AnyUrlValue.from_db_value('http://examle.org/'))

        # Test 2: ID field
        x = PageModel.objects.create(slug='foo')
        form = ExampleForm(data={
            'url_0': 'any_urlfield.pagemodel',
            'url_1': '',
            'url_2': str(x.pk),
        })
        self.assertTrue(form.is_valid())
        self.assertEqual(form.cleaned_data['url'].to_db_value(), 'any_urlfield.pagemodel://{}'.format(x.pk))
        self.assertEqual(form.cleaned_data['url'].get_object(), x)

        expected = AnyUrlValue.from_db_value('any_urlfield.pagemodel://{}'.format(x.pk), url_type_registry=reg)
        self.assertEqual(form.cleaned_data['url'], expected)

        # Test 3: invalid ID
        x = PageModel.objects.create(slug='foo')
        form = ExampleForm(data={
            'url_0': 'any_urlfield.pagemodel',
            'url_1': '',
            'url_2': '-1',
        })
        self.assertFalse(form.is_valid())
Пример #15
0
    def test_pickle_registry(self):
        """
        Test pickle when the ``AnyUrlValue`` has a custom registry.
        """
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)
        page = PageModel.objects.create(slug='foo')

        # See if custom registries can be pickled
        v1 = AnyUrlValue(urltype.prefix, page.id, reg)
        out = StringIO()
        pickle.dump(v1, out)

        # Unpickle.
        out.seek(0)
        v2 = pickle.load(out)
        self.assertEqual(v1,
                         v2)  # Note that __eq__ is overridden for AnyUrlValue!

        # See that the object still works properly
        self.assertEqual(v2.get_object(), page)
        self.assertEqual(str(v2), '/foo/')
Пример #16
0
    def test_resolve_values(self):
        """
        Make sure ID values are properly stored and serialized.
        """
        reg = UrlTypeRegistry()
        urltype = reg.register(PageModel)
        page = PageModel.objects.create(slug='foo')

        valid = AnyUrlValue(urltype.prefix, page.id, reg)
        invalid = AnyUrlValue(urltype.prefix, 999999, reg)

        with self.assertNumQueries(1):
            AnyUrlValue.resolve_values([valid, invalid])
        self.assertTrue(valid._resolved_objects)
        self.assertTrue(invalid._resolved_objects)

        with self.assertNumQueries(0):
            self.assertEqual(valid.get_object(), page)
            self.assertTrue(valid.exists())
            self.assertRaises(PageModel.DoesNotExist,
                              lambda: invalid.get_object())
            self.assertFalse(invalid.exists())
Пример #17
0
    def test_has_changed_empty_form(self):
        """
        Test that empty placeholder forms are not considered filled in
        """
        reg = UrlTypeRegistry()
        reg.register(PageModel)

        class ExampleForm(forms.Form):
            url = any_urlfield.forms.AnyUrlField(url_type_registry=reg)

        if django.VERSION >= (1, 10):
            empty_kwargs = dict(empty_permitted=True, use_required_attribute=False)
        else:
            empty_kwargs = dict(empty_permitted=True)

        form = ExampleForm(**empty_kwargs)
        data = get_input_values(form.as_p())
        assert form.initial == {}
        assert data == {'url_0': 'http', 'url_1': ''}

        # Submit the values unchanged
        form = ExampleForm(data=data, **empty_kwargs)
        self.assertFalse(form.has_changed(), "form marked as changed!")
Пример #18
0
class ICEkitURLField(AnyUrlField):
    # start with an empty UrlTypeRegistry to undo Fluent_Page's
    # inappropriate-for-us restriction to only-published Pages.
    _static_registry = UrlTypeRegistry()

    @classmethod
    def register_model_once(cls, ModelClass, **kwargs):
        """
        Tweaked version of `AnyUrlField.register_model` that only registers the
        given model after checking that it is not already registered.
        """
        if cls._static_registry.get_for_model(ModelClass) is None:
            logger.warn("Model is already registered with {0}: '{1}'"
                        .format(cls, ModelClass))
        else:
            cls.register_model.register(ModelClass, **kwargs)
Пример #19
0
 def test_registry(self):
     reg = UrlTypeRegistry()
     self.assertIsNotNone(reg['http'])
     self.assertIsNotNone(reg['https'])
Пример #20
0
class AnyUrlField(six.with_metaclass(models.SubfieldBase, models.CharField)):
    """
    A CharField that can either refer to a CMS page ID, or external URL.

    .. figure:: /images/anyurlfield1.*
       :width: 363px
       :height: 74px
       :alt: AnyUrlField, with external URL input.

    .. figure:: /images/anyurlfield2.*
       :width: 290px
       :height: 76px
       :alt: AnyUrlField, with internal page input.

    By default, the ``AnyUrlField`` only supports linking to external pages.
    To add support for your own models (e.g. an ``Article`` model),
    include the following code in :file:`models.py`:

    .. code-block:: python

        from any_urlfield.models import AnyUrlField
        AnyUrlField.register_model(Article)

    Now, the ``AnyUrlField`` offers users a dropdown field to directly select an article.
    By default, it uses a :class:`django.forms.ModelChoiceField` field with a :class:`django.forms.Select` widget
    to render the field.  This can be customized using the ``form_field`` and ``widget`` parameters:

    .. code-block:: python

        from any_urlfield.models import AnyUrlField
        from any_urlfield.forms import SimpleRawIdWidget

        AnyUrlField.register_model(Article, widget=SimpleRawIdWidget(Article))

    Now, the ``Article`` model will be displayed as raw input field with a browse button.
    """
    _static_registry = UrlTypeRegistry(
    )  # Also accessed by AnyUrlValue as internal field.

    def __init__(self, *args, **kwargs):
        if 'max_length' not in kwargs:
            kwargs['max_length'] = 300
        super(AnyUrlField, self).__init__(*args, **kwargs)

    @classmethod
    def register_model(cls,
                       ModelClass,
                       form_field=None,
                       widget=None,
                       title=None,
                       prefix=None):
        """
        Register a model to use in the URL field.

        This function needs to be called once for every model
        that should be selectable in the URL field.

        :param ModelClass: The model to register.
        :param form_field: The form field class used to render the field. This can be a lambda for lazy evaluation.
        :param widget: The widget class, can be used instead of the form field.
        :param title: The title of the model, by default it uses the models ``verbose_name``.
        :param prefix: A custom prefix for the model in the serialized database format. By default it uses "appname.modelname".
        """
        cls._static_registry.register(ModelClass, form_field, widget, title,
                                      prefix)

    def formfield(self, **kwargs):
        # Associate formfield.
        # Import locally to avoid circular references.
        from any_urlfield.forms.fields import AnyUrlField as AnyUrlFormField
        kwargs['form_class'] = AnyUrlFormField
        kwargs['url_type_registry'] = self._static_registry
        if 'widget' in kwargs:
            del kwargs['widget']
        return super(AnyUrlField, self).formfield(**kwargs)

    def to_python(self, value):
        if isinstance(value, AnyUrlValue):
            return value

        # Convert the string value
        if value is None:
            return None

        return AnyUrlValue.from_db_value(value, self._static_registry)

    def get_prep_value(self, value):
        if isinstance(value, six.string_types):
            # Happens with south migration
            return value
        elif value is None:
            return None if self.null else ''
        else:
            # Convert back to string
            return value.to_db_value()

    def value_to_string(self, obj):
        # For dumpdata
        value = self._get_val_from_obj(obj)
        return self.get_prep_value(value)

    def validate(self, value, model_instance):
        # Final validation of the field, before storing in the DB.
        super(AnyUrlField, self).validate(value, model_instance)
        if value:
            if value.type_prefix == 'http':
                validate_url = URLValidator()
                validate_url(value.type_value)
            elif value.type_value:
                if not value.exists():
                    raise ValidationError(
                        self.error_messages['invalid_choice'] %
                        value.type_value)
Пример #21
0
class ICEkitURLField(AnyUrlField):
    # start with an empty UrlTypeRegistry to undo Fluent_Page's
    # inappropriate-for-us restriction to only-published Pages.
    _static_registry = UrlTypeRegistry()
Пример #22
0
class AnyUrlField(models.CharField):
    """
    A CharField that can either refer to a CMS page ID, or external URL.

    .. figure:: /images/anyurlfield1.*
       :width: 363px
       :height: 74px
       :alt: AnyUrlField, with external URL input.

    .. figure:: /images/anyurlfield2.*
       :width: 290px
       :height: 76px
       :alt: AnyUrlField, with internal page input.

    By default, the ``AnyUrlField`` only supports linking to external pages.
    To add support for your own models (e.g. an ``Article`` model),
    include the following code in :file:`models.py`:

    .. code-block:: python

        from any_urlfield.models import AnyUrlField
        AnyUrlField.register_model(Article)

    Now, the ``AnyUrlField`` offers users a dropdown field to directly select an article.
    By default, it uses a :class:`django.forms.ModelChoiceField` field with a :class:`django.forms.Select` widget
    to render the field.  This can be customized using the ``form_field`` and ``widget`` parameters:

    .. code-block:: python

        from any_urlfield.models import AnyUrlField
        from any_urlfield.forms import SimpleRawIdWidget

        AnyUrlField.register_model(Article, widget=SimpleRawIdWidget(Article))

    Now, the ``Article`` model will be displayed as raw input field with a browse button.
    """
    _static_registry = UrlTypeRegistry(
    )  # Also accessed by AnyUrlValue as internal field.

    def __init__(self, *args, **kwargs):
        if 'max_length' not in kwargs:
            kwargs['max_length'] = 300
        super(AnyUrlField, self).__init__(*args, **kwargs)

    @classmethod
    def register_model(cls,
                       ModelClass,
                       form_field=None,
                       widget=None,
                       title=None,
                       prefix=None):
        """
        Register a model to use in the URL field.

        This function needs to be called once for every model
        that should be selectable in the URL field.

        :param ModelClass: The model to register.
        :param form_field: The form field class used to render the field. This can be a lambda for lazy evaluation.
        :param widget: The widget class, can be used instead of the form field.
        :param title: The title of the model, by default it uses the models ``verbose_name``.
        :param prefix: A custom prefix for the model in the serialized database format. By default it uses "appname.modelname".
        """
        cls._static_registry.register(ModelClass, form_field, widget, title,
                                      prefix)

    def formfield(self, **kwargs):
        # Associate formfield.
        # Import locally to avoid circular references.
        from any_urlfield.forms.fields import AnyUrlField as AnyUrlFormField
        kwargs['form_class'] = AnyUrlFormField
        kwargs['url_type_registry'] = self._static_registry
        if 'widget' in kwargs:
            del kwargs['widget']
        return super(AnyUrlField, self).formfield(**kwargs)

    def from_db_value(self, value, expression, connection, context=None):
        # This method is used to cast DB values to python values.
        # The call to to_python() is not used anymore.
        if value is None:
            return None
        return AnyUrlValue.from_db_value(value, self._static_registry)

    def to_python(self, value):
        if isinstance(value, AnyUrlValue):
            return value

        # Convert the string value
        if value is None:
            return None

        return AnyUrlValue.from_db_value(value, self._static_registry)

    def get_prep_value(self, value):
        if isinstance(value, six.string_types):
            # Happens with south migration
            return value
        elif value is None:
            return None if self.null else ''
        else:
            # Convert back to string
            return value.to_db_value()

    def pre_save(self, model_instance, add):
        # Make sure that the SQL compiler in doesn't get an AnyUrlValue,
        # but a regular 'str' object it can write to the database.
        value = super(AnyUrlField, self).pre_save(model_instance, add)
        if not value:
            return None
        else:
            return value.to_db_value()

    def value_to_string(self, obj):
        # For dumpdata and serialization
        value = self.value_from_object(obj)
        return self.get_prep_value(value)

    def validate(self, value, model_instance):
        # Final validation of the field, before storing in the DB.
        super(AnyUrlField, self).validate(value, model_instance)
        if value:
            if value.type_prefix == 'http':
                validate_url = ExtendedURLValidator()
                validate_url(value.type_value)
            elif value.type_value:
                if not value.exists():
                    raise ValidationError(
                        self.error_messages['invalid_choice'] %
                        value.type_value)

    @classmethod
    def resolve_objects(cls, objects, skip_cached_urls=False):
        """
        Make sure all AnyUrlValue objects from a set of objects is resolved in bulk.
        This avoids making a query per item.

        :param objects: A list or queryset of models.
        :param skip_cached_urls: Whether to avoid prefetching data that has it's URL cached.
        """
        # Allow the queryset or list to consist of multiple models.
        # This supports querysets from django-polymorphic too.
        queryset = list(objects)
        any_url_values = []

        for obj in queryset:
            model = obj.__class__
            for field in _any_url_fields_by_model[model]:
                any_url_value = getattr(obj, field)
                if any_url_value and any_url_value.url_type.has_id_value:
                    any_url_values.append(any_url_value)

        AnyUrlValue.resolve_values(any_url_values,
                                   skip_cached_urls=skip_cached_urls)