Django - ModelForm removing not included values when updating - python

I'm integrating ModelForm with Django's restless to make an API for my server. By now, I'm working on a update operation on a User.
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_("email address"), unique=True)
username = models.CharField(_("username"), max_length=30, unique=True)
first_name = models.CharField(verbose_name=_("First name"), max_length=100, blank=True)
last_name = models.CharField(verbose_name=_("Last name"), max_length=100, blank=True)
description = models.TextField(verbose_name=_("Description"), blank=True)
phone = models.CharField(verbose_name=_("Phone"), max_length=30, blank=True)
last_location = models.PointField(verbose_name=_("Last location"), geography=True, null=True, blank=True)
other fields...
Here's my ModelForm for the User class:
class ProfileForm(ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'description', 'phone', 'last_location', 'lang')
And here's the call I make to update the user from the restless Resource update function:
form = ProfileForm(self.data, instance=user)
if form.is_valid():
user = form.save()
where self.data is a dictionary including the values received on the call.
{dict} {u'lang': u'es', u'phone': u'293923293', u'first_name': u'Name'}
My problem comes when updating a User. The other fields not included in self.data dictionary are setting their values to ''.
Any ideas?

Related

Django admin queryset foreign key to user django-allauth

I have an e-commerce development and I'm looking to send an email to the client from the admin site, I can´t the queryset correclty to do this. I have the following model:
models.py:
class Orden(models.Model):
cliente = models.ForeignKey(
User, on_delete=models.CASCADE, verbose_name='Usuario')
productos = models.ManyToManyField(OrdenProducto)
fecha_orden = models.DateTimeField(auto_now_add=True)
completada = models.BooleanField(default=False, null=True, blank=True)
id_transaccion = models.CharField(max_length=20, null=True)
correo_enviado = models.BooleanField(default=False, null=True, blank=True)
datos_pedido = models.ForeignKey(
'DatosPedido', on_delete=models.SET_NULL, blank=True, null=True)
pago = models.ForeignKey(
'Pago', on_delete=models.SET_NULL, blank=True, null=True)
cupon = models.ForeignKey(
'Cupon', on_delete=models.SET_NULL, blank=True, null=True)
class Meta:
verbose_name_plural = "Orden"
def __str__(self):
return self.cliente.username
cliente has a foreign key to the User model and I want to get the email address, I have tried many ways but I just can´t get it.
admin.py:
class OrdenAdmin(admin.ModelAdmin):
list_display = ('cliente', 'completada', 'correo_enviado')
actions = ['enviar_correo']
def enviar_correo(self, request, queryset):
queryset.update(correo_enviado=True)
a = queryset.get(cliente=self.user.email)
send_mail('test', 'test', 'xxxxxx#mail.com',
['a], fail_silently=True)
You can try iterating the queryset to access the specific data in the rows.
Try the following code.
class OrdenAdmin(admin.ModelAdmin):
list_display = ('cliente', 'completada', 'correo_enviado')
actions = ['enviar_correo']
def enviar_correo(self, request, queryset):
queryset.update(correo_enviado=True)
for obj in queryset:
email = obj.cliente.email
send_mail('test', 'test', email,
['a], fail_silently=True)
I hope this code helps you
Unless you have extended the default user model or have created Your Own user model, django default user model does not have an email field.
So if you have extended or created Your Own model you can do
myordenobj.cliente.email
But if you're using the default user model and your username is an email then do.
myordenobj.cliente.username

Django User model saves twice into database

I am trying to create multi user registration system with Django. However, anytime I call the save() method to save a User type, it saves into the User table twice. The funny thing about the second model that is saved is that many required fields are empty.
I am using a custom user model that I created from AbstractBaseUser. I also rewrote the forms for the CustomUser model. For the multiple user types, I am using a profile model (Student model has a OneToOne field to the user model)
models.py:
class User(AbstractBaseUser, PermissionsMixin):
# I've removed some unimportant code here
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
class Types(models.TextChoices):
STUDENT = 'STUDENT', 'Student'
DEPARTMENT_USER = 'DEPARTMENT_USER', 'Department user'
ADMIN = 'ADMIN', 'Admin'
user_type = models.CharField(_('Type'), max_length=50, choices=Types.choices, default=Types.STUDENT)
first_name = models.CharField(_('First name'), max_length=70, blank=False, default="")
middle_name = models.CharField(_('Middle name'), max_length=70, blank=True, default="")
last_name = models.CharField(_('Last name'), max_length=70, blank=False, default="")
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False) # a admin user; non super-user
is_superuser = models.BooleanField(default=False) # a superuser
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['user_type', 'first_name', 'last_name'] # Email & Password are required by default.
objects = UserManager()
class Meta:
verbose_name = ('user')
verbose_name_plural = ('users')
#db_table = 'auth_user'
abstract = False
class AccountConfirmed(models.Model):
# Model to determine which users have confirmed their email addresses.
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='accountconfirmed')
email_confirmed = models.BooleanField(default=False)
reset_password = models.BooleanField(default=False)
class Meta:
app_label = 'auth'
# When the user model is created, through signals an AccountConfirmed model is also created.
# The email_confirmed and reset_password field is set to false.
#receiver(models.signals.post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
AccountConfirmed.objects.create(user=instance)
instance.accountconfirmed.save()
######################################################
######################################################
class Student(User):
# This is the model class for students
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, related_name='students')
matric_number = models.CharField(_('Matriculation number'), max_length=11, blank=False)
department = models.CharField(_('Department'), max_length=40, blank=False)
# has_graduated, level, etc. future possibilities
def __str__(self):
return f'{self.user.email}'
forms.py:
class StudentSignupForm(UserCreationForm):
# first_name = forms.CharField(max_length=70)
# middle_name = forms.CharField(max_length=70, required=False)
# last_name = forms.CharField(max_length=70)
matric_number = forms.CharField(min_length=10, max_length=11, help_text='Your Matric number must be 10 characters')
department = forms.CharField(max_length=40, help_text='e.g Computer Science')
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('matric_number', 'department')
#transaction.atomic
def save(self, commit=True):
# Save the User instance and get a reference to it
user = super().save(commit=False)
user.user_type = User.Types.STUDENT
user.is_active = False
#if commit:
user.save()
print(f' forms.py {user.email} {user.first_name}')
student = Student.objects.create(user=user, matric_number=self.cleaned_data.get('matric_number'), department=self.cleaned_data.get('department'))
# Add other details
# Return User instance, not Student instance
return user
views.py:
class StudentUserSignupView(CreateView):
model = User
template_name = 'account/signup.html'
form_class = StudentSignupForm
def get_context_data(self, **kwargs):
kwargs['user_type'] = 'STUDENT'
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
#login(self.request, user)
send_verification_mail(self, user)
return redirect('verification_sent')
Anytime a user signs up, this is what the students table looks like:
Also, this is what the users table look like after signup (with the multiple saves)
So how do I correct the multiple saves in the user table?
Also, How is it even possible to save a model with most of the required fields empty?
As pointed out by #RaghavKundra, the line below was what caused the problem of saving multiple times to the database
class Student(User):
Instead of that, it should be
class Student(models.Model):

Django Rest Framework serializer field incorrectly named

When I run api request I get a following error:
AttributeError: Got AttributeError when attempting to get a value for field email on serializer UserSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the tuple instance.
Original exception text was: 'tuple' object has no attribute 'email'.
New user gets inserted in database anyway, email field is filleld properly.
View:
class Register(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
return Response(serializer.data, status=status.HTTP_201_CREATED)
Serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'name', 'password']
def create(self, validated_data):
user = User.objects.create_user(**validated_data),
return user
Model:
class User(AbstractBaseUser):
email = models.EmailField(max_length=254, unique=True)
username = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=60)
date_of_birth = models.DateField(blank=True, null=True)
bio = models.CharField(default='', max_length=10000)
photo = models.ImageField(max_length=255, null=True, blank=True)
email_verified_at = models.DateTimeField(null=True, blank=True)
email_token_time = models.DateTimeField(null=True, blank=True)
email_token = models.CharField(default='', max_length=64)
password_token_time = models.DateTimeField(null=True, blank=True)
password_token = models.CharField(default='', max_length=64)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
last_seen = models.DateTimeField(null=True, blank=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'name']
objects = UserManager()
class Meta:
db_table = "User"
def __str__(self):
return self.username
I also have custom user manager, but that is probably irrelevant, and works as user does get inserted to database.
You have typo in this line:
user = User.objects.create_user(**validated_data),
It contains comma , in the last of line. So user become a tuple of user instance, not just user instance. It become (user,).
Should return user instance.

Getting null for my profile serializer data instead of actual data

I am using Django-rest-auth to authenticate my users, that works well. how my model is set up is that I have the custom user model for authentication and I also have a profile model that gets created with a signal whenever a user is created.
I want that when the users are fetched in its URL, the profile for that user is also displayed, and I have passed through the serializer.
THE PROBLEM: I am getting null instead of the actual data
my models.py (I didnt include some models like the user managers, skill, e.t.c as i felt they werent relevant)
class User(AbstractBaseUser, PermissionsMixin):
username = None
email = models.EmailField(max_length=254, unique=True)
fullname = models.CharField(max_length=250)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['fullname']
objects = UserManager()
class Profile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profiles')
date_of_birth = models.DateField(blank=True, verbose_name="DOB", null=True)
bio = models.TextField(max_length=500, blank=True, null=True)
profile_photo = models.CharField(blank=True, max_length=300, null=True)
skills = models.ManyToManyField(Skill, related_name='skills')
sex = models.CharField(max_length=1, choices=SEX, blank=True, null=True)
type_of_body = models.CharField(max_length=8, choices=BODYTYPE, blank=True, null=True)
feet = models.PositiveIntegerField(blank=True, null=True)
inches = models.PositiveIntegerField(blank=True, null=True)
lives_in = models.CharField(max_length=50, blank=True, null=True)
updated_on = models.DateTimeField(auto_now_add=True)
the serializers.py code
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = "__all__"
read_only_fields = ('pk',)
class CustomUserDetailsSerializer(serializers.ModelSerializer):
profiles = ProfileSerializer(read_only=True)
class Meta:
model = User
fields = ('pk', 'email', 'fullname', 'profiles')
read_only_fields = ('email', 'fullname', 'profiles')
view.py
class ListUsersView(APIView):
permission_classes = [AllowAny]
def get(self, request):
user = User.objects.all()
serializer = CustomUserDetailsSerializer(user, many=True)
return Response(serializer.data)
urls.py
url(r'^list-users/$', ListUsersView.as_view(), name='list-users'),
the JSON response I get
[
{
"pk": 1,
"email": "opeyemiodedeyi#gmail.com",
"fullname": "opeyemi odedeyi",
"profiles": {
"date_of_birth": null,
"bio": null,
"profile_photo": null,
"sex": null,
"type_of_body": null,
"feet": null,
"inches": null,
"lives_in": null
}
}
]
how do I get the profiles to show in the response?
I guess the problem is in your CustomUserDetailsSerializer. you have one-to-many relationship with User and Profile, but you are not explicitly telling it in your profiles attribute of the serializer. You have to pass many=True argument to the ProfileSerializer:
class CustomUserDetailsSerializer(serializers.ModelSerializer):
profiles = ProfileSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ('pk', 'email', 'fullname', 'profiles')
read_only_fields = ('email', 'fullname', 'profiles')
But I am curious why you are using one-to-many relationship for that. You could use OneToOneField which explicitly tells that one user can only have one profile. But I am not familiar with your situation, so that is only my advice.

form object has no attribute 'email' in django

I have a registration form made of 2 forms, from which email field gets saved in both User and Student models. Now, I made an update account info view. Problem is, I want that email will get updated the in both models. But I get error:
'StudentEditForm' object has no attribute 'email'
Here is my code:
class StudentEditForm(forms.ModelForm):
email = forms.EmailField(required=False)
name = forms.CharField(max_length=30)
surname = forms.CharField(max_length=50)
photo = forms.ImageField(required=False)
phone = forms.CharField(max_length=15, required=False)
class Meta:
model = Student
fields = ('email', 'name', 'surname', 'phone', 'photo')
class User(AbstractUser):
pass
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
name = models.CharField(max_length=30, null=True, blank=True, default=None)
surname = models.CharField(max_length=50, null=True, blank=True, default=None)
email = models.EmailField(unique=True, null=True, blank=True, default=None)
student_ID = models.CharField(unique=True, max_length=14,
validators=[RegexValidator(regex='^.{14}$',
message='The ID needs to be 14 characters long.')],
null=True, blank=True, default=None)
photo = models.ImageField(upload_to='students_images', null=True, blank=True, default=None)
phone = models.CharField(max_length=15, null=True, blank=True, default=None)
def __str__(self):
return self.surname
User.student = property(lambda p: Student.objects.get_or_create(user=p)[0])
def profile_edit(request):
user = request.user
student = request.user.student
if request.method != 'POST':
form = StudentEditForm(instance=student)
else:
form = StudentEditForm(request.POST, instance=student)
user.email = form.email
form.save()
return render(request, 'index.html')
context = {
"form": form,
}
return render(request, "registration/profile_edit.html", context)
Again, I need that I will be able to update the email fields. And the email will get saved in User email but also I want it saved to Student email.
call form.is_valid() and then use form.cleaned_data['email'], see https://docs.djangoproject.com/en/1.11/topics/forms/
I had to add
form.is_valid()
and
user.email = form.cleaned_data['email']
but also to add user.save() which solved this issue.

Categories