Ports and Adapters in python - part two

Posted on Sun 05 June 2016

Last time I wrote about how to do simple port & adapter in python. In this post, I will show to actually use them.

I briefly remind you what is purpose of application build in this series: user will log in, then search with keyword so he can save any search result to database for read it later.

I decided to first implement search mechanism for Reddit. This is what I will write today. Search request will be sent via GET. First, I need some form to handle this:

 1  from django import forms
 2  from django.conf import settings
 3 
 4  from external_api.external_api_port import instantiated_port
 5 
 6  class RedditSearchForm(forms.Form):
 7      query = forms.CharField(label='search query', max_length=100)
 8 
 9      def perform_search(self):
10          search_result = instantiated_port.search(self.cleaned_data['query'])
11          return search_result

I defined simple form that has only one field: query which is CharField field with label. My form has one method perform_search. In this method, I import instantiated reddit port that takes instance of reddit adapter with settings from django settings module. Idealy this adapter should be singleton class. This is how it looks in reddit_adapter:

from django.conf import settings

# reddit adapter class here ...

instantiated_adapter = RedditAdapter(
    settings.REDDIT_CLIENT_ID,
    settings.REDDIT_CLIENT_SECRET,
    settings.REDDIT_USERNAME,
    settings.REDDIT_PASSWORD
)

and in external_api_port:

from .reddit_adapter import instantiated_adapter

# port class here ...

instantiated_port = ExternalAPIPort(instantiated_adapter)

Lastly, I perform the search using the port and cleaned_data['query']. I have access to cleaned_data attribute after form validation which will be shown in the view. At the end of perform_search I return search results. These results are processed further in view:

 1 from django.views.generic.edit import FormView
 2 from django.http import HttpResponse
 3 from django.shortcuts import render
 4 from .forms import RedditSearchForm
 5 
 6 class RedditSearchView(FormView):
 7     template_name = 'search/index.html'
 8     form_class = RedditSearchForm
 9     success_url = 'add-to-favourites'
10     search_result = None
11 
12     def get(self, request, *args, **kwargs):
13         form = self.form_class(self.request.GET or None)
14         if form.is_valid():
15             self.search_result = form.perform_search()
16         return self.render_to_response(self.get_context_data(form=form))
17 
18     def get_context_data(self, **kwargs):
19         context = super(RedditSearchView, self).get_context_data(**kwargs)
20         if self.search_result:
21             context.update({
22                 'search_result': self.search_result,
23                 'sucess': True
24                 }
25             )
26         return context

Let begin from get method: this method is called every time get request is performed by the user. How to ensure that? I used method parameter in html:

<form method="get" class="form" role="form">
    {{ form }}
    <input type="submit" class="btn btn-primary" value="Search">
</form>

In get method I get the form for given request.GET. On this form I call form.is_valid() to get access to cleaned_data. After that I have search results so I can insert them to html. It is done via get_context_data method when I get my basic context calling super. And if there was search performed I update context with search results and I tell my html to render them in one template.

Such updated context is taken by django and rendered to full html. Key success is present because I got if statement in html template which allows me to render results on the same page that search was performed:

{% if sucess %}
    {% for item in search_result %}
        <li>{{ item }}</li>
    {% endfor %}
{% else %}
<!--- form here ---!>

And that basically all for search view. In next post I will take care of saving results to database. Code for this you can find under this repo.

Changes from 07.06.16:

  • Moving port & adapter to it's own module
  • Having only one instance of port & adapter

(Special thanks for pointing this to Mariusz)

Cover image by Creative Magic under CC0.

tags: django,

Comments !