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.
Related
At the moment I'm working on an e-commerce application.
It contains a sub-app called "blog".
The idea is that the superuser creates an account for the *Trainer.
And yeah, I already created a new AbstractUser
Trainer logins into his account and creates Post
I logged in here using my Trainer`s credentials
After I want the superuser to see WHO created post, but DjangoAdmin displays me admin`s email
How could I display the email of the 'creator' of the post in Django admin?
Code:
#models.py
class UserTrainer(AbstractUser):
email = models.EmailField(verbose_name='email', max_length=100, unique=True)
age = models.PositiveIntegerField(null=True)
info = RichTextField(blank=True, null=True)
image = models.ImageField(upload_to='media/stuff_images')
inst = models.URLField(blank=True)
REQUIRED_FIELDS = ['email', ]
def __str__(self):
return self.email
def get_email(self):
return self.object.email
class Post(models.Model):
DEFAULT_TRAINER_ID = 1
article = models.CharField(max_length=50, default='Article text')
slug = models.SlugField(max_length=30)
keywords = models.CharField(max_length=100)
text = RichTextField(blank=True, null=True)
trainer = models.ForeignKey(UserTrainer, on_delete=models.CASCADE, null=False, default=1)
def __str__(self):
return self.article
class Meta:
verbose_name = 'Post'
verbose_name_plural = 'Posts'
#admin.py
class CustomUserAdmin(UserAdmin):
model = UserTrainer
add_form = CustomUserCreationForm
fieldsets = (
*UserAdmin.fieldsets,
(
'TrainerInfo',
{
'fields': (
'age', 'info', 'image', 'inst',
)
}
)
)
admin.site.register(UserTrainer, CustomUserAdmin)
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('article', 'slug','trainer')
list_display_links = ('article',)
fields = ('article', 'slug', 'keywords', 'text',)
readonly_fields = ('trainer',)
The problem is that you are are not specifying user when you save your post, so you should override your save method in admin.py, try this (OFFICIAL DOCS):
admin.site.register(UserTrainer, CustomUserAdmin)
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('article', 'slug','trainer')
list_display_links = ('article',)
fields = ('article', 'slug', 'keywords', 'text',)
readonly_fields = ('trainer',)
def save_model(self, request, obj, form, change):
obj.trainer = request.user
super().save_model(request, obj, form, change)
I want to do a filtering in django and I want to do this with date_created from custumUser for my filter. I can show date_create using list_display to my admin panel, but I can't filter through it on the page.
my models.py like that.
class CustomUser(AbstractBaseUser):
email = models.EmailField(max_length=200, unique=True, verbose_name='Email')
username = models.CharField(max_length=150, verbose_name='Username')
first_name = models.CharField(max_length=150, verbose_name='First Name')
last_name = models.CharField(max_length=150, verbose_name='Last Name')
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __str__(self):
return self.email
# this methods are require to login super user from admin panel
def has_perm(self, perm, obj=None):
return self.is_admin
# this methods are require to login super user from admin panel
def has_module_perms(self, app_label):
return self.is_admin
class Meta:
verbose_name = ('user')
verbose_name_plural = ('users')
class EmailConfirmed(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
activation_key = models.CharField(max_length=500)
email_confirmed = models.BooleanField(default=False)
date_confirmed = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.email
class Meta:
verbose_name_plural = 'User Email Confirmed'
#receiver(post_save, sender=CustomUser)
def create_user_email_confirmation(sender, instance, created, **kwargs):
if created:
dt = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
email_confirmed_instance = EmailConfirmed(user=instance)
user_encoded = f'{instance.email}-{dt}'.encode()
activation_key = hashlib.sha224(user_encoded).hexdigest()
email_confirmed_instance.activation_key = activation_key
email_confirmed_instance.save()
and my admin.py
class UserAdmin(BaseUserAdmin):
add_form = UserCreationForm
form = UserChangeForm
list_display = ('email', 'first_name', 'last_name', 'is_active', 'is_admin', 'is_staff','is_superuser','date_created')
list_filter = ('is_admin', 'is_staff','date_created',)
field_sets = (
(
None, {'fields': ('email', 'first_name', 'last_name', 'password')}
),
(
'Permissions', {'fields': ('is_admin', 'is_staff', 'is_superuser')}
)
)
add_fieldsets = (
(
None, {
'fields': ('email', 'first_name', 'last_name', 'is_active', 'password1', 'password2')
}
),
('Permissions', {'fields': ('is_admin', 'is_superuser')})
)
ordering = ('email',)
search_fields = ('email',)
filter_horizontal = ()
admin.site.register(User, UserAdmin)
class EmailConfirmedAdmin(admin.ModelAdmin):
list_display = ('user', 'first_name', 'last_name', 'date_created', 'email_confirmed', 'date_confirmed')
list_filter = ('email_confirmed', 'date_confirmed', )
def date_created(self, obj):
return obj.user.date_created
def first_name(self, obj):
return obj.user.first_name
def last_name(self, obj):
return obj.user.last_name
admin.site.register(EmailConfirmed, EmailConfirmedAdmin)
and I want to add admin panel for use date_created filtering which is belong to CustomUser
when I write date_created in the list_filter I get error.
How can do this?
You don't need to create a new object to know when it was created. Django by default give every model this field.
Try this in admin.py:
list_filter = ['created_at']
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.
I created a custom user using CBV and i have succeeded in implementing signup view however i am finding difficult to implement the update view. I have implemented the update view using the following code:
models.py
class CustomUser(AbstractUser):
state = models.CharField(choices=States, default ='abia', max_length=50 )
city = models.CharField(max_length=50)
local_government = models.CharField(max_length=50)
phone_number = models.CharField(max_length=50, blank= True)
picture = models.ImageField(upload_to='user/photos', blank=False, null=False, validators=
[clean_image])
forms.py
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = UserCreationForm.Meta.fields + ('state', 'city', 'local_government', 'phone_number',
'picture',)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = UserCreationForm.Meta.fields + ('state', 'city', 'local_government', 'phone_number',
'picture',)
admin.py
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['id', 'first_name', 'last_name', 'email','subscription_plan', 'state', 'city',
'local_government', 'phone_number', 'picture']
views.py (update):
class UpdateUser( LoginRequiredMixin, UpdateView):
model = CustomUser
form_class = CustomUserChangeForm
template_name = 'profile/profile-edit.html'
login_url = 'login'
The UpdateUser page just reload when i click to update profile without committing the update. Any help will be appreciated.
Since you are updating user object you should send the pk value to the URL tag in the template
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?