wagtailmodelchooser icon indicating copy to clipboard operation
wagtailmodelchooser copied to clipboard

Missing information on how to customize the modal

Open jeffreyfabrique opened this issue 2 years ago • 3 comments

Is it possible to provide examples on how to use the modelchooser in version 4.0.1 (with Wagtail 4.1)? In the past I could easily extend the templates for showing more properties in the modal and also add a search form to it. Basically the last two steps in version 3: https://pypi.org/project/wagtail-modelchooser/3.0.0/

Thank you so much in advance!

jeffreyfabrique avatar Oct 05 '23 14:10 jeffreyfabrique

No worries, most of that functionality is still there, some of the templates and paths have changes so it was easier to remove that section that it was to update it. If you're considering a heavily customised chooser, I'd recommend looking further into the ChooserViewSet docs, they are very brief but once you dive into the class there's lots of customisation options.

If you wanted do the example from the old readme you could do something like this -


@register_model_chooser
class CityChooser(Chooser):
    model = City
    modal_template = 'wagtailmodelchooser/city_modal.html'
    modal_results_template = \
        'wagtailmodelchooser/city_modal_results.html'

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.GET.get('capital'):
            qs = qs.filter(capital=request.GET.get('capital') == '0')

        return qs
{% extends "wagtailmodelchooser/modal.html" %}

{% block search_fields %}
<ul class="fields">
	<li>
		<div class="w-field__wrapper">
			<label class="w-field__label" for="id_q" id="id_q-label">
				Search term
			</label>
			<div class="w-field w-field--char_field w-field--text_input">
				<div class="w-field__input">
					<input type="text" name="q" placeholder="Search" id="id_q">					
				</div>
				<div class="w-field__input">
					<input type="checkbox" name="capital">				
				</div>
			</div>
		</div>
	</li>
</ul>
{% endblock %}
{% extends "wagtailmodelchooser/results.html" %}
{% block results_listing %}
<table>
   <thead>
       <tr>
           <th>Name</th>
           <th>Is Capital</th>
       </tr>
   </thead>
   <tbody>
        {% for result in results %}
        <tr>
            <td>{{ result }}</td>
            <td>{{ result.capital }}</td>
        </tr>
  </tbody>
 </table>
{% endblock %}

Bit messy and I have no idea if this would work properly, the equivalent using the ChooserViewSet would looks something like this

# This is the 'search' view
class CityChooseView(ChooseView):
  filter_form_class = CityFilter # A filterset class that inherits from BaseFilterForm and SearchFilterMixin
  
  @property
  def columns(self):
     columns = super().columns
     return columns + [
          TitleColumn("capital", label="Is Capital?")
     ]

class CityChooserViewSet(ChooserViewSet):
  model = City  
  chooser_view_class = CityChooserView

seb-b avatar Oct 06 '23 00:10 seb-b

Hey @seb-b,

Thank you for the quick response and providing an example. When I try to use the above I don't see the form though.

{% extends "wagtailmodelchooser/modal.html" %}

{% block search_fields %}
<ul class="fields">
	<li>
		<div class="w-field__wrapper">
			<label class="w-field__label" for="id_q" id="id_q-label">
				Search term
			</label>
			<div class="w-field w-field--char_field w-field--text_input">
				<div class="w-field__input">
					<input type="text" name="author" placeholder="Author" id="id_author">
				</div>
			</div>
		</div>
	</li>
</ul>
{% endblock %}
@register_model_chooser
class BlogAuthorSnippetChooser(Chooser):
    model = BlogAuthorSnippet
    modal_template = 'snippet/blog_author_modal.html'
    # modal_results_template = 'snippet/blog_author_modal_results.html'

    def get_queryset(self, request):
        qs = super().get_queryset(request)

        if request.GET.get('author'):
            qs = qs.filter(name__icontains=request.GET.get('author'))

        return qs
Screenshot 2023-10-06 at 09 12 26

These are my dependencies: wagtail==4.1.6 wagtail-modelchooser==4.0.1

I have tried overriding the template or even use is_searchable. But I can't seem to add a custom field to it (or a new property in the list).

jeffreyfabrique avatar Oct 06 '23 07:10 jeffreyfabrique

I managed to do it with a ChooseViewSet!

Screenshot 2023-10-06 at 11 06 02
class BlogAuthorSnippet(index.Indexed, models.Model):
    name = models.CharField(max_length=255)
    locale = models.ForeignKey('wagtailcore.Locale', on_delete=models.CASCADE, related_name='+')

    search_fields = [
        index.SearchField('name', partial_match=True)
    ]

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('Blog author')
        verbose_name_plural = _('Blog authors')


class BlogAuthorChooseView(ChooseView):
    @property
    def columns(self):
        columns = super().columns

        return columns + [
            Column("locale", label="Locale")
        ]


class BlogAuthorChooserViewSet(ChooserViewSet):
    model = BlogAuthorSnippet
    choose_view_class = BlogAuthorChooseView
    choose_one_text = "Choose an author"
    edit_item_text = "Edit this author"
    form_fields = ["name", "locale"]


blog_author_chooser_viewset = BlogAuthorChooserViewSet("blog_author_chooser")


@hooks.register("register_admin_viewset")
def register_viewset():
    return blog_author_chooser_viewset

My biggest issue was that I was using filter.IndexField instead of index.SearchField(partial=True). But of course @register_model_chooser is a lot shorter and would prefer to use it like that. The only problem is that I can't seem to add a new column (in my case locale), because the listing template loops through a table instance. Which is why the example given above doesn't work anymore. If you can fix this that would be amazing!

jeffreyfabrique avatar Oct 06 '23 09:10 jeffreyfabrique