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
Basically, by overriding your custom user
save
method. Here is a simple example using SWAPI that assigns aShip
for an user:models.py
Used
AbstractUser
for the sake of simplicity.P.S. you want python requests