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
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'm having trouble displaying all the jobs as options in the apply url view see image below.
I am getting the error which says
Lists are not currently supported in HTML input
The main function I am looking for is for a list of jobs that were posted to be available for selection when applying for the job.
models.py
class Job(models.Model):
"""A Job used to create a job posting"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
description = models.TextField()
job_type = models.CharField(max_length=12, choices=JOB_TYPE_CHOICES, default='Full-Time')
city = models.CharField(max_length=255)
def __str__(self):
return self.description[:50]
class Applicant(models.Model):
"""A applicant that can apply to a job"""
job = models.ForeignKey(Job, related_name='applicants', on_delete=models.CASCADE)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField(max_length=254)
phone_number = PhoneNumberField()
resume = models.FileField(upload_to=resume_file_path, validators=[validate_file_extension])
def __str__(self):
return self.first_name
I've removed some of the attributes in Job so that the code is not so long.
serializers.py
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from core.models import Job, Applicant
class JobSerializer(serializers.ModelSerializer):
"""Serializer for tag objects"""
applicants = serializers.StringRelatedField(many=True)
class Meta:
model = Job
fields = ('id', 'description', 'job_type', 'city', 'state', 'salary', 'position', 'employer', 'created_date', 'is_active', 'applicants')
read_only_fields = ('id',)
def create(self, validated_data):
"""Create a job posting with user and return it"""
return Job.objects.create(**validated_data)
class ApplyJobSerializer(serializers.ModelSerializer):
"""Serializer for applying to jobs"""
jobs = JobSerializer(many=True, queryset=Job.objects.all())
class Meta:
model = Applicant
fields = ('id','jobs', 'first_name', 'last_name', 'email', 'phone_number', 'resume')
read_only_fields = ('id',)
views.py
class ApplyJobView(generics.CreateAPIView):
"""Allows applicants to apply for jobs"""
serializer_class = serializers.ApplyJobSerializer
I've tried adding a queryset=Job.objects.all() as an argument to the JobSerializer() in the ApplyJobSerializer class in my serializers.py field. However I get an error that says
TypeError: __init__() got an unexpected keyword argument 'queryset'
You can select an existing job in the form.
class JobSerializer(serializers.PrimaryKeyRelatedField, serializers.ModelSerializer):
"""Serializer for tag objects"""
applicants = serializers.StringRelatedField(many=True)
class Meta:
model = Job
fields = ('__all__')
read_only_fields = ('id',)
def create(self, validated_data):
"""Create a job posting with user and return it"""
return Job.objects.create(**validated_data)
class ApplyJobSerializer(serializers.ModelSerializer):
"""Serializer for applying to jobs"""
jobs = JobSerializer(many=True, queryset=Job.objects.all())
class Meta:
model = Applicant
fields = ('__all__')
read_only_fields = ('id',)
This result as I have created a new job with description is desc
Here are my models :
class Profile(models.Model):
user = models.ForeignKey(User, related_name="profile", on_delete=PROTECT)
plan = models.ForeignKey(Plans, on_delete=PROTECT)
full_name = models.CharField(max_length=2000)
company_name = models.CharField(max_length=50, null=True, blank=True)
activation_token = models.UUIDField(default=uuid.uuid4)
activated = models.BooleanField(default=False)
thumb = models.ImageField(upload_to='uploads/thumb/', null=True, blank=True)
renew_data = models.DateField()
is_paid = models.BooleanField(default=False)
And as you see the Profile model have user field that is related to the Abstract user of django framework. now here is how i call them using an API :
Serializers
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Profile
fields = ['company_name']
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile_set = ProfileSerializer(
read_only=True, many=True) # many=True is required
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile_set']
But when I call the API it shows only the fields username and 'id but not the profile_set
Your UserSerializer should like this,
class UserSerializer(serializers.HyperlinkedModelSerializer):
# no need to set `profile.all` as you have related name profile defined in your model
profile_set = ProfileSerializer(source='profile', many=True)
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile_set']
OR,
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = ProfileSerializer(many=True) # as you have related name `profile`
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile']
Try setting the source of your serializer:
profile_set = ProfileSerializer(
source='profile.all',
read_only=True, many=True
)
It looks like you've set the related_name on your foreign key:
user = models.ForeignKey(User, related_name="profile", on_delete=PROTECT)
This defines the reverse relation name, so that's how you need to refer to it in DRF, too:
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = ProfileSerializer(read_only=True, many=True)
class Meta:
model = User
depth = 1
fields = ['username', 'id', 'profile']
Since it's clearly a plural, I'd also suggest you rename profile to profiles.
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?