I created two custom user models (AbstractBaseUser and a separate for extra information). Is there a way to combine the two models to create one form that the user can use to update all of their information between the two models?
For example, it would be ideal to have something like this (although I know not possible):
class ProfileChangeForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['username', 'email', first_name', 'last_name', 'bio', 'website']
Thank you in advance for your help! The models are below:
MyUser:
class MyUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=30, unique=True)
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=120, null=True, blank=True)
last_name = models.CharField(max_length=120, null=True, blank=True)
UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(MyUser)
bio = models.TextField(null=True, blank=True)
website = models.CharField(max_length=120, null=True, blank=True)
Following solution worked for me. I used formsets to create this solution.
My models were as follows,
Models:
#Custom user model
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, **extra_fields):
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
#Related model(One-to-One relationship with custom user)
class Student(models.Model):
user = models.OneToOneField(CustomUser,on_delete = models.CASCADE)
first_name = models.CharField(max_length=50)
middle_name = models.CharField(max_length=50,blank=True,null=True)
last_name = models.CharField(max_length=50,blank=True,null=True)
After that I created two ModelForms
Forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser,Student
from django import forms
# Form for custom user
class SignUpForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ('email', 'password1', 'password2')
class StudentCreationForm(forms.ModelForm):
class Meta:
model = Student
fields = ['user','first_name','middle_name','last_name']
Now the main part, I created a simple inline formset factory to handle Student model as an inline form.
Formset
from django.forms import inlineformset_factory
from .models import CustomUser,Student
from .forms import StudentCreationForm
# As parameters I provided parent Model(CustomUser),child Model(Student) and the Child
# ModelForm(StudentCreationForm)
StudentCreationFormSet = inlineformset_factory(CustomUser, Student,form=StudentCreationForm,extra=1,can_delete = False)
In views, I created the SignUpForm and StudentCreationFormSet object respectively. And in the POST request first I validated the CustomUser form and saved it without comitting it(commit=False). I created an object of custom user and passed it as a instance to the StudentCreationFormSet to validate the related form. If everything goes fine my both forms will be saved else the errors will be shown in the template.
View
from django.shortcuts import render,redirect
from .forms import SignUpForm
from .formsets import StudentCreationFormSet
def createAccountView(request):
student_account_form = SignUpForm()
student_details_formset = StudentCreationFormSet()
if request.method == 'POST':
student_account_form = SignUpForm(request.POST)
if student_account_form.is_valid():
# Validating Custom User form and creating object of it(not comitting as formset needed to be verified)
student_account = student_account_form.save(commit=False)
# Getting Custom User object as passing it as instance to formset
student_details_formset = StudentCreationFormSet (request.POST,instance=student_account)
if student_details_formset.is_valid():
student_account_form.save()
student_details_formset.save()
return redirect('login')
else:
student_details_formset = StudentCreationFormSet (request.POST)
context = {
'student_account_form':student_account_form,
'student_details_form':student_details_formset
}
return render(request, 'account/createStudentPage.html',context=context)
Also note that I am passing both the form and formset in single post request.
Template (createStudentPage.html)
<form method="POST" >
{% csrf_token %}
{{ student_account_form.as_p }}
{{ student_details_form.as_p }}
<button type="submit">Sign Up</button>
</form>
I think you can do something like :
class ProfileChangeForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['user__username', 'user__email', 'user__first_name', 'user__last_name', 'bio', 'website']
Related
I am making a movie watching website in which there are users and films and the user model has a ManyToMany Field that references the film model. it's called WatchList and an authenticated user can add any movie they want to this watchlist.
My problem is that I want an API that only gets the ID of a film and adds it to the user's watch list.
these are my models and serializers and I am trying to make a view to implement this API.
# models.py
class Film(models.Model):
filmID = models.AutoField(primary_key=True)
title = models.CharField(max_length=150)
# ...
class User(AbstractBaseUser, PermissionsMixin):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
email= models.EmailField(max_length=100, unique=True, validators=[EmailValidator()])
name = models.CharField(max_length=100)
watchList = models.ManyToManyField(Film)
objects = UserManager()
USERNAME_FIELD = 'username'
# serializers.py
class WatchListSerializer(serializers.ModelSerializer):
class FilmSerializer(serializers.ModelSerializer):
model = Film
fields = ('filmID', 'title',)
read_only_fields = ('filmID', 'title')
film_set = FilmSerializer(read_only=True, many=True)
class Meta:
model = get_user_model()
fields = ('userID', 'film_set')
read_only_fields = ('userID',)
# views.py
class WatchListAddView(...):
pass
The serializer can be changed. but this kind of shows what I want the api to be. the authentication validation part is already taken care of, so imagine that any request to the view is from an authenticated user.
I would not recommend patching this directly and instead create a separate endpoint for adding removing data to this field.
In your case it would look like this. I show just a small working example, you can adjust it to your needs
from django.shortcuts import get_object_or_404
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
#action(detail=True,
methods=['POST'])
def add_film_to_watch_list(self, request, **kwargs):
film = get_object_or_404(klass=Film, filmID=kwargs.get('filmID'))
user = self.get_object()
user.watchList.add(film)
return Response("Success")
I have created the user authentication system which includes both the default User model and an extended User model. They are as below:
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
Photo = models.ImageField(upload_to='documents/%Y/%m/%d/', null=True)
uploaded_at = models.DateTimeField(auto_now_add=True, null=True)
dob = models.DateField(max_length=20, null=True)
country = models.CharField(max_length=100, null=True)
State = models.CharField(max_length=100, null=True)
District = models.CharField(max_length=100, null=True)
phone = models.CharField(max_length=10, null=True)
def get_absolute_url(self):
return reverse('profile', kwargs={'id': self.id})
forms.py
class UserProfileForm(forms.ModelForm):
Photo = forms.ImageField( max_length=100)
dob = forms.DateField(widget=forms.TextInput(attrs={'type': 'date'}))
country = forms.CharField(max_length=100)
State = forms.CharField(max_length=100)
District = forms.CharField(max_length=100)
phone = forms.CharField(max_length=10)
class Meta:
model = UserProfile
fields = ('Photo', 'dob', 'country', 'State', 'District', 'phone')
With the help of the above model and form, I am able to create user, and enter values for those custom model fields and see the user profile. So far so good.
However, I am facing issues while I update those custom fields. I have used the Django's in-built modules to update the default User fields(email). But I am not able to find a way to update those custom fields('dob', 'country', 'State', 'District', 'phone'). Below is the method from views.
views.py
#login_required(login_url="/login/")
def editUserProfile(request):
if request.method == "POST":
form = UserProfileUpdateForm(request.POST, instance=request.user) # default User profile update
obj = UserProfile.objects.get(id=request.user.id)
form1 = UserProfileForm(request.POST or None, instance=obj) # custom fields update.
if form.is_valid() and form1.is_valid():
obj.Photo = form1.cleaned_data['Photo']
obj.dob = form1.cleaned_data['dob']
obj.country = form1.cleaned_data['country']
obj.State = form1.cleaned_data['State']
obj.District = form1.cleaned_data['District']
obj.phone = form1.cleaned_data['phone']
form.save()
form1.save()
messages.success(request, f'updated successfully')
return redirect('/profile1')
else:
messages.error(request, f'Please correct the error below.')
else:
form = UserProfileUpdateForm(instance=request.user)
form1 = UserProfileUpdateForm(instance=request.user)
return render(request, "authenticate\\editProfilePage.html", {'form': form, 'form1': form1})
I have an update button on my profile page, on clicking I could only see the "email" field with pre-populated data to update(I can update this default field successfully).
I have seen other stackoverflow posts, but they are not helping.
I am not able to figure out the mistakes.
Please help
Thank you,
I think the problem is in this line
obj = UserProfile.objects.get(id=request.user.id)
here left id is id from UserProfile model. so it will be something like this
obj = UserProfile.objects.get(user__id=request.user.id)
hey i want to create a profile page for my user,in which when people logging to the website they can view the profile of every user,i get the above error anytime i tried to log on to every profile of the user i get it, this is my code below
views.py
class DoctorDetailView(LoginRequiredMixin, DetailView):
model = Doctor
fields = ['user', 'email', 'image', 'speciality', 'bio']
template_name = 'pages/doctor_detail.html'
def get_queryset(self):
user = get_object_or_404(Doctor, username=self.kwargs.get('username'))
return Doctor.objects.filter(doctor=user.doctor)
urls.py
path('doctor/', doctor, name='doctor'),
path('doctor/info/<str:username>', user_views.DoctorDetailView.as_view(), name='doctor-detail'),
doctor.html
<a href="{% url 'doctor-detail' doc.user.username %}"><div class="img-wrap d-flex align-items-stretch">
<div class="img align-self-stretch" style="background-image: url({{ doc.user.doctor.image.url }}"></div>
models.py
class CustomUser(AbstractUser):
is_doctor = models.BooleanField(default=False)
def __str__(self):
return self.email
class Status(models.Model):
title= models.CharField(max_length=5)
def __str__(self):
return self.title
class Doctor(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, null=True, related_name="doctor")
image = models.ImageField(default='jazeera.jpg', upload_to='profile_pics')
bio = models.TextField()
speciality = models.CharField(max_length=300)
describtion = models.CharField(max_length=100)
status = models.ManyToManyField(Status)
A Doctor object has no username, hence:
user = get_object_or_404(Doctor, username=self.kwargs.get('username'))
makes not much sense, you however do not need to use get_object_or_404 to fetch the user first, you can filter with:
from django.shortcuts import get_object_or_404
class DoctorDetailView(LoginRequiredMixin, DetailView):
model = Doctor
fields = ['user', 'email', 'image', 'speciality', 'bio']
template_name = 'pages/doctor_detail.html'
def get_object(self):
return get_object_or_404(Doctor, user__username=self.kwargs['username'])
I'm trying to retrieve data from user. I have my model like this:
from django.db import models
from django.contrib.auth.models import User
Create your models here.
class informacionFacturacion(models.Model):
usuario = models.ForeignKey(User)
apellidos = models.CharField(max_length=100, default="editar")
nombres = models.CharField(max_length=100, default="editar")
telefono = models.CharField(max_length=100, default="editar")
email = models.EmailField(default="editar", null=False)
direccion_1 = models.CharField(max_length=100, default="editar")
direccion_2 = models.CharField(max_length=100, null=True, blank=True)
provincia = models.CharField(max_length=100, default="editar")
ciudad = models.CharField(max_length=100, default="editar")
codigoPostal = models.CharField(max_length=100, default="editar")
empresa = models.CharField(max_length=100, default="editar")
def __str__(self):
return self.usuario
My form for update user information:
from .models import informacionFacturacion
class informacionFacturacionForm(ModelForm):
class Meta:
model = informacionFacturacion
fields = [
"usuario",
"apellidos",
"nombres",
"telefono",
"email",
"direccion_1",
"direccion_2",
"provincia",
"ciudad",
"codigoPostal",
"empresa",
]
And in my view I have my query like this
from django.contrib.auth.decorators import login_required
from .models import informacionFacturacion
from .forms import informacionFacturacionForm
#login_required
def datosPersonales(request):
form = informacionFacturacionForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
query = informacionFacturacion.objects.filter(usuario=request.user)
context = {
"titulo": "Datos personales | Cadenas Giordanino S.R.L" + request.user.username,
"body_class": "class= sidebar_main_open sidebar_main_swipe",
"form": form,
"infoFacturacion": query,
}
template = "micuenta/datosPersonales.html"
return render(request, template, context)
And this QuerySet is empty.
I need to retrieve this data in the user profile
**UPDATE: ** Full code on post.
**UPDATE 2: ** For displaying the user data on profile, im using a "For loop". This data, is retrieved in "value=" attr of html inputs. If the user has no data, the form dosnt show.
This is the way I wanna show the data. I populated this form from the same form u see here.
Here's when i enter for first time to my profile with no data
Thanks a lot.
Are you sure that request.user is the user you've linked your anotherModel to? If you aren't currently logged in then request.user will be an instance of AnonymousUser. See more in the Documentation: https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpRequest.user
You can use the Django Shell for testing your models:
$ python manage.py shell
Then make some models:
from django.contrib.auth.models import User
from models import AnotherModel
# Grab a User
user = User.objects.first()
# Create a new anotherModel, linking the user
my_model = AnotherModel(
user=user,
address="whatever"
)
my_model.save()
my_model.user == user
>>> True
I have a model :
from django.db import models
from tinymce.models import HTMLField
class Team(models.Model):
name = models.CharField(max_length=100, verbose_name='Team name')
city = models.CharField(max_length=100, verbose_name='Team city')
biography = HTMLField(verbose_name='Team biography')
country = models.ForeignKey('Country')
slug = models.SlugField(max_length=100)
def __str__(self):
return self.name
class Country(models.Model):
name = models.CharField(max_length=100, verbose_name='Country name')
code = models.CharField(max_length=5, verbose_name='Country code')
def __str__(self):
return self.code
And a form for this model:
from django import forms
from teams.models import Team
class TeamForm(forms.ModelForm):
class Meta:
model = Team
fields = (
'biography',
'city',
'country'
)
And this is my view:
def add(request):
if request.method == 'POST':
form = TeamForm(request.POST)
if form.is_valid():
send = True
form.save()
else:
form = TeamForm()
return render(request, 'teams/add.html', locals())
As you can see, all my model fields are required because I don't add argument 'null' to True in my model attributes.
In my ModelForm, for testing, I just specify fields biography, city and country.
But when I fill the form and send-it, data are saved in database, however is missing name and slug....
Why dont i have a django exception ?
Thanks for youre help
Neither of those fields are saved as Null, though. They are both character fields (SlugField is a subclass of CharField), and an empty charfield is saved as an empty string - which is perfectly valid from the database point of view.