I am trying to practice a simple project: A user registers (using Django registration-redux
), uploads some file, and then s/he is provided with a list of her files, being downloadable. Here are my models.py, forms.py, and views respectively:
models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, related_name='uploaded_by')
names = models.CharField(max_length=40)
lastname = models.CharField(max_length=50)
email = models.EmailField()
uploads = models.FileField(upload_to= 'blablabla')
def __str__(self):
return self.email
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['names', 'uploads']
view.py
from .forms import UserProfileForm
from .models import UserProfile
@login_required()
def new(request):
form = UserProfileForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
context = {'title': 'welcome', 'form': form}
return render(requst, 'upform.html', context)
however when I login with a user and try to upload a file I get the error: IntegrityError at /new NOT NULL constraint failed: _userprofile.user_id
After digging a lot I noticed someone suggested the reason of the error is because the user is not included anyhow in the process of posting the form, so I tried whatever came to my mind and the case in which I added the user field to the forms.py worked:
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['names', 'uploads']
the problem however is that the form shown in the browser now includes a drop-down list containing all the registered users. I tried to associate the logged-in user with the form in the views by I kept seeing different errors. My question is: How can I associate the uploads with the logged-in user in a transparent manner. sorry if the question is too newbie-liked
Keep the user out of the form and add it on save:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
I must say your model looks a bit odd; you have multiple profiles for each user, each with a single upload. Seems more likely you want a single profile, with a OneToOne relationship to User, than a separate Uploads model with a ForeignKey to UserProfile.
I added the user field to the forms.py worked:
This probably also opens up a security hole, because you could then set the user from outside of your application, overriding the logged-in user.
After digging a lot I noticed someone suggested the reason for the error is because the user is not included anyhow in the process of posting the form.
You figured that quite right. If the user is not a field, the form never knows how to fill the user
field of UserProfile
. Since you cannot save a UserProfile
without a filled in user
foreign key, you get the error.
There are various ways around this:
One way to solve the problem is to use save(commit=False)
on the form, patch the user into the unsaved instance and manually saving the object to the database:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
This slightly violates the encapsulation because you now handle the database save outside of the form.
You are also able to provide a initial "template" instance to the form:
form = UserProfileForm(
request.POST,
request.FILES,
instance=UserProfile(user=self.request.user)
)
You probably want to do so, anyway because the form also allows to edit an existing userprofile. Currently you are saving a new UserProfile
each time and since user
is not unique=True
in your model class, you will get multiple profiles per user.
If you do not want this to be possible, check Daniel Roseman's answer, because then you probably want UserProfile.user
to be a OneToOne
field.
In this case, we can simplify to
profile, created = UserProfile.objects.get_or_create(user=request.user)
form = UserProfileForm(
request.POST,
request.FILES,
instance=profile
)
Note also that I removed the or None
as it is not necessary. BaseForm
(which ModelForm
derives from) does this check for you (it actually does self.data = data or {}
) which essentially undoes the prior none-ification)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With