Create a new model in Django linked tothe User table - python

I'm new to Django and I've correctly created my first web app where I can register and login as an user. I'm using the standard from django.contrib.auth.models import User and UserCreationFormto manage this thing.
Now, I would like to create a new table in the database to add new fields to the user. I'm already using the standard one such as email, first name, second name, email, username, etc but I would like to extend it by adding the possibility to store the latest time the email has been changed and other info. All those info are not added via the form but are computed by the backend (for instance, every time I want to edit my email on my profile, the relative field on my new table, linked to my profile, change value)
To do that I have added on my models.py file the current code
from django.db import models
from django.contrib.auth.models import User
class UserAddInformation(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
last_time_email_change = models.TimeField('Last email changed', auto_now_add=True, blank=True)
def __str__(self):
return self.user.username
And on my admin.py
from django.contrib import admin
from .models import UserAddInformation
admin.site.register(UserAddInformation)
The form to edit the email and the view can be found below
forms.py
class EditUserForm(UserChangeForm):
password = None
email = forms.EmailField(widget=forms.EmailInput(attrs={
'class': 'form-control'
}))
class Meta:
model = User
# select the fields that you want to display
fields = ('email',)
views.py
#authenticated_user
def account_user(request):
if request.method == "POST":
form = EditUserForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
user_info_obj = UserAddInformation.objects.create(user=request.user,
last_time_email_change=datetime.now())
user_info_obj.save()
messages.success(request, "Edit Succesfully")
else:
pass
else:
form = EditUserForm()
return render(request, 'authenticate/account.html', {
'form_edit': form,
})
The issue is that, once I'm going to update the email via the form, I got an error UNIQUE constraint failed: members_useraddinformation.user_id
Using ForeignKey make it works but it create a new row in the table, with the same when I just want to update the first one
The edit process for the email works tho
What am I doing wrong?

It turned out that auto_now_add=True inherit editable=False generating the error. So changing my models.py with
class UserAddInformation(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
last_time_email_change = models.TimeField('Last email changed')
def save(self, *args, **kwargs):
self.last_time_email_change = timezone.now()
return super(UserAddInformation, self).save(*args, **kwargs)
def __str__(self):
return self.user.username
and checking with
user_info_obj = UserAddInformation.objects.get_or_create(user=request.user)
user_info_obj[0].save()
inside def account_user(request): worked
I'm not sure it's the best solution for my issue tho

Related

Set and not let modify foreign key value (user)

I have 2 models that I want to preset the value and ideally have it hidden from the Django admin when creating new record, this way the user don't amend this value. This are the created and modified by that are foreign keys to users.
I found this link https://pypi.org/project/django-currentuser/, that i thought it might do the job, however it reverted my django from the latest version to version 3, so I dont want to use it, and also, it doesnt work if i set either created or last modified but not both, if i set it in the 2 models i get 4 errors.
I am wondering if there is an easy way to set this default value?
from django.db import models
from email.policy import default
from django.contrib.auth.models import User
from django.utils import timezone
# from django.contrib import admin
# https://pypi.org/project/django-currentuser/
from django_currentuser.middleware import (get_current_user, get_current_authenticated_user)
from django_currentuser.db.models import CurrentUserField
class Company(models.Model):
modified_by = models.ForeignKey(User, related_name='company_modified_by', unique = False, on_delete=models.CASCADE)
created_by = CurrentUserField()
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=150, unique = True)
class Meta:
verbose_name = "Company"
verbose_name_plural = "Companies"
def __str__(self):
return self.name
class UserProfile(models.Model):
modified_by = models.ForeignKey(User, related_name='user_profile_modified_by', unique = False, on_delete=models.CASCADE)#CurrentUserField(on_update=True)
created_by = CurrentUserField()
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now_add=True)
I've learned that instance doesn't work as described in a previous stackoverflow interaction. I've done some tinkering figured out how to do my usual is_edit flag in the admin
This is what I've come up with. It requires changing the admin.py and adding a new form.
The values will still show up in that table in the admin page, which I assume is good, they're just hidden in the new+edit forms.
Note: I only did Company as I'm not 100% sure on how UserProfile works as the only two fields are supposed to be hidden ones, so what's to edit?
admin.py
from django.contrib import admin
# this \/ needs to change
from myapp.forms import CompanyForm
from myapp.models import Company
class CompanyAdmin(admin.ModelAdmin):
# columns to show in admin table
list_display = (
'name',
'created_by', 'created_date',
'modified_by', 'modified_date',
)
# custom form
form = CompanyAdminForm
# override default form_save to pass in the request object
# - need request.user inside the save method for `{x}_by = user`
def save_form(self, request, form, change):
return form.save(commit=False, request=request)
admin.site.register(Company, CompanyAdmin)
forms.py
from django import forms
from django.contrib import admin
# this \/ needs to change
from myapp.models import Company
class CompanyForm(forms.ModelForm):
class Meta:
model = Company
fields =['name']
exclude =['modified_by', 'created_by', 'created_date', 'modified_date']
def __init__(self, *args, **kwargs):
# We must determine if edit here, as 'instance' will always exist in the save method
self.is_edit = kwargs.get('instance') != None
super(CompanyForm, self).__init__(*args, **kwargs)
def save(self, commit=True, *args, **kwargs):
# We must Pop Request out here, as super() doesn't like extra kwargs / will crash
self.request = kwargs.pop('request') if 'request' in kwargs else None
obj = super(CompanyForm, self).save(commit=False, *args, **kwargs)
# do your stuff!
from datetime import datetime
if self.is_edit:
obj.modified_date = datetime.now().date()
obj.modified_by = self.request.user
else:
obj.created_date = datetime.now().date()
obj.created_by = self.request.user
if commit:
obj.save()
return obj
Note: But you can reuse the Company form for a non-admin form, you just have to remember to call the save like: form.save(commit=False, request=request)
# Example (Might need some minor tinkering)
def myview(request):
if request.method == 'POST':
form = CompanyForm(request.POST)
if form.is_valid():
form.save(commit=True, request=request)
Normally I inject vars into the declaration + __init__, ex form = CompanyForm(request.POST, request=request, is_edit=True) instead of the save() but 1 look at contrib/admin/options.py + ModelAdmin.get_form() & no thanks!
You can use editable=False, eg,
modified_by = models.ForeignKey(User, related_name='company_modified_by', unique = False, on_delete=models.CASCADE, editable=False)
According to the docs, If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation. Default is True.
That way, you can set it programmatically during creation (eg, via a request to a view) and not have to worry about it being edited.
def create_view(request):
if request.method == "POST":
company_form = CompanyForm(request.POST)
company_form.instance.created_by = request.user
company_form.save()
(Also - don't forget, use
modified_date = models.DateTimeField(auto_now=True)
for modified dates. auto_now_add is for one time creation updates.)

How do I add username of logged in user to model field in django

How to add username of currently logged in user to field in my model? For example, I need to store info about user like name, email and so on in model, other than default Django user model, but I still use default one to store credentials. I want to establish relationship between those, so I created username field in my model. How do I fill it with current user's username upon saving the corresponding form?
My model
class ApplicantProfile(models.Model):
name = models.CharField(max_length = 50)
dob = models.DateField()
email = models.EmailField()
description = models.TextField()
username = <something>
What do I change <something> with?
My form
class ApplicantProfileEdit(forms.ModelForm):
class Meta:
model = ApplicantProfile
fields = [
'name',
'dob',
'email',
'description',
]
My view
def ApplEditView(request):
form = ApplicantProfileEdit(request.POST or None)
if form.is_valid():
form.save()
form = ApplicantProfileEdit()
context = {
'form':form
}
return render(request, "applProfileEdit.html", context)
P.S. I tried to import models straight to my views.py, and assign request.user.username to username field of the model in my view, but it didn't work, just left that field empty. I had username as CharField when I tried this.
It is not a good idea to save the username itself, or at least not without a FOREIGN KEY constraint. If later a user changes their name, then the username now points to a non-existing user, if later another user for example changes their username to thatusername, then your ApplicantProfile will point to the wrong user.
Normally one uses a ForeignKey field [Django-doc], or in case each ApplicantProfile points to a different user, a OneToOneField [Django-doc]:
from django.conf import settings
from django.db import models
class ApplicantProfile(models.Model):
name = models.CharField(max_length = 50)
dob = models.DateField()
email = models.EmailField()
description = models.TextField()
# maybe a OneToOneField
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
In the view:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def appl_edit_view(request):
if request.method == 'POST':
form = ApplicantProfileEdit(request.POST)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('some-view-name')
else:
form = ApplicantProfileEdit()
context = {
'form':form
}
return render(request, 'applProfileEdit.html', context)
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

NOT NULL constraint failed: users_userprofile.user_id

I am trying to insert django form data inside the UserProfile model in my app. I tried using the django shell and views.py but I keep getting this error.
Models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
section = models.CharField(max_length=255, blank=True)
year = models.IntegerField(null=True, blank=True)
course = models.CharField(max_length=255, blank=True)
qrcode = models.CharField(max_length=255, blank=True)
present = models.BooleanField(default=False)
def __str__(self):
return self.user.username
views.py
#staticmethod
def generate_qr(request):
if request.method == "POST":
form = MakeAttendance(request.POST)
if form.is_valid():
course = form.cleaned_data.get('courses')
section = form.cleaned_data.get('section')
year = form.cleaned_data.get('year')
profile = UserProfile.objects.get_or_create(user=request.user)
userobj = UserProfile(qrcode=unique_id)
userobj.save().filter(course=course, section=section, year=year)
return redirect('/users/dashboard')
This question has been answered many times here, but none of the solutions worked for me. I tried Creating a user profile with get_or_create method. I tried deleting my entire database and making migrations again. I manually tried to pass the user ID but nothing.
First create a user using user=User.objects.create_user(username=request.user, password='password'), then save it using user.save() and create profile using profile=UserProfile.objects.get_or_create(user=user). The reason this error occours is because the UserProfile looks for a user instance which you did not provide.
The problem is in these two line
userobj = UserProfile(qrcode=unique_id)
userobj.save().filter(course=course, section=section, year=year)
In the first line you created an instance of UserProfile with only qr_code
and in the next line you are trying to save it which will try to insert a new row in the database without the user.
in models.py you should create user object:
from django.conf import settings
User = settings.AUTH_USER_MODEL
before class creating

Can't update user profile in my Django app Django

I have created a Edit Profile form in my Django app but it doesn't save in the database.
This is the profile model:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', primary_key=True) #Each User is related to only one User Profile
city_search_text = models.CharField(blank=True, max_length=300)#user picks a city from autocomplete and in the view I get or create a City object
city = models.ForeignKey(City, blank=True, null=True, related_name='city') #Each User Profile must be related to one city.
prof_pic = models.ImageField(blank=True, upload_to='profile_pictures')
dob = models.DateField(blank=True, null=True)
def __str__(self):
return self.user.first_name
This is the form:
class EditProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('dob',)#I'm testing to update this field only
def save(self, commit=True):
profile = super(EditProfileForm, self).save(commit=False)
if commit:
profile.save()
return profile
This is the view:
def editprofile(request):
if request.method == 'POST':
edit_profile_form = EditProfileForm(request.POST, instance=request.user)
if edit_profile_form.is_valid():
profile = edit_profile_form.save(commit=False)
profile.save()
if 'next' in request.GET:
return redirect(request.GET['next'])
else:
print (profile_form.errors)
else:
edit_profile_form = EditProfileForm(instance=request.user.profile)
return render(request, 'excurj/editprofile.html', {'edit_profile_form':edit_profile_form,})
After I submit the form it forwards me to index page okay but the values remain the same in the user's profile.
Seems like
if edit_profile_form.is_valid():
isn't getting called at all, and your data is not saved. That means your form has invalid data, and you should check for form errors to detect those.
Also, you are trying to print form errors if the request isn't POST, which makes no sense and won't help you printing form errors. Try using this way;
if edit_profile_form.is_valid():
profile = edit_profile_form.save(commit=False)
profile.save()
else:
print (profile_form.errors)
And check your form for errors.
I figured it out eventually. In the view I should have passed an instance of the profile not the User object. So it needs to be like this:
edit_profile_form = EditProfileForm(request.POST, instance=request.user.*profile*)

Django register extended user

I'm trying to create and manage a custom user in django.
I saw there are two possibilities, and i've chosen to extend (not create a new auth).
Models
models.py
class Artists(models.Model):
user = models.OneToOneField(User)
artist_image = models.ImageField(null=True, blank=True, upload_to="/artist_image/")
def __str__(self):
return 'Profil de {0}'.format(self.username)
def get_absolute_url(self):
return reverse('artist-details', kwargs={'pk': self.pk})
As i read in doc, I just make a OneToOne field with the User class of django auth models, so I can access method and properties, such as username, email, on my own user (here Artists).
form.py
class CreateArtistForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'email')
def save(self, commit=True):
user = super(CreateArtistForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
Here I extend UserCreationForm to prepare a form a little different (I want to have email field on my register form).
But here is my question : I first tried with
class Meta:
model = Artists
fields = ('user.username', 'user.email')
But I the error fields unknown in model Artist.
So I tried just with username and email and same error.
So I changed the model = Artists to User, and it works fine.
But now how i register my Artist Object when the user is saved?
Do I have to make something like (in save()):
artist = Artists()
artist.user = user
artist.save()
Or override create_user()?
I'm quite lost here and i'm looking docs and questions not able to find something because most of example people define their own auth.
Thanks in advance
Besta
edit : i'm using django 1.8.2 and python 3.4

Categories