1

I'm using wagtailgmaps for add google maps feature to my project. Use it is easy, and i want add a stream block for use Google Maps block to the content.

enter image description here

But, we have a big problem, this package only add a new edit_handler, it doesn't add a new model or field, only add a new handler for the admin.

So, my question is, how can i create a Google Maps stream block adding the handler feature?

Thanks!

SalahAdDin
  • 2,023
  • 25
  • 51

1 Answers1

1

For create a new StreamBlock do you need create a widget for your field:

 def render_form(self, value, prefix='', errors=None):
        widget = self.field.widget

        widget_attrs = {'id': prefix, 'placeholder': self.label}

        field_value = self.value_for_form(value)

        if hasattr(widget, 'render_with_errors'):
            widget_html = widget.render_with_errors(prefix, field_value, attrs=widget_attrs, errors=errors)
            widget_has_rendered_errors = True
        else:
            widget_html = widget.render(prefix, field_value, attrs=widget_attrs)
            widget_has_rendered_errors = False

        return render_to_string('wagtailadmin/block_forms/field.html', {
            'name': self.name,
            'classes': self.meta.classname,
            'widget': widget_html,
            'field': self.field,
            'errors': errors if (not widget_has_rendered_errors) else None
        })

Normally, all blocks in the base core have this function, and you can see that use widget.

So, first we have to create a widget for the map:

class MapsField(HiddenInput):
    ***
    def __init__(self, *args, **kwargs):
        self.address_field = kwargs.pop('address_field', self.address_field)
        ***

        super(MapField, self).__init__(*args, **kwargs)

    class Media:
        css = {
            'all': ('*/**/geo-field.css',)
        }

        js = (
            '*/**/geo-field.js',
            'https://maps.google.com/maps/api/js?key={}&libraries=places'
            .format(
                GOOGLE_MAPS_V3_APIKEY
            ),
        )

    def render(self, name, value, attrs=None):
        out = super(MapField, self).render(name, value, attrs)

        location = format_html(
            '<div class="input">'
            '<input id="_id_{}_latlng" class="geo-field-location" maxlength="250" type="text">'  # NOQA
            '</div>',
            name
        )

        ***

        return mark_safe(
            '<script>window["{}"] = {};</script>'.format(data_id, json_data) +
            out +
            location +
            '<div class="geo-field" data-data-id="{}"></div>'.format(data_id) +
            """
            <script>
            (function(){
                if (document.readyState === 'complete') {
                    return initializeGeoFields();
                }
                $(window).load(function() {
                    initializeGeoFields();
                });
            })();
            </script>
            """
        )

Now, we will create a new block extending the default wagtail block:from

from wagtail.wagtailcore.blocks import FieldBlock
from wagtailgeowidget.widgets import GeoField

def __init__(self, address_field=None, required=True, help_text=None,
                 **kwargs):
        self.field_options = {}
        self.address_field = address_field
        super(GeoBlock, self).__init__(**kwargs)

    @cached_property
    def field(self):
        field_kwargs = {'widget': GeoField(
            srid=4326,
            id_prefix='',
            address_field=self.address_field,
        )}
        field_kwargs.update(self.field_options)
        return forms.CharField(**field_kwargs)

    def clean(self, value):
        if not value:
            value = "SRID={};POINT({} {})".format(
                4326,
                GEO_WIDGET_DEFAULT_LOCATION['lng'],
                GEO_WIDGET_DEFAULT_LOCATION['lat']
            )
        return super(GeoBlock, self).clean(value)

    def render_form(self, value, prefix='', errors=None):
        if value and isinstance(value, dict):
            value = "SRID={};POINT({} {})".format(value['srid'],
                                                  value['lng'],
                                                  value['lat'])
        return super(GeoBlock, self).render_form(value, prefix, errors)

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

        value = geosgeometry_str_to_struct(value)
        value = {
            'lat': value['y'],
            'lng': value['x'],
            'srid': value['srid'],
        }

        return super(MapBlock, self).to_python(value)

This is basically the structure of wagtail block.

SalahAdDin
  • 2,023
  • 25
  • 51