Django: Integrity error UNIQUE constraint failed: user_profile.user_id - django

When I am trying to edit a profile to add info to a UserProfile model, I am getting this strange error:
IntegrityError at /profiles/edit/
UNIQUE constraint failed: user_profile.user_id
What is wrong here,
model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
bio = models.TextField(blank=True)
phone= models.CharField(max_length=10, blank=True)
address = models.CharField(max_length=1024)
age = models.PositiveIntegerField(blank=True,null=True)
gender = models.IntegerField(choices=GENDER_CHOICES, default=1)
form:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('phone','age','gender','address','bio')
view:
def edit_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST)
print request.POST
if form.is_valid():
new_profile = UserProfile(
user = request.user,
bio = request.POST['bio'],
address = request.POST['address'],
age = request.POST['age']
)
new_profile.save()
return HttpResponseRedirect(reverse('user_public_profile', args=(request.user.username,)))
return render(request,'users/edit_profile.html', {'form': form})
else:
form = UserProfileForm()
return render(request,'users/edit_profile.html',
{'form': form})

It's not strange. You already have a profile for that user, so adding another one breaks the unique constraint. You need to edit the existing one, not add a new one.
Also note that you're not using the cleaned form data when you save, which you should be. Either use form.cleaned_data['bio'] etc, or even better just do form.save() which is the whole point of using a model form.
Putting that together:
try:
profile = request.user.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile(user=request.user)
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return redirect...
else:
form = UserProfileForm(instance=profile)
return render...

I was getting the same error multiple times and I was really disheartened but finally came over the solution.
Convert:
user = models.OneToOneField(User)
to
user = models.ForeignKey(User)
This should solve the issue.

add (instance = request.user) in UserProfileForm(request.POST)

I accidentally got this error because I forgot to specify update_fields in my call to save. I had:
modified_instance.save(video_attr)
When I should have had:
modified_instance.save(update_fields=[video_attr])
The truthy string was being interpreted as the positional force_update parameter, which obviously causes problems if the record already exists.

Consider the case: If the same user makes the post request again on URL 'profile/edit' then your code will try to create the new UserProfile using the same user instance, but since this is a one to one field and you have already created one profile using that user hence it will throw integrity error.
So you should check first if the profile associated with that user already exists or not, then if it didn't exist then create it.

Yes you are making a OneToOne relation between User and UserProfile models that's why you are facing integrity error,
for fixing this you can change it to ForeignKey
Like :
class UserProfile(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)
bio = models.TextField(blank=True)
...
...

Related

Django: Foreign Key to User -> Form is not validating because field is required

I'm currently creating a Registration-Page with two parts
One part is about the Username and a Passwort.
The second part is about choosing the own PC-Configuration
After defining everything, the User can register to get to the Main-Page.
Therefore I got a Model called "PC_Configuration" with a bunch of Foreign-Keys to the different Database-Models of the Processors/Graphicscards etc.:
class PC_Configuration(models.Model):
user = models.ForeignKey(User, related_name='user_id', on_delete=models.DO_NOTHING)
processor = models.ForeignKey(Processors, related_name='processor_id', on_delete=models.DO_NOTHING)
graphicscard = models.ForeignKey(Graphicscard, related_name='graphicscard_id', on_delete=models.DO_NOTHING)
os = models.ForeignKey(OS, related_name='os_id', on_delete=models.DO_NOTHING)
ram = models.ForeignKey(RAM, related_name='ram_id', on_delete=models.DO_NOTHING)
harddrive = models.ForeignKey(Harddrive, related_name='harddrive_id', on_delete=models.DO_NOTHING)
Also, there is one ForeignKey to the User to connect the Configuration to the respective User-ID.
Inside views.py, I've been creating a DropdownForm for all the Dropdown-Fields which the User shall choose on his own:
class DropdownForm(forms.ModelForm):
class Meta:
model = models.PC_Configuration
exclude = []
def __init__(self, *args, **kwargs):
super(DropdownForm, self).__init__(*args, **kwargs)
self.fields['processors'].queryset = DropdownForm.objects.all()
self.fields['processors'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['graphicscard'].queryset = DropdownForm.objects.all()
self.fields['graphicscard'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['os'].queryset = DropdownForm.objects.all()
self.fields['os'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['ram'].queryset = DropdownForm.objects.all()
self.fields['ram'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['harddrive'].queryset = DropdownForm.objects.all()
self.fields['harddrive'].label_from_instance = lambda obj: "%s" % obj.name
But regarding the fact, that the User-ID shall be assigned to the Configuration automatically, there's no field for that in here.
It is defined in the register_view(request) - Method:
def register_view(request):
form = DropdownForm()
if request.method == "POST":
form = DropdownForm(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
myuser = User.objects.create_user(username, None, password)
myuser.save()
auth.login(request, myuser)
#form.user = request.user
print(form.errors)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
messages.success(request, "Account has been created successfully")
return redirect(reverse('gamesearch_view'))
else:
print('Failed')
form = DropdownForm()
render(request, 'register.html', dict(form=form))
return render(request, 'register.html', dict(form=form))
And in here, we got the problem, I guess.
While Testing the Registration, the Testaccounts keep creating and login successfully. But the problem is, that there's no PC-Configuration created because the form is not validating.
With
print(form.errors)
I've been trying to figure out why exactly and it said
<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
So it seems like it's necessary to define the "user"-field before checking, if the form is validating and defining the user inside an instance afterwards.
That's why I was trying to do this:
form.user = request.user
But it's still not working and I can't figure out, what's exactly the problem since "user" shouldn't be part of the form-validation.
Can you help me out here?
Thank you in Advance!
You'll have a simpler time with something like this...
Your related_names were somewhat bogus; they're supposed to be the reverse name from the "viewpoint" of the other model. (Also, you never need to add _id to your fields by hand in Django.) If you elide the related_names, they'll implicitly be pc_configuration_set.
on_delete=DO_NOTHING is likely not a good idea. PROTECT is a good default.
It's easier to just handle the username and password as fields in the form.
You were missing exclude = ["user"], so if your template didn't render a field for user, of course it'd be missing. However, you also don't want the POSTer of the form to submit any old user id.
Using a FormView removes most of the boilerplate required to manage forms.
We're using transaction.atomic() to make sure the user doesn't get finally saved to the database if saving the PC Configuration fails.
We assign the created user to form.instance, which is the new but as-of-yet unsaved PC Configuration.
(Of course, imagine these are in separate files.)
from django import forms
from django.db import models, transaction
from django.views.generic import FormView
class PC_Configuration(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
processor = models.ForeignKey(Processors, on_delete=models.PROTECT)
graphicscard = models.ForeignKey(Graphicscard, on_delete=models.PROTECT)
os = models.ForeignKey(OS, on_delete=models.PROTECT)
ram = models.ForeignKey(RAM, on_delete=models.PROTECT)
harddrive = models.ForeignKey(Harddrive, on_delete=models.PROTECT)
class RegisterAndConfigurePCForm(forms.ModelForm):
username = forms.CharField(required=True)
password = forms.CharField(required=True, widget=forms.PasswordInput())
class Meta:
model = PC_Configuration
exclude = ["user"] # we'll assign this by hand
class RegisterAndConfigureView(FormView):
form_class = RegisterAndConfigurePCForm
template_name = "register.html"
def form_valid(self, form):
with transaction.atomic():
user = User.objects.create_user(form.cleaned_data["username"], None, form.cleaned_data["password"])
form.instance.user = user # assign user to the to-be-created PC configuration
form.save()
return redirect(reverse("gamesearch_view"))

Django get_or_create model function

In one of my recent project, I created a website for users to submit their information in a multi-stage form, in each form I use get_or_create to see if the user submit information previously or not, for example, consider user education model as follows,
class UserEducation(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE)
university_name = models.CharField(max_length=100)
in the view, I have the following code,
def education_view(request):
if request.method == "POST":
uedu, created = UserEducation.objects.get_or_create(user=request.user)
uedu.university_name = request.POST['university_name']
uedu.save()
return HttpResponse("success")
I didn't set uploading for the submit button and the problem is some users have multiple education object!
Does anyone know why this happened and whats is wrong with get_or_create?
Insted you can use update_or_create
uedu, created = UserEducation.objects.update_or_create(
user=request.user,uedu.university_name = request.POST['university_name'],
defaults={'user': 'default_value'},
)
I think the reason is that, as every time you moved on to the next step, Django will think that you tell it to create a new object because, as every time you submit a form, a new Model will be created.
What you should do is to halt the process until everything is finished. Something like:
class Person(models.Model):
fn = models.CharField(max_length=40)
class Pet(models.Model):
owner = models.ForeignKey(Person)
name = models.CharField(max_length=40)
class PersonForm(forms.ModelForm):
class Meta:
model = Person
class PetForm(forms.ModelForm):
class Meta:
model = Pet
exclude = ('owner',)
#views
def step1(request):
initial={'fn': request.session.get('fn', None)}
form = PersonForm(request.POST or None, initial=initial)
if request.method == 'POST':
if form.is_valid():
request.session['fn'] = form.cleaned_data['fn']
return HttpResponseRedirect(reverse('step2'))
return render(request, 'step1.html', {'form': form})
def step2(request):
form = PetForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
pet = form.save(commit=False)
person = Person.objects.create(fn=request.session['fn'])
pet.owner = person
pet.save()
return HttpResponseRedirect(reverse('finished'))
return render(request, 'step2.html', {'form': form})
Reference
I find the following note in the documentation,
Warning
This method is atomic assuming that the database enforces uniqueness
of the keyword arguments (see unique or unique_together). If the
fields used in the keyword arguments do not have a uniqueness
constraint, concurrent calls to this method may result in multiple
rows with the same parameters being inserted.
the university does not have a unique constraint on the user foreign key and as the result, multiple objects will be saved in the concurrent calls.

django logged in userprofile and relating additional form

I have question regarding how to attach additional form to logged in users in Django.
I want that additional form belongs to logged in user and the data I enter in the form should goes under logged in user table. I am new to Django and python please have patience I hope i can explain correctly what i want to do with this
Data I shall enter for this view shall go under logged in user only basically i want to attach this view to the logged in user only Error I am getting is
Exception Value:
registration_todos.user_id may not be NULL
#models
class userProfile(models.Model):
user = models.OneToOneField(User)
birth =models.DateField()
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class todos(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=100)
created = models.DateField()
time = models.TimeField()
def __unicode__(self):
return unicode(self.user)
#forms additional form for todos
class formtodos(ModelForm):
title = forms.CharField(label=(u'Todo'))
created = forms.DateField(label=(u'Date'))
time = forms.TimeField(label=(u'Time'))
#user = forms.CharField(label=(u'username'))
class Meta:
model = todos
exclude=('user',)
#view
def modeltodo(request):
if request.user.is_authenticated():
todos.objects.filter(user=request.user)
if request.method == 'POST':
form =formtodos(request.POST)
if form.is_valid():# All validation rules pass
todoss = form.save(commit=False)
todoss.created_by = request.user
form.save()
return HttpResponseRedirect('/profile/')
else:
form = formtodos() # An unbound form
context = {'form':form}
return render_to_response('todo.html', context, context_instance=RequestContext(request))
you've specified exclude = ('user',) in your form. This means that when you try to save the form there is no user_id present which causes the error. You probably want to put this before the save() call: todoss.user = request.user

FieldError with keyword 'username' when trying to create a user with Django

My Models.py, Here I'm using OneToOneField here, to extend StudentProfile.
from django.db import models
from django.contrib.auth.models import User, UserManager
class SecretQuestion(models.Model):
question = models.CharField(max_length=200)
def __unicode__(self):
return self.question
class StudentProfile(models.Model):
user = models.OneToOneField(User)
batch = models.CharField(max_length=10)
course = models.CharField(max_length=20)
date_of_birth = models.DateField()
secret_question = models.ForeignKey(SecretQuestion)
answer = models.CharField(max_length=20)
contact = models.CharField(max_length=20)
And my registration view, I'm creating new user main_user and I'm using it to create Student Profile :-
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
main_user = User.objects.create_user(
username= form.cleaned_data['username'],
password = form.cleaned_data['password1'],
)
user = StudentProfile.objects.create(
user_id = main_user.id,http://stackoverflow.com/questions/ask
batch=form.cleaned_data['batch'],
course=form.cleaned_data['course'],
date_of_birth=form.cleaned_data['date_of_birth'],
secret_question=form.cleaned_data['secret_question'],
answer=form.cleaned_data['answer'],
contact=form.cleaned_data['contact']
)
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {'form': form})
return render_to_response('registration/register.html',variables)
After registering in Django, I'm getting,
FieldError at /register/
Cannot resolve keyword 'username' into field. Choices are: answer, batch, contact, course, date_of_birth, id, secret_question, user
This is happening after filling registration fields and pressing register button.
I'm not able to interpret this error. What does this mean?
You need to subclass RegistrationForm as described in Adding extra fields to django-registration form
User.objects.create_user has no keyword argument named username. The username must be the first positional argument:
main_user = User.objects.create_user(form.cleaned_data['username'],
password = form.cleaned_data['password1'],
)
In the future, please include at least the line number where the error occurred.
The current code shouldn't cause this error.
The error suggests you are filtering by a username keyword argument to your StudentProfile class, which does not have a username field according to your model.
The error should list which line your code is breaking on and tell you exactly where this is happening - that's where the answer lies. Your code? Django registration code? I don't know until you post it.
FYI, this error appears if you try to filter a model by an invalid field.
MyModel.objects.filter(non_existent_field=True) # would throw this error.
Passing an invalid field into create would cause a TypeError

Django Modelform (with excluded field)

I have a sample form:
class AdminDiscountForm(ModelForm):
class Meta:
model = Discount
exclude = ('company',)
the model it's pointing to is:
class Discount(models.Model):
class Meta:
verbose_name=_('Discount')
verbose_name_plural=_('Discounts')
unique_together = ('company','type')
company = models.ForeignKey(Company)
type = models.CharField(max_length=5, choices=DISCOUNT_CHOICES)
discount = models.DecimalField(max_digits=7, decimal_places=2, verbose_name=_('Discount'))
The form excludes the 'company' field because the user has already selected this using the UI.
i am planning on doing a:
company = blah
if form.is_valid():
obj = form.save(commit=False)
obj.company = company
obj.save()
The problem is that the combination of 'company' and 'type' should be unique (hence the 'unique_together'). This is enforced in the database, so django doesn't care.
I need to extend the clean() method of this form to check for uniqueness as such:
def clean(self):
cleaned_data = self.cleaned_data
# check for uniqueness of 'company' and 'type'
The problem here is that 'company' is not in there because it has been excluded.
What is the best way to raise a form validation error in this case?
-- edit
This is only for adding discount entries.
There's no initial instance.
Jammon's method is the one I use. To expand a bit (using your example):
models.py
class Discount(models.Model):
class Meta:
verbose_name=_('Discount')
verbose_name_plural=_('Discounts')
unique_together = ('company','type')
company = models.ForeignKey(Company)
type = models.CharField(max_length=5, choices=DISCOUNT_CHOICES)
discount = models.DecimalField(max_digits=7, decimal_places=2, verbose_name=_('Discount'))
forms.py
class AdminDiscountForm(ModelForm):
class Meta:
model = Discount
exclude = ('company',)
views.py
def add_discount(request, company_id=None):
company = get_object_or_404(Company, company_id)
discount=Discount(company=company)
if request.method == 'post':
form = AdminDiscountForm(request.POST, instance=discount)
if form.is_valid():
form.save()
return HttpResponse('Success')
else:
form = AdminDiscountForm(instance=company)
context = { 'company':company,
'form':form,}
return render_to_response('add-discount.html', context,
context_instance=RequestContext(request))
This works by creating an instance of your discount model, then binding your form to this instance. This instance is not saved to your db but used to bind the form. This bound form has a value for company of the bound instance. It is then sent to your template for the user to fill out. When the user submits this form, and the form is validated, the model validation check will check for uniqueness of the unique together defined in Meta.
See Model Validation Docs and overriding clean for ModelForms
edit:
You can do a couple of things to catch non unique together entry attempts.
Inside your form.is_valid() you can except an Integrity Error like this:
if request.method == 'post':
form = AdminDiscountForm(request.POST, instance=discount)
if form.is_valid():
try:
form.save()
return HttpResponse('Success')
except IntegrityError:
form._errors["company"] = "some message"
form._errors["type"] = "some message"
else:
...
Use self.instance within the model form's clean method to check for uniqueness.
You could try this:
discount = Discount(company = blah)
form = AdminDiscountForm(request.POST, instance=discount)
if form.is_valid():
discount = form.save()
And the docs say: By default the clean() method validates the uniqueness of fields that are marked as ... unique_together