Django models: Access to a multilevel link between tables - python

I have a problem to access certain data in my models.
I have a User model giving its id to a Profile model which is giving its id to a ProfileA model.
And when I create a User it automatically creates a Profile.
Here is my user_model
from django.db import models
from django.contrib.auth.models import AbstractUser
from django_countries.fields import CountryField
from .model_custom_user_manager import CustomUserManager
class User(AbstractUser):
"""auth/login-related fields"""
is_a = models.BooleanField('a status', default=False)
is_e = models.BooleanField('e status', default=False)
# objects = CustomUserManager()
def __str__(self):
return "{} {}".format(self.first_name, self.last_name)
My profile_model:
from django.db import models
from django_countries.fields import CountryField
from django.contrib.auth import get_user_model
User = get_user_model()
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
"""non-auth-related/cosmetic fields"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
birth_date = models.DateTimeField(auto_now=False, auto_now_add=False, null=True)
nationality = CountryField(null=True)
def __str__(self):
return f'{self.user.username} Profile'
"""receivers to add a Profile for newly created users"""
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
And what I'm trying to do is create a ProfileA when a Profile is created AND when a User has is_a==True.
Here is my profilea_model:
from django.db import models
from .model_profile import *
from django.db.models.signals import post_save
from django.dispatch import receiver
class ProfileA(models.Model):
profile = models.ForeignKey(Profile, null=True, on_delete=models.CASCADE, related_name='profile_a')
biography = models.CharField(max_length=200, null=True)
def __str__(self):
return "{}".format(self.profile)
#receiver(post_save, sender=Profile)
def create_profile_profile_a(sender, instance, created, **kwargs):
if created & Profile.user.is_a == True:
ProfileA.objects.create(profile=instance)
And when I try creating a User with Postman:
{
"username":"Pepe",
"password":"Yolo1234",
"first_name":"Pepe",
"last_name":"Pepito",
"email":"pepe#yolo.com",
"is_a":true,
"is_e":false
}
I get the error:
AttributeError at /users/
'ForwardOneToOneDescriptor' object has no attribute 'is_a'
I've tried a lot of things but nothing woks and using the DjangDocs I can't understand.
What am I doing wrong?
Thanks for your responses!

The instance argument of the create_profile_profile_a function is the instance of Profile which can be easily pointed to User and further get the target attribute is_a.
The receiver part in profilea_model should change to:
#receiver(post_save, sender=Profile)
def create_profile_profile_a(sender, instance, created, **kwargs):
if created & instance.user.is_a == True:
ProfileA.objects.create(profile=instance)

Related

Queries related to Django Signals

To begin with, Here Profile and Seller model is created when a User model is created through Signals.What I want to do is When profile model is first created or updated,I want all the fields of Seller model to be same as all fields of Profile.Similarly when I first created Seller model,I also want all fields of Profile Model to be same as that of Seller model.But,I couldn't figure out how to do?
from typing import Tuple
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.db.models.fields import DecimalField
from django.dispatch import receiver
from django.db.models.signals import post_save
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True) #cascade is for deleting the customer
first_name = models.CharField(max_length=10, null=True)
second_name=models.CharField(max_length=10,null=True)
email = models.EmailField(max_length=70, null=True,blank=True)
#receiver(post_save, sender=User)
def create_profile(sender, instance,created,**kwargs):#Signal receivers must accept keyword arguments (**kwargs).
if created:
Profile.objects.create(user=instance)
...
#receiver(post_save, sender=Profile)
def create_seller(sender, instance,created,**kwargs):#Signal receivers must accept keyword arguments (**kwargs).
if created:
Seller.objects.create(user=instance)
class Seller(models.Model):
user = models.OneToOneField(Profile, on_delete=models.CASCADE, null=True, blank=True) #cascade is for deleting the customer
first_name = models.CharField(max_length=10, null=True)
second_name=models.CharField(max_length=10,null=True)
email = models.EmailField(max_length=70, null=True,blank=True)
...
If created=True it means a Profile object is created. otherwise if created=False it means that a Profile object has been updated.
When created=True you need to create a Seller object and when created=False you need to update a Seller object.
You can do this:
#receiver(post_save, sender=Profile)
def update_or_create_seller(sender, instance, created, **kwargs):
if created:
Seller.objects.create(
user=instance.user,
first_name=instance.first_name,
second_name=instance.second_name,
email=instance.email
)
else:
seller = instance.user.seller
seller.first_name = instance.first_name
seller.last_name = instance.second_name
seller.email = instance.email
seller.save()
Also notice that unless you define related_name in your OneToOneField, Django will use lowercased model name to access related object. So, instance.user.seller should work.

Django: UNIQUE constraint failed: user.username

I have a problem using Djangos Default user from django.contrib.auth.models but trying to use it with my custom User model, using from django.contrib.auth.models import AbstractUser.
So here is my User model:
from django.db import models
from django.contrib.auth.models import AbstractUser, UserManager as AbstractUserManager
# from django.conf import settings
from django_countries.fields import CountryField
# https://github.com/SmileyChris/django-countries
from django.db.models.signals import post_save
from django.dispatch import receiver
class UserManager(AbstractUserManager):
pass
class User(AbstractUser):
"""auth/login-related fields"""
is_a = models.BooleanField('a status', default=False)
is_o = models.BooleanField('o status', default=False)
def __str__(self):
return "{} {}".format(self.first_name, self.last_name)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
User.objects.set_password(instance.password)
and here is my Profile model:
from django.db import models
from django_countries.fields import CountryField # https://github.com/SmileyChris/django-countries
from django.contrib.auth import get_user_model
User = get_user_model()
# https://medium.com/swlh/best-practices-for-starting-a-django-project-with-the-right-user-model-290a09452b88
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
"""non-auth-related/cosmetic fields"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='Profile')
birth_date = models.DateTimeField(auto_now=False, auto_now_add=False, null=True)
nationality = CountryField(null=True)
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, null=True)
def __str__(self):
return f'{self.user.username} Profile'
"""receivers to add a Profile for newly created users"""
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
But before adding User.objects.set_password(instance.password) to my User model, I did it in py shell, which worked perfectly.
But now I added this line in my User model, and when I'm using Postman to post a User:
{
"username":"1564",
"password":"12345678",
"first_name":"Al",
"last_name":"Pongvf",
"email":"ahgj#live.fr"
}
It gives me this error message:
IntegrityError at /user/create/
UNIQUE constraint failed: ep_user.username
Which I don't get it because I'm creating a new user with a total new username.
I've searched for a solution, but I didn't get lucky:
StackOverflow
1
StackOverflow
2
StackOverflow
3
StackOverflow
4
Does anyone know what am I missing? or doing wrong?
Thanks!
UPDATE:
Here is my User Serializer:
from rest_framework import serializers
from django.contrib.auth import get_user_model
from ..models.model_user import *
# from .serializers_profile import *
class UserIndexSerializer(serializers.ModelSerializer):
# profile = ProfileIndexSerializer()
class Meta:
model = User
fields = [
'id',
'username',
'password',
'first_name',
'last_name',
'email',
'is_a',
'is_o'
# 'profile'
]
class UserCreateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'username',
'password',
'first_name',
'last_name',
'email',
'is_a',
'is_o'
]
class UserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
And here is my User View:
from rest_framework import generics
from django.contrib.auth import get_user_model
from ..models.model_user import *
from ..serializers.serializers_user import *
class UserIndex(generics.ListAPIView):
"""List all users, or create a new user."""
queryset = User.objects.all().order_by('id')
serializer_class = UserIndexSerializer
class UserCreate(generics.CreateAPIView):
"""List all art pieces, or create a new art piece."""
queryset = User.objects.all()
serializer_class = UserCreateSerializer
class UserDetails(generics.RetrieveUpdateDestroyAPIView):
"""Retrieve, update or delete a user instance."""
queryset = User.objects.all()
serializer_class = UserDetailsSerializer
And my admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
# Register your models here.
from .models.model_user import *
from .models.model_profile import *
admin.site.register(User, UserAdmin)
admin.site.register(Profile)

How to add extra fields to registration end point of rest-auth

I am using rest-auth registration api for user registration. I have some extra fields in the UserProfile model.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
org_id = models.CharField(max_length=100, default='')
is_teacher = models.BooleanField(blank=True, default=False)
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
The UserProfile model is shown above. How can I add these fields to rest-auth regestration api endpoint and save the data to database.
I found an answer for myself
The serializers can be written as
from rest_framework import serializers
from rest_auth.registration.serializers import RegisterSerializer
from .models import UserProfile
class RegistrationSerializer(RegisterSerializer):
first_name = serializers.CharField(required=False)
last_name = serializers.CharField(required=False)
personal_id = serializers.CharField(required=True)
def custom_signup(self, request, user):
user.first_name = self.validated_data.get('first_name', '')
user.last_name = self.validated_data.get('last_name', '')
user.userprofile.personal_id = self.validated_data.get(
'personal_id', '')
user.save(update_fields=['first_name', 'last_name'])
user.userprofile.save(update_fields=['org_id'])
I didnt add the is_teacher because its optional.
In views.py extend the RegisterView of the rest_auth.regeistration.views to pass this data and its done.
class RegistrationView(RegisterView):
serializer_class = RegistrationSerializer
And finally add a url and pass RegisterView.as_view().

Django 2.1.3/SQLite : UNIQUE constraint failed: users_profile.user_id || while trying to access my superuser account

i am working on a website that has a social appeal and i have to create an AbstractUser Model to store extra info about the users; while doing so, i ran into this error while trying to log into django admin page with my superuser account.
UNIQUE constraint failed: users_profile.user_id.
i have rebuilt the project 3 times and the issue still occurs.
here's my models/forms/signals/admin.py files
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.conf import settings
from PIL import Image
# Create your models here.
class UserModel(AbstractUser):
bio = models.TextField(max_length=500, blank=True, null=True)
location = models.CharField(max_length=35, blank=True, null=True)
birthday = models.DateField(blank=True, null=True)
url = models.URLField(blank=True, null=True)
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
image = models.ImageField(default='default.png', upload_to="profile_pics")
def __str__(self):
return self.user.username
def save(self, *args, **kwargs):
super(Profile, self).save(*args, **kwargs)
path = self.image.path
img = Image.open(path)
if img.height > 500 and img.width > 500:
output_size = (500, 500)
img.thumbnail(output_size)
img.save(path)
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import UserModel, Profile
class UserModelCreationForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = UserModel
fields = [
'username',
'password1',
'password2',
'email',
]
class UserModelChangeForm(UserChangeForm):
email = forms.EmailField()
class Meta:
model = UserModel
fields = [
'username',
'email',
]
class ProfileUpdateForm(forms.ModelForm):
model = Profile
fields = [
'image',
]
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.conf import settings
from .models import Profile
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_user_profile(sender, instance, created, **kwargs):
Profile.objects.create(user=instance)
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def update_user_profile(sender, instance, **kwargs):
instance.profile.save()
admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from .models import UserModel, Profile
from .forms import UserModelCreationForm, UserModelChangeForm
# Register your models here.
#admin.register(UserModel)
class UserModelAdmin(UserAdmin):
add_form = UserModelCreationForm
form = UserModelChangeForm
model = UserModel
list_display = [
'username', 'email', 'first_name', 'last_name',
]
admin.site.register(Profile)
i have added AUTH_USER_MODEL = 'users.UserModel' to settings.py
i have added default_app_config = 'users.apps.UsersConfig' to users/__init__.py to automatically create profiles.
i have added def ready(self): import users.signals to apps.py
all i am trying to do is access my superuser account. i have created 7 different superusers (just to check) they all raise the same error.
I think the signals are redundant. Only one signal should be fine, it should be like this:
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)

Django models.py OneToOneField --- help creating a relationship between two models

I am having trouble testing a relationship between two models (CustomUser and Profile) located in different apps. I'm hoping someone can identify where I am going wrong here:
Here is my profiles/models.py --- you can see my user field attempting to create a OneToOne with with my users/models.py:
from django.db import models
from core.models import TimeStampedModel
class Profile(TimeStampedModel):
user = models.OneToOneField('users.CustomUser', on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
bio = models.TextField(blank=True)
image = models.URLField(blank=True)
def __str__(self):
return self.user.username
Here is my users/models.py:
class CustomUser(AbstractBaseUser, PermissionsMixin, TimeStampedModel):
username = models.CharField(db_index=True, max_length=255, unique=True)
email = models.EmailField(db_index=True, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_provider = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = CustomUserManager()
def __str__(self):
return self.email
#property
def token(self):
return self._generate_jwt_token()
def get_short_name(self):
return self.username
def _generate_jwt_token(self):
dt = datetime.now() + timedelta(days=60)
token = jwt.encode({
'id': self.pk,
'exp': int(dt.strftime('%s'))
}, settings.SECRET_KEY, algorithm='HS256')
return token.decode('utf-8')
So the idea is that when I create a new user, a profile is automatically created as well. To do this, I am using a post_save signal in my users app:
users/signals.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from conduit.apps.profiles.models import Profile
from .models import User
#receiver(post_save, sender=User)
def create_related_profile(sender, instance, created, *args, **kwargs):
if instance and created:
instance.profile = Profile.objects.create(user=instance)
And finally an update to my users/init.py file:
from django.apps import AppConfig
class UsersAppConfig(AppConfig):
name = 'django.users'
label = 'users'
verbose_name = 'Users'
def ready(self):
import users.signals
default_app_config = 'django.users.UsersAppConfig'
That last update is something I am relatively unfamiliar with. I suspect this is where my problem is located.
I am able to resister a new user via an api call with no problem, however, when I test to see if a Profile object exists for that new user, I am left with the following error:
python manage.py shell
from users.models import CustomUser
u = CustomerUser.objects.last()
u
<CustomUser:testuser#gmail.com> --- everything works to this point
u.profile --- this is where it breaks down
I'm left with this error in shell:
users.models.CustomUser.profile.RelatedObjectDoesNotExist: CustomUser has no profile.
Any help would be appreciated, thanks!
I think your error is in your signal method:
from django.db.models.signals import post_save
from django.dispatch import receiver
from conduit.apps.profiles.models import Profile
from .models import User # you have CustomUser but you are calling User
#receiver(post_save, sender=User)
def create_related_profile(sender, instance, created, *args, **kwargs):
if instance and created: # you should only have created because you want this happen only when it is created
instance.profile = Profile.objects.create(user=instance)
Also, I see no need updating the users init.py.

Categories

Resources