I have User Model and Favorite Product Model. I have link as ID to user in Favorite Product model. Can I change ID link to email?
This is my User Model.
class User(AbstractBaseUser, PermissionsMixin):
"""Custom user model that supports using email instead of username"""
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
surname = models.CharField(max_length=255, default='Фамилия')
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
favorite_product = models.ForeignKey(
'FavoriteProduct',
related_name='favorite_products_for_user',
on_delete=models.SET_NULL, null=True
)
objects = UserManager()
USERNAME_FIELD = 'email'
This is my Favourite Product model
class FavoriteProduct(models.Model):
"""Favorite product object"""
created = models.DateTimeField(auto_now_add=True, null=True)
product = models.ForeignKey(
'Product',
related_name='favorite_products',
on_delete=models.CASCADE, null=True
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
favorite_product\serializers.py
from rest_framework import serializers
from core.models import FavoriteProduct
class FavoriteProductSerializer(serializers.ModelSerializer):
"""Serializer for favorite products objects"""
class Meta:
model = FavoriteProduct
fields = ('__all__')
read_only_fields = ('id',)
user\serializers.py
from django.contrib.auth import get_user_model, authenticate
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
"""Serializer for the users object"""
class Meta:
model = get_user_model()
fields = ('email', 'password', 'name', 'surname')
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def create(self, validated_data):
"""Create a new user with encrypted password and return it"""
return get_user_model().objects.create_user(**validated_data)
def update(self, instance, validated_data):
"""Update a user, setting the password correctly and return it"""
password = validated_data.pop('password', None)
user = super().update(instance, validated_data)
if password:
user.set_password(password)
user.save()
return user
class AuthTokenSerializer(serializers.Serializer):
"""Serializer for the user authentication object"""
email = serializers.CharField()
password = serializers.CharField(
style={'input_type': 'password'},
trim_whitespace=False
)
def validate(self, attrs):
"""Validate and authenticate the user"""
email = attrs.get('email')
password = attrs.get('password')
user = authenticate(
request=self.context.get('request'),
username=email,
password=password
)
if not user:
msg = _('Аутентификация не прошла по указанным данным')
raise serializers.ValidationError(msg, code='authentication')
attrs['user'] = user
return attrs
class USerializer(serializers.ModelSerializer):
"""Serializer for user objects"""
class Meta:
model = get_user_model()
fields = ('__all__')
read_only_fields = ('id',)
If you do not understand me? I uploaded picture.png file.enter image description here
This has nothing to do with Models. It's about serializing the model objects.
from rest_framework import serializers
class FavoriteProductSerializer(serializers.ModelSerializer):
user = serializers.RelatedField(source='email', read_only=True)
class Meta:
model = FavoriteProduct
fields = ('id', 'created', 'product', 'user')
from_the_docs: I think what you need to do is set
USERNAME_FIELD = 'email'
in your User Model.
Also, I would suggest changing name of your Customer User Model to something else like MyUser or CustomerUser to avoid confusion with default User Model provided by django.
Related
I'm trying to create create a nested serializer using the Django Rest framework. The relationship is Profile X User but when i use Profile.objects.create(user=profile, **user_data) i get ValueError: Cannot assign "<Profile: Profile object (7)>": "Profile.user" must be a "User" instance..
This should be some rookie misunderstanding of models relationship definitions or the serializer declaration itself but I can't find anything around the docs. If someone can point me a direction I'll be gracefull.
models.py
class User(models.Model):
name = models.CharField(max_length=100, blank=False)
email = models.CharField(max_length=100, blank=True, default='')
password = models.CharField(max_length=100, blank=True, default='')
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
class Profile(models.Model):
# choices [...]
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
profile_type = models.CharField(max_length=2,choices=PROFILE_CHOICES,default=TEAMMEMBER)
authentication_token = models.CharField(max_length=100, null=True)
avatar_url = models.CharField(max_length=100, default='')
permissions = models.CharField(max_length=100, null=True)
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
serializer.py
class UserSerlializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['name', 'email', 'password']
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
Profile.objects.create(user=profile, **user_data)
return Profile
POST
{
"profile_type" : "ST",
"user": {
"name" : "test",
"email" : "test#test.com",
"password" : "123456"
}
}
You are creating instances in wrong way. Change your create(...) method as,
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
user_instance = User.objects.create(**user_data)
profile_instance = Profile.objects.create(user=user_instance, **validated_data)
return profile_instance
Profile.user should beUser instance, but you are assigning Profile instance.
Change your create method to this:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
user = User.objects.create(**user_data) # 1. creating user
profile.user = user # 2. assigning user
profile.save() # 3. saving profile after adding user
return profile # returning Profile instance.
inherit your user model from django contrib auth module also, and make a one to one relation with profile
from django.contrib.auth.models import User
I have a CustomUser model with ForeignKey to another model. In my Signup form, I'm using a hidden field that is populated with a value depending on a search result from the user (the user is searching for a company and after clicking on the result the hidden input value represents that company's ID).
When I'm saving the User object I can easily get the ID, make a query and add the company object to the field.
The problem is with my Admin. The Admin is using the same form and the company field is rendered as hidden with the ID value.
When I try to change some User info and try to save it I get this error:
Cannot assign "'3'": "CustomUser.kompanija" must be a "Kompanija" instance.
I thought that the Admin is using the same save method from my custom UserAccountAdapter...?
Is it possible to override the hidden input just for the Admin to show:
forms.ModelChoiceField(queryset=Kompanija.objects.all(), empty_label="Kompanija")
with initial value from the saved user object?
My models.py:
class Kompanija(models.Model):
naziv = models.CharField(max_length=50)
adresa = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.naziv
class CustomUser(AbstractUser):
ime = models.CharField(max_length=30, default='')
prezime = models.CharField(max_length=30, default='')
kompanija = models.ForeignKey(Kompanija, on_delete=models.CASCADE, null=True, blank=True)
is_premium = models.BooleanField('premium status', default=False)
def __str__(self):
return self.email
My forms.py:
class CustomUserCreationForm(UserCreationForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta(UserCreationForm):
model = CustomUser
fields = ('username', 'email', 'ime', 'prezime')
class CustomUserChangeForm(UserChangeForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = CustomUser
fields = ('username', 'email', 'ime', 'prezime')
class CustomSignupForm(SignupForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = CustomUser
def signup(self, request, user):
user.ime = self.cleaned_data['ime']
user.prezime = self.cleaned_data['prezime']
user.kompanija = Kompanija.objects.get(id=self.cleaned_data['kompanija'])
user.save()
return user
My adapter.py:
class UserAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
user = super(UserAccountAdapter, self).save_user(request, user, form, commit=False)
user.ime = form.cleaned_data.get('ime')
user.prezime = form.cleaned_data.get('prezime')
user.kompanija = Kompanija.objects.get(id=form.cleaned_data.get('kompanija'))
user.save()
My admin.py:
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium']
fieldsets = (
(('Korisnik'), {'fields': ('email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium')}),
)
admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register(Kompanija)
My settings.py:
ACCOUNT_FORMS = {
'signup': 'korisnici.forms.CustomSignupForm',
}
ACCOUNT_ADAPTER = 'korisnici.adapter.UserAccountAdapter'
After some more research and crashing my site, I found a solution to this.
I needed to add ModelForm in my forms.py just for the Admin site.
In my forms.py I added:
class CustomUserAdminForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ('email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium')
And my admin.py:
class CustomUserAdmin(UserAdmin):
form = CustomUserAdminForm
model = CustomUser
list_display = ['email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium']
fieldsets = (
(('Korisnik'), {'fields': ('email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium')}),
)
admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register(Kompanija)
Now everything works as expected.
all.
I have some issues with the 'create' method which i overwrite in my ModelSerializer. By the way, my models:
class User(AbstractEmailUser):
target_languages = models.ManyToManyField(Language, default='en', related_name="target_language")
biography = models.TextField(null=True, blank=True)
location = models.CharField(max_length=2, null=True)
objects = UserManager()
And my serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = ['password', 'groups', 'user_permissions', 'is_superuser',
'is_staff']
read_only_fields = ['date_joined', 'last_login']
def create(self, validated_data):
target_languages = validated_data.pop('target_languages')
user = User.objects.create(**validated_data)
for target_language in target_languages:
user.target_languages.add(get_object_or_404(Language, iso_639_1=target_language))
user.save()
return user
The main problem is when I trying to POST to this serializer with target_languages field like target_languages": ["en", "es"] it doesn't set up the ManyToMany field, it's just empty.
Where did I go wrong?
views.py
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
model = User
serializer_class = UserSerializer
paginate_by = 10
def get_queryset(self):
queryset = User.objects.all()
search_query = self.request.query_params.get('user', None)
if search_query is not None:
queryset = queryset.filter(name__istartswith=search_query)
queryset = queryset.order_by('name')
return queryset
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
model = User
serializer_class = UserSerializer
models.py
class UserRole(models.Model):
class Meta:
ordering = ["name"]
db_table = 'userrole'
name = models.CharField(max_length=50)
status = models.CharField(max_length=100)
class User(models.Model):
class Meta:
ordering = ["name"]
db_table = 'user'
name = models.CharField(max_length=100)
email = models.EmailField(unique=True, max_length=100)
password = models.CharField(max_length=100)
status = models.CharField(max_length=100, default='active')
roleid = models.ForeignKey(UserRole, on_delete=models.CASCADE,
default=None, blank=True, db_column='roleid')
createdby = models.CharField(max_length=100, blank=True, default="")
createdon = models.DateTimeField(blank=True, auto_now_add=True)
updatedon = models.DateTimeField(blank=True, auto_now=True)
Serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
class UserRoleSerializer(serializers.ModelSerializer):
class Meta:
model = UserRole
I've gone through with Django-rest documentation but not able to find a reference for implementing authentication and permission for custom created Users.
Please advise.
Check Django Document for User authentication in Django and Permissions in Django Rest Framework.
Below is the sample models structure for custom BaseUser and BaseUserManager.
from django.conf import settings
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from django.template.defaultfilters import slugify
User = settings.AUTH_USER_MODEL
class BaseUserManager(BaseUserManager):
def create_user(self, useremail, display_name, password=None):
if not useremail:
raise ValueError('Users must have an email address')
now = timezone.now()
user = self.model(useremail=BaseUserManager.normalize_email(useremail))
user.display_name = display_name
user.email = useremail
user.profile_slug = getUniqueValue(BaseUser,slugify(useremail.split("#")[0]),field_name="profile_slug")
user.set_password(password)
user.status='A'
user.last_login = user.date_joined = now
user.save(using=self._db)
return user
def create_superuser(self, useremail, display_name, password):
user = self.create_user(useremail=useremail,
display_name=display_name, password=password)
user.email = useremail
user.display_name = display_name
user.is_superuser = True
user.is_staff = True
user.status='A'
user.save(using=self._db)
return user
class BaseUser(AbstractBaseUser, PermissionsMixin):
display_name = models.CharField(max_length=25)
profile_slug = models.CharField(max_length=25,null=True)
gender = models.CharField(max_length=1, blank=True, choices=Gender)
useremail = models.EmailField(unique=True)
is_staff = models.BooleanField(default=False)
user_status = models.CharField(max_length=1, default='A')
USERNAME_FIELD = 'useremail'
REQUIRED_FIELDS = ['display_name']
objects = BaseUserManager()
def __unicode__(self):
return self.display_name
In your permission.py, you may define permissions as:
from rest_framework import permissions
class CheckPermission(permissions.BasePermission):
def has_permission(self, request, view):
try:
# some code
return True
except:
return False
I am getting following error while using the PostSerializer:
Got AttributeError when attempting to get a value for field
full_name on serializer UserSerializer. The serializer field might
be named incorrectly and not match any attribute or key on the long
instance. Original exception text was: 'long' object has no attribute
'full_name'.
Serializers are as follows:
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required=False, allow_null=True)
class Meta:
model = Post
fields = ('id', 'author', 'message', 'rating', 'create_date', 'close_date',)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'full_name',)
View:
class PostMixin(object):
model = Post
serializer_class = PostSerializer
permission_classes = [
PostAuthorCanEditPermission
]
queryset = model.objects.all()
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
class PostList(PostMixin, generics.ListCreateAPIView):
pass
User model:
class User(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True, null=True)
full_name = models.CharField(max_length=50, blank=False)
phone = models.CharField(max_length=20, unique=True, null=True)
about = models.CharField(max_length=255, blank=True)
type = models.CharField(max_length=1, default='U')
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['full_name']
def __unicode__(self):
return self.email
def get_full_name(self):
return self.full_name
def get_short_name(self):
return self.full_name
Problem
Got AttributeError when attempting to get a value for field full_name on serializer UserSerializer.
The model User in Django has no such field called full_name.
There is though a method get_full_name() that does what you want.
Solution
So try using it through a SerializerMethodField
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username') # no full_name here
full_name = serializers.SerializerMethodField('get_full_name')
This will add a field called full_name to your serialized object, with the value pulled from User.get_full_name()
Check you are using your custom model and not Django's User model
You've customized your own User model, but since that models has full_name, you shouldn't have gotten that error in the first place, so double check you are not referencing Django's default User model first.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User # <--- Make sure this is your app.models.User,
# and not Django's User model
fields = ('id', 'username', 'full_name',) # This is OK on your User model
or just
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name')