Serialise an extended User Model in Django - django

I'm extending a django auth user model in a Profile model:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
activity = models.IntegerField(default=500)
def _str_(self):
return self
in my views I'm getting the current auth user and I get the associated profile:
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getUserProfile(request):
profile = Profile.objects.get(user = request.user)
serializer = profileSerializer(profile, many=False)
return Response(serializer.data)
Here is my serializers code:
from rest_framework import serializers
from .models import Profile
class profileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('first_name', 'activity')
The error I'm getting that Profie object has not a first_name attribute, but when I replace 'first_name' with 'user' I get only the id of the user, so I want to show the first_name as well.
Thank you

The OneToOneField connects your field user in the Profile model to a User object which has a first_name attribute, but in the Profile table that user field is just a number that says which User object it is. To access the first name, you can do this:
from rest_framework import serializers
from .models import Profile
class profileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
Then in your template you could display the first_name like this, assuming you pass person to it where person is an instance of the Profile model:
{{ person.user.first_name }}
I also think it would be less confusing to use another name for the field, rather then user. Call it person maybe, like:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
person = models.OneToOneField(User, on_delete=models.CASCADE)
activity = models.IntegerField(default=500)
def _str_(self):
return self

Try this:
from django.contrib.auth.models import User
class profileSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(max_length=200, read_only=True)
class Meta:
model = Profile
fields = ('user', 'first_name', 'activity')
def get_first_name(self, obj):
return User.objects.filter(id=obj.user.id).first().values_list('first_name', flat=True).last()

I was able to show the fields I wanted using Django depth in the serializer
from rest_framework import serializers
from .models import Profile
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('first_name', 'last_name')
class profileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Profile
fields = ('activity','user')
depth=1

Related

how to render two forms in one template using a class based view

I'm using django's built-in User model, but I also have my own Account model which extends it:
class Account(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.IntegerField(blank=True)
location = models.CharField(max_length=255, blank=True)
experience = models.TextField(blank=True)
in my admin.py file:
class AccountInline(admin.StackedInline):
model = Account
can_delete = False
verbose_name_plural = 'Accounts'
class CustomUserAdmin(UserAdmin):
inlines = (AccountInline,)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
I want it to be that when a User registers they enter this information in, but my issue is getting both the UserForm and AccountForm in the same template/view.
As of now this is my registration view:
class UserRegistration(generic.CreateView):
form_class = RegisterForm
template_name = 'registration/registration.html'
def form_valid(self, form):
user = form.save()
form.registration_notification()
login(self.request, user, backend='django.contrib.auth.backends.ModelBackend')
return redirect(self.request.GET.get('next'))
How do I add my AccountForm to this view as well so that I can render both in the template and submit with one button. I've seen people do it with a function based view but is there a way to do it with a class-based view?
I also want the same idea for my UpdateView where a User can update User information, but also Account information. I assume it would follow the same logic.
You can use a Custom user model "Extend AbstractUser" since you want to add extra fields and add as many other fields as you want in a single model and avoid making extra queries to the database.
From Django documentation :
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
field 1
field 2
forms.py
from django.contrib.auth.forms import UserCreationForm
from myapp.models import User
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('custom_field',)
You can read more here

Django user profile style auth with Djoser

I'm trying to implement authentication with djoser. Since I haven't extended my AbstractBaseUser from Django at start of my project I decided to use a one-to-one profile relation pattern. I have a seperated user app and Here are my related codes:
# models.py (user and profile models)
from django.db import models
# Create your models here.
from django.db import models
from django.contrib.auth.models import User as BaseUser
class User(BaseUser):
#property
def mobile_number(self):
return self.profile.mobile_number
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
mobile_number = models.CharField(max_length=11, unique=True)
def __str__(self):
return self.user.username
# settings.py (djoser settings)
...
DJOSER = {
"SERIALIZERS":{
"user": "user.serializers.UserSerializer"
"current_user": "user.serializers.UserSerializer"
"user_create": "user.serializers.UserCreateSerializer"
}
...
}
...
# user.serializers.py
from rest_framework import serializers
from .models import UserProfile
from .models import User
from djoser.serializers import UserCreateSerializer as BaseUserCreateSerializer
from djoser.serializers import UserSerializer as BaseUserSerializer
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
exclude = ["user"]
class UserSerializer(serializers.ModelSerializer):
mobile_number = serializers.CharField(required=True, allow_blank=False)
class Meta:
model = User
fields = ["username", "email", "first_name", "last_name", "mobile_number"]
class UserCreateSerializer(BaseUserCreateSerializer):
mobile_number = serializers.CharField(required=True, allow_blank=False)
class Meta(BaseUserCreateSerializer.Meta):
fields = ["username", "email", "first_name", "last_name", "mobile_number"]
def create(self, validated_data):
mobile_number = validated_data.pop("mobile_number")
user = super.create(**validated_data)
UserProfile.objects.create(user=user, mobile_number=mobile_number)
return user
What I'm trying to do here is
Skip migrations with adding just a property to django User model.
Extending Djoser serializer to just create the relation in create step.
But when i try to create user with post request to djoser endpoint (auth/users/create) with body like this:
{
"username": "user",
"email": "user#examssple.com",
"first_name": "hatef",
"last_name": "madani",
"mobile_number": "01236549879"
}
I receive this error: "User() got an unexpected keyword argument 'mobile_number'". Any help on current approach or better one would be great.
I think you should follow this official documentation: https://docs.djangoproject.com/en/3.1/topics/auth/customizing/
your custom User should extends AbstractBaseUser or AbstractUser and you need customize a UserManager with create_user(..) method

UserProfile db showing (None) in django

I was dealing with user authentication in django and created a UserProfile model with foreign key to User in-built model. Then I created explicitly defined User model and then deleted it to restore the original code.
But now in my UserProfile database accessed from admin page is showing (None) for all the profiles created earlier.
And when I click on it it shows an error.
This is the code:
forms.py and models.py
from django import forms
from django.contrib.auth.models import User
from cms.models import UserProfile
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'email', 'password')
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('designation',)
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
designation = models.CharField(max_length=128,blank=True)
def __unicode__(self):
return self.user.username
How do I delete that (None)?
As I said in comments, it's because your __unicode__() built-in method prints this user
Try change this part of the code to:
def __unicode__(self):
try:
return self.user.username
except:
return "UserProfile has No User instance"

Initialize user django so he has default settings values

I am using Django + Angular 2
. I am creating a user fine but he does not have the default values i want...
Model
class Settings(models.Model):
user = models.ForeignKey('auth.User', related_name='settings', on_delete=models.CASCADE)
bolean1 = models.BooleanField(default=False)
boolean2 = models.BooleanField(default=False)
boolean3 = models.BooleanField(default=False)
string1 = models.CharField(max_length=100, default='No description')
Serializer
class SettingsSerializer(serializers.ModelSerializer):
class Meta:
model = Settings
fields = ('id', 'bolean1', 'bolean1', 'bolean3', 'string1')
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password' ,'settings', 'image')
extra_kwargs = {'password': {'write_only': True}}
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
Views
class SettingsValues(generics.ListAPIView):
serializer_class = SettingsSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_queryset(self):
queryset = Settings.objects.all()
queryset = queryset.filter(user=self.request.user.id)
return queryset
class RegisterUser(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = CreateUserSerializer
The problem is that when i create a new user he does not have default values,e.g boolean 1, boolean 2 etc. i must go to django admin create new setting and choose the user.
Any idea ?
Sounds like you want Django's Signals, specifically post_save. You'll want to create a Settings() for each User() that's created - you have default values right now, but that only helps when you've saved the Settings model.
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
User = get_user_model()
#receiver(post_save, sender=User)
def create_settings(sender, instance, created, **kwargs):
# Only create a Settings relation if the User is new
if created:
settings = Settings(user=instance)
settings.save()
If your Settings model is in an app called settings, then your settings/apps.py should have this - good StackOverflow answer on this:
from django.apps import AppConfig
class SettingsConfig(AppConfig):
name = 'settings'
def ready(self):
from . import signals
Now, whenever a new User is created, a Settings relation is created for them as well.
I am not sure if this is what you want to achieve, but try this.
//admin.py
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from accounts import models
admin.site.unregister(User)
class UserRoleInline(admin.StackedInline):
model = models.UserRole
class UserRoleAdmin(UserAdmin):
inlines = [UserRoleInline]
admin.site.register(User, UserRoleAdmin)
//model.py
from django.db import models
from django.contrib.auth.models import User
class RoleType(object):
MANAGER = 'm'
CANDIDATE = 'c'
CHOICES = ((MANAGER, 'Manager'), (CANDIDATE, 'Candidate'))
class UserRole(models.Model):
user = models.OneToOneField(User)
role = models.CharField(max_length=1, choices=RoleType.CHOICES)
Reference: django StackedInline
This will help you stack the setting models at end of User model. This won't automatically take a default value, but will let you choose the settings as the user models is created.

Can't POST after extending User model fields in Django REST

I am able to extend User model fields and I can see API browser with these fields and inputs, but when I try to POST a new user I got this error:
'signup' is an invalid keyword argument for this function
or if I change in serializers.py
User.objects.create(**validated_data)
to
SignUp.objects.create(**validated_data)
I got
'username' is an invalid keyword argument for this function
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
city = serializers.CharField(source='signup.city')
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'city')
def create(self, validated_data):
return User.objects.create(**validated_data)
models.py
from django.contrib.gis.db import models
from django.contrib.auth.models import User
class SignUp(models.Model):
user=models.OneToOneField(User, primary_key=True)
city = models.CharField(max_length=50, blank=True)
def __unicode__(self):
return self.first_name
You'll want to do this through the related_name field in your model and then you can reference it on the serializer.
class SignUp(models.Model):
user=models.OneToOneField(User, primary_key=True, related_name='signup') # <---
I don't think you can flatten nested fields into the parent model's serializer.
You can do a nested field:
class UserSerializer(serializers.ModelSerializer):
city = serializers.CharField(source='signup')
If you access it via the JSON path, you'll get an object like:
{
url: "localhost:8000/users/1",
username: "foo",
email: "foo#example.com",
signup: {
city: "Seattle"
}
}