0

Is it possible to make a model field that consumes a REST API as a foreign key?

I have two projects. The first project has this model in models.py:

from django.db import models
from django_countries.fields import CountryField
from django.urls import reverse

class Currency(models.Model):
    name = models.CharField(max_length = 50)
    country = CountryField()
    code = models.CharField(max_length = 3)
    base = models.BooleanField()

    class Meta:
        ordering = ['code']

    def __str__(self):
        return (self.code)

    def get_absolute_url(self):
        return reverse('currency_detail', args = [str(self.id)])

I have serialized the model with the following code in serializers.py:

from rest_framework import serializers
from currencies . models import Currency

class CurrencySerializer(serializers.ModelSerializer):
    class Meta:
        model = Currency
        fields = ('name', 'code')

I created the following views.py:

from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

from currencies . models import Currency
from . serializers import CurrencySerializer


def currency_list(request):
    currency = Currency.objects.all()
    serializer = CurrencySerializer(currency, many = True)
    return JsonResponse(serializer.data, safe = False)

and provided the following in urls.py:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^currencies/$', views.currency_list),
]

The URL is passing the information ok in JSON format. There are no authentications (I am not good enough yet). When I use the requests library and make a get request, it returns the following:

[{"name": "Krone", "code": "DKK"}, {"name": "Pound Sterling", "code": "GBP"}, {"name": "Cedi", "code": "GHS"}]

I would like to consume this information in a new project which will have a different database and post records with the following model:

class JournalEntry(models.Model):
    date = DateField()
    currency = ForeignKey(consuming the json data so it renders as a dropdown menu in html)
    value = IntegerField()

Is there a good way to do something like this? I haven't found any responses that help me conceptualize this properly. Also it is to help me with my learning. I intend to implement more complicated projects with this API-based approach. Thanks.

2
  • In your second project (where the JournalEntry is defined), don't you have access to the Currency model of the first project? I mean... Can't you do something along the lines currency = ForeignKey('other_project.Currency...)`? Commented Jan 6, 2018 at 20:14
  • I think your approach works well when everything is on my computer. I was concerned about how I'd access the data from Project 1 when I deployed it to Elastic Beanstalk. I will try it out in production and get back to you. Thanks for your review. I deeply appreciate it. Commented Jan 8, 2018 at 16:10

1 Answer 1

1

You have to change your mindset a little with APIs. API is a data source and you have to ask yourself, how are you going to use this data source and what kind of data you are receiving.

How you're going to implement an API depends on answers. In many cases, APIs act as a database. Instead of querying database you query API and from this perspective it doesn't make much sense to store this data in another database and then query the same data from there. Then there are situation when API provides only current data and not historical and you'd like to have historical data available to you as well. In this case it makes total sense to store API data in your database. There are other situations as well, but these two are probably the most frequent.

If you translate that to your case, using data as it is without storing might be the preferred choice, because you maybe want that users are able to select only currencies that are currently available. The code in this case would be:

models.py

class JournalEntry(models.Model):
    date = DateField()
    currency = CharField(max_length=4)
    value = IntegerField()

forms.py

class JournalEntryForm(forms.ModelForm):
    class Meta:
        model = JournalEntry
        fields = '__all__'

views.py

def currency(request):
    form = JournalEntryForm()
    r = requests.get('currencies_url')
    currencies = r.json()
    if form.is_valid():
        form.save()

    return render(request, 'currency.html', {'currencies': currencies, 'form': form})

currency.html

<html>
  <body>
    <form method="POST">{% csrf_token %}
      {% for field in form %}
      <p>{{ field.date }}</p>
      <p>
        <select name="currency" id="id_currency" required="">
          {% for currency in currencies %}
          <option value="{{ currency.code }}">{{ currency.name }}</option>
          {% endfor %}
        </select>
      </p>
      <p>{{ field.value }}</p>
      <input type="submit" value="Save">
    </form>
  </body>
</html>

If you want currency date to be saved in your database for whatever reason, then you first need to create entries with data received from API. The code would like something like this:

models.py

class Currency(models.Model):
    name = models.CharField(max_length=50)
    code = models.CharField(max_lenght=4)

class JournalEntry(models.Model):
    date = DateField()
    currency = ForeignKey(Currency)
    value = IntegerField()

forms.py

... same as before ...

views.py

def get_currencies(request):
    r = requests.get('currencies_url')
    currencies = r.json()
    currency_data = []
    for currency in currencies:
        c = Currency()
        c.name = currency['name']
        c.code = currency['code']
        currency_data.append(c)

   if len(currency_data) > 0:
       bulk_create  = Currency.objects.bulk_create(currency_data)

   return HttpResponse('Done!')

def currency(request):
    form = JournalEntryForm()
    if form.is_valid():
        form.save()

    return render(request, 'currency.html', {'form': form})

currency.html

<html>
  <body>
    <form method="POST">{% csrf_token %}
      {{ form.as_p }}
      <input type="submit" value="Save">
    </form>
  </body>
</html>

Code was a bit simplified, but shows a basic principle. I used bulk_create to save currency data received from API because it inserts list of objects in an efficient manner with only 1 query. The rest should probably be self-explanatory.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much. This is very helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.