Django ModelForm ensuring FK integrity without using it in the form
I have a User Profile model with a Model Form:
class Profile(models.Model):
# Managed fields
user = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE)
memberId = models.CharField(unique=True, max_length=15, null=False, blank=False, default=GenerateFA)
bio = models.TextField(null=True, blank=True)
avatar = models.ImageField(upload_to="static/MCARS/img/members", null=True, blank=True)
birthday = models.DateField(null=True, blank=True)
gender = models.CharField(max_length=10, choices=constants.GENDER_CHOICES, null=True, blank=True)
invited = models.BooleanField(default=False)
registered = models.BooleanField(default=False)
height = models.PositiveSmallIntegerField(null=True, blank=True)
phone = models.CharField(max_length=32, null=True, blank=True)
address = models.CharField(max_length=255, null=True, blank=True)
number = models.CharField(max_length=32, null=True, blank=True)
city = models.CharField(max_length=50, null=True, blank=True)
state = models.CharField(max_length=50, null=True, blank=True)
zip = models.CharField(max_length=30, null=True, blank=True)
facebook = models.URLField(null=True, blank=True)
Twitter = models.URLField(null=True, blank=True)
LinkedIn = models.URLField(null=True, blank=True)
Instagram = models.URLField(null=True, blank=True)
Snapchat = models.URLField(null=True, blank=True)
website = models.URLField(null=True, blank=True)
class UserProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ('user','memberId','invited', 'registered')
I don't want user
in the form, but remember who the user is, when saving back to the model. How do I ensure that? Without it, the compiler throws a FK integrity error.
This is what I have the view:
@login_required()
def Profile(request, memberid=None):
if memberid:
user = User.objects.select_related('profile').get(id=memberid)
else:
user = User.objects.select_related('profile').get(id=request.user.id)
errors = None
if request.method == 'POST':
print('found me')
data = request.POST
form = UserProfileForm(data)
form.user = user
if form.is_valid():
form.save()
else:
print('form is invalid')
errors = form.errors
context = {
'pagetitle': 'MCARS - Service Record',
'user': user,
'rank': Rank.objects.get(user=user.id),
'assignments': Assignment.objects.filter(user=user.id),
'form': UserProfileForm(),
}
if errors:
context['errors'] = errors
return render(request, template_name='MCARS/pages/ServiceRecord.html', context=context)
Thanks!
We don't need to fetch the user, since we already got the user: request.user
, so we can work with:
@login_required
def Profile(request, memberid=None):
if request.method == 'POST':
form = UserProfileForm(request.POST, request.FILES)
form.user = user
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('name-of-some-view')
else:
form = UserProfileForm()
context = {
'pagetitle': 'MCARS - Service Record',
'user': request.user,
'rank': Rank.objects.get(user=request.user),
'assignments': Assignment.objects.filter(user=request.user),
'form': form,
}
return render(
request, template_name='MCARS/pages/ServiceRecord.html', context=context
)
You typically don't create a new form when the POST request fails, but you rerender the form, which will, if you render the form correctly, show the errors, together with the filled in data.
You also normally redirect if the form is valid and correct to implement the Post/Redirect/Get architectural pattern [wiki].