Proper way to consume data from RESTFUL API in django
Asked Answered
L

4

68

I'm trying to learn django so while I have a current solution I'm not sure if it follows best practices in django. I would like to display information from a web api on my website. Let's say the api url is as follows:

http://api.example.com/books?author=edwards&year=2009

Thsis would return a list of books by Edwards written in the year 2009. Returned in the following format:

{'results':
             [
                {
                   'title':'Book 1',
                   'Author':'Edwards Man',
                   'Year':2009
                },
                {
                   'title':'Book 2',
                   'Author':'Edwards Man',
                   'Year':2009}
           ]
}

Currently I am consuming the API in my views file as follows:

class BooksPage(generic.TemplateView):
    def get(self,request):
        r = requests.get('http://api.example.com/books?author=edwards&year=2009')
        books = r.json()
        books_list = {'books':books['results']}
        return render(request,'books.html',books_list)

Normally, we grab data from the database in the models.py file, but I am unsure if I should be grabbing this API data in models.py or views.py. If it should be in models.py, can someone provide an example of how to do this? I wrote the above example sepecifically for stackoverflow, so any bugs are purely a result of writing it here.

Leucocyte answered 15/5, 2015 at 12:22 Comment(2)
Check out Django Rest Framework.Bringingup
Isn't that for building web apis? I'm trying to read from an external one.Leucocyte
L
143

I like the approach of putting that kind of logic in a separate service layer (services.py); the data you are rendering is quite not a "model" in the Django ORM sense, and it's more than simple "view" logic. A clean encapsulation ensures you can do things like control the interface to the backing service (i.e., make it look like a Python API vs. URL with parameters), add enhancements such as caching, as @sobolevn mentioned, test the API in isolation, etc.

So I'd suggest a simple services.py, that looks something like this:

def get_books(year, author):
    url = 'http://api.example.com/books' 
    params = {'year': year, 'author': author}
    r = requests.get(url, params=params)
    books = r.json()
    books_list = {'books':books['results']}
    return books_list

Note how the parameters get passed (using a capability of the requests package).

Then in views.py:

import services
class BooksPage(generic.TemplateView):
    def get(self,request):
        books_list = services.get_books('2009', 'edwards')
        return render(request,'books.html',books_list)

See also:

Lorou answered 18/5, 2015 at 21:2 Comment(4)
Am I missing something here? get_books() is not returning a variable ...Metrist
That was an omission in the answer. Corrected.Lorou
@Lorou - can one use this approach for CRUD operations - or - should some kind of serialization take place? Have seen these options : django-rest-framework.org/api-guide/serializers/… pypi.python.org/pypi/django-rest-models TIAMillpond
Absolutely. The scope of the answer is actually very narrow. It just recommends encapsulating interactions with external services, leaving wide latitude in how you do that. That depends on the nature of the service. If it has a deeply JSON structure, manually unpacking dicts gets tedious, so using Rest Framework serializers to build Python objects is reasonable. If the service allowed for CRUD, you might opt for per-verb functions in service.py - get_book, put_book, delete_book etc.Lorou
K
3

Use the serializer instead of .json, as it gives flexibility to return in a number of formats.As while using rest-api , the provided serializer use is preferred.

Also keep the data handling and get data requests in view.py.The forms are used for templating not as the business logic.

Knuckleduster answered 21/5, 2015 at 7:41 Comment(0)
A
1

Well, there are several things to keep in mind. First of all, in this case your data is not changing so often. So it is a good practice to cache this kind of responces. There are a lot of caching tools around, but redis is a popular option. Alternatevly, you can choose additional NoSQL database just for caching.

Secondly, what is the purpose of displaying this data? Are you expecting your users to interact with books or authors, etc? If it is just an information, there is no need in forms and models. If not, you must provide proper views, forms and models for both books and authors, etc.

And considering the place where you should call an API request, I would say it dependes heavily on the second question. Choices are:

  • views.py for just displaying data.
  • forms.py or still views.py for ineractivity.
Aguilera answered 18/5, 2015 at 18:12 Comment(0)
A
-2
 <tbody>
                {% if libros %}
                {% for libro in libros %}
                <tr>
                    <td>{{ libro.id }}</td>
                    <td>{{ libro.titulo }}</td>
                    <td>{{ libro.autor }}</td>
                    <td>{{ libro.eiditorial }}</td>
                    <td>{{ libro.descripcion }}</td>
                    <td>{{ libro.cantidad }}</td>
                    <td>{{ libro.Bodega.nombre }}</td>
                    <td>
                        {% if libro.imagen %}
                    <td><img src= "{{ libro.imagen.url }} "align="center" width="50px" ></td>
                    {% else %}
                    <h6>no hay imagen de libros</h6>
                    {% endif %}
                    </td>
                    <td><a class ="btn btn-primary "href="/">Editar</a></td>
                    <td><a class ="btn btn-danger red"href="/">Eliminar</a></td>
                </tr>
                {% endfor %}
                {% else %}
                <h1>no hay registros de libros</h1>
                {% endif%}
            </tbody>

as I can call it in an html when I already have a list of my local application greetings

Aquino answered 24/6, 2019 at 2:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.