How can I store the response from a third party API as instance in a Django model

111 views Asked by At

Edit: I have edited my question to include my code:

I am building a web API in DRF. And there's this third party API whose response returns fields like ip_address, latitude, longitude, etc. I have a user model (which is basically inherited from an AbstractBaseUser) that contains fields like email, username, first_name and last_name. Creating an instance of this user model works.

But what I want to do is save response from this third party API as an instance of a model called Artist that has a OneToOneField with the user model whenever an instance of this user model is saved. How can I do this?

Note: I am letting Djoser, a third-party library for handling authentication, take care of the authentication endpoints on my user model. So no views involved in this project so far yet it works.

Here's my code so far:

import uuid
import requests

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from .managers import CustomUserManager



class UserGeoData(models.Model):
    ip_address = models.GenericIPAddressField(default="0.0.0.0")
    city = models.CharField(max_length=50)
    region_iso_code = models.CharField(max_length=5)
    country_code = models.CharField(max_length=5)
    longitude = models.DecimalField(max_digits=9, decimal_places=6)
    latitude = models.DecimalField(max_digits=9, decimal_places=6)

class User(AbstractBaseUser, PermissionsMixin):
    pkid = models.BigAutoField(primary_key=True, editable=False)
    id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    username = models.CharField(verbose_name=_("Username"), max_length=255, unique=True)
    first_name = models.CharField(verbose_name=_("First Name"), max_length=50)
    last_name = models.CharField(verbose_name=_("Last Name"), max_length=50)
    email = models.EmailField(verbose_name=_("Email Address"), unique=True)
    usergeo = models.OneToOneField(UserGeoData, on_delete=models.CASCADE, null=False)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    date_joined = models.DateTimeField(default=timezone.now)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username", "first_name", "last_name"]

    objects = CustomUserManager()

    class Meta:
        verbose_name = _("User")
        verbose_name_plural = _("Users")

    def __str__(self):
        return self.username

    @property
    def get_full_name(self):
        return f"{self.first_name} {self.last_name}"

    def get_short_name(self):
        return self.username
    
    def save(self, *args, **kwargs):
        url = "https://ipgeolocation.abstractapi.com/v1/?api_key=d77f75a5593e4073a6eb1bef9c35e929"
        response = requests.get(url)
        data = response.json()
        obj, created = UserGeoData.objects.get_or_create(
            ip_address=data["ip_address"],
            defaults={
                "city": data["city"],
                "region_iso_code": data["region_iso_code"],
                "country_code": data["country_code"],
                "longitude": data["longitude"],
                "latitude": data["latitude"],
            },
        )
        self.ship = obj
        return super().save()

# class Holiday(models.Model):
#     user = models.OneToOneField(User, on_delete=models.CASCADE)
#     holiday_name = models.CharField(max_length=120)
#     holiday_type = models.CharField(max_length=120)
#     date = models.DateTimeField(auto_now=True)
    
# class Security(models.Model):
#     usergeodata = models.OneToOneField(UserGeoData, on_delete=models.CASCADE)
#     is_vpn = models.BooleanField(default=False)

Here's my User manager:

from django.contrib.auth.base_user import BaseUserManager
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.utils.translation import gettext_lazy as _


class CustomUserManager(BaseUserManager):
    def email_validator(self, email):
        try:
            validate_email(email)
        except ValidationError:
            raise ValueError(_("You must provide a valid email address"))

    def create_user(
        self, username, first_name, last_name, email, password, **extra_fields
    ):
        if not username:
            raise ValueError(_("Users must submit a username"))

        if not first_name:
            raise ValueError(_("Users must submit a first name"))

        if not last_name:
            raise ValueError(_("Users must submit a last name"))

        if email:
            email = self.normalize_email(email)
            self.email_validator(email)
        else:
            raise ValueError(_("Base User Account: An email address is required"))

        user = self.model(
            username=username,
            first_name=first_name,
            last_name=last_name,
            email=email,
            **extra_fields
        )

        user.set_password(password)
        extra_fields.setdefault("is_staff", False)
        extra_fields.setdefault("is_superuser", False)
        user.save(using=self._db)
        return user

    def create_superuser(
        self, username, first_name, last_name, email, password, **extra_fields
    ):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        extra_fields.setdefault("is_active", True)

        if extra_fields.get("is_staff") is not True:
            raise ValueError(_("Superusers must have is_staff=True"))

        if extra_fields.get("is_superuser") is not True:
            raise ValueError(_("Superusers must have is_superuser=True"))

        if not password:
            raise ValueError(_("Superusers must have a password"))

        if email:
            email = self.normalize_email(email)
            self.email_validator(email)
        else:
            raise ValueError(_("Admin Account: An email address is required"))

        user = self.create_user(
            username, first_name, last_name, email, password, **extra_fields
        )
        user.save(using=self._db)
        return user
1

There are 1 answers

12
Niko On

Basically, by overriding your custom user save method. Here is a simple example using SWAPI that assigns a Ship for an user:

models.py

import requests as req


class Ship(models.Model):
    name = models.CharField(max_length=100)
    model = models.CharField(max_length=255)
    manufacturer = models.CharField(max_length=255)
    hyperdrive_rating = models.FloatField()
    starship_class = models.CharField(max_length=255)


class User(AbstractUser):
    # supposedly you want to maintain the ship in this case.
    # but may want to get rid of your actual values with models.CASCADE
    ship = models.OneToOneField(Ship, on_delete=models.DO_NOTHING)

    def save(self, *args, **kwargs):
        res = req.get("https://swapi.dev/api/starships/10")
        data = res.json()
        obj, created = Ship.objects.get_or_create(
            name=data["name"],
            defaults={
                "model": data["model"],
                "manufacturer": data["manufacturer"],
                "hyperdrive_rating": data["hyperdrive_rating"],
                "starship_class": data["starship_class"],
            },
        )
        self.ship = obj
        return super().save()

Used AbstractUser for the sake of simplicity.

P.S. you want python requests